Skip to content

Commit

Permalink
feat: remove usage of Node built-ins (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardoboucas committed Mar 6, 2024
1 parent 8259e5e commit 3148833
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 20 deletions.
55 changes: 49 additions & 6 deletions src/environment.ts
@@ -1,5 +1,48 @@
import { Buffer } from 'node:buffer'
import { env } from 'node:process'
import { base64Decode, base64Encode } from './util.ts'

interface EnvironmentVariables {
delete: (key: string) => void
get: (key: string) => string | undefined
has: (key: string) => boolean
set: (key: string, value: string) => void
toObject: () => Record<string, string>
}

interface Globals {
Deno?: {
env: EnvironmentVariables
}
Netlify?: {
env: EnvironmentVariables
}
process?: {
env: Record<string, string>
}
}

/**
* Returns a cross-runtime interface for handling environment variables. It
* uses the `Netlify.env` global if available, otherwise looks for `Deno.env`
* and `process.env`.
*/
export const getEnvironment = (): EnvironmentVariables => {
const { Deno, Netlify, process } = globalThis as Globals

return (
Netlify?.env ??
Deno?.env ?? {
delete: (key: string) => delete process?.env[key],
get: (key: string) => process?.env[key],
has: (key: string) => Boolean(process?.env[key]),
set: (key: string, value: string) => {
if (process?.env) {
process.env[key] = value
}
},
toObject: () => process?.env ?? {},
}
)
}

declare global {
// Using `var` so that the declaration is hoisted in such a way that we can
Expand All @@ -21,13 +64,13 @@ export interface EnvironmentContext {
}

export const getEnvironmentContext = (): EnvironmentContext => {
const context = globalThis.netlifyBlobsContext || env.NETLIFY_BLOBS_CONTEXT
const context = globalThis.netlifyBlobsContext || getEnvironment().get('NETLIFY_BLOBS_CONTEXT')

if (typeof context !== 'string' || !context) {
return {}
}

const data = Buffer.from(context, 'base64').toString()
const data = base64Decode(context)

try {
return JSON.parse(data) as EnvironmentContext
Expand All @@ -39,9 +82,9 @@ export const getEnvironmentContext = (): EnvironmentContext => {
}

export const setEnvironmentContext = (context: EnvironmentContext) => {
const encodedContext = Buffer.from(JSON.stringify(context)).toString('base64')
const encodedContext = base64Encode(JSON.stringify(context))

env.NETLIFY_BLOBS_CONTEXT = encodedContext
getEnvironment().set('NETLIFY_BLOBS_CONTEXT', encodedContext)
}

export class MissingBlobsEnvironmentError extends Error {
Expand Down
7 changes: 3 additions & 4 deletions src/lambda_compat.ts
@@ -1,16 +1,15 @@
import { Buffer } from 'node:buffer'

import { EnvironmentContext, setEnvironmentContext } from './environment.ts'
import type { LambdaEvent } from './types.ts'
import { base64Decode } from './util.ts'

interface BlobsEventData {
token: string
url: string
}

export const connectLambda = (event: LambdaEvent) => {
const rawData = Buffer.from(event.blobs, 'base64')
const data = JSON.parse(rawData.toString('ascii')) as BlobsEventData
const rawData = base64Decode(event.blobs)
const data = JSON.parse(rawData) as BlobsEventData
const environmentContext: EnvironmentContext = {
deployID: event.headers['x-nf-deploy-id'],
edgeURL: data.url,
Expand Down
6 changes: 3 additions & 3 deletions src/metadata.ts
@@ -1,4 +1,4 @@
import { Buffer } from 'node:buffer'
import { base64Decode, base64Encode } from './util.ts'

export type Metadata = Record<string, unknown>

Expand All @@ -12,7 +12,7 @@ export const encodeMetadata = (metadata?: Metadata) => {
return null
}

const encodedObject = Buffer.from(JSON.stringify(metadata)).toString('base64')
const encodedObject = base64Encode(JSON.stringify(metadata))
const payload = `b64;${encodedObject}`

if (METADATA_HEADER_EXTERNAL.length + payload.length > METADATA_MAX_SIZE) {
Expand All @@ -28,7 +28,7 @@ export const decodeMetadata = (header: string | null): Metadata => {
}

const encodedData = header.slice(BASE64_PREFIX.length)
const decodedData = Buffer.from(encodedData, 'base64').toString()
const decodedData = base64Decode(encodedData)
const metadata = JSON.parse(decodedData)

return metadata
Expand Down
5 changes: 2 additions & 3 deletions src/retry.ts
@@ -1,8 +1,7 @@
import { env } from 'node:process'

import { getEnvironment } from './environment.ts'
import type { Fetcher } from './types.ts'

const DEFAULT_RETRY_DELAY = env.NODE_ENV === 'test' ? 1 : 5000
const DEFAULT_RETRY_DELAY = getEnvironment().get('NODE_ENV') === 'test' ? 1 : 5000
const MIN_RETRY_DELAY = 1000
const MAX_RETRY = 5
const RATE_LIMIT_HEADER = 'X-RateLimit-Reset'
Expand Down
6 changes: 2 additions & 4 deletions src/store.ts
@@ -1,5 +1,3 @@
import { Buffer } from 'node:buffer'

import { ListResponse, ListResponseBlob } from './backend/list.ts'
import { Client } from './client.ts'
import type { ConsistencyMode } from './consistency.ts'
Expand Down Expand Up @@ -341,7 +339,7 @@ export class Store {
throw new Error('Blob key must not start with forward slash (/).')
}

if (Buffer.byteLength(key, 'utf8') > 600) {
if (new TextEncoder().encode(key).length > 600) {
throw new Error(
'Blob key must be a sequence of Unicode characters whose UTF-8 encoding is at most 600 bytes long.',
)
Expand All @@ -363,7 +361,7 @@ export class Store {
throw new Error('Store name must not contain forward slashes (/).')
}

if (Buffer.byteLength(name, 'utf8') > 64) {
if (new TextEncoder().encode(name).length > 64) {
throw new Error(
'Store name must be a sequence of Unicode characters whose UTF-8 encoding is at most 64 bytes long.',
)
Expand Down
22 changes: 22 additions & 0 deletions src/util.ts
Expand Up @@ -19,3 +19,25 @@ export const collectIterator = async <T>(iterator: AsyncIterable<T>): Promise<T[
export const isNodeError = (error: unknown): error is NodeJS.ErrnoException => error instanceof Error

export type Logger = (...message: unknown[]) => void

export const base64Decode = (input: string) => {
// eslint-disable-next-line n/prefer-global/buffer
const { Buffer } = globalThis

if (Buffer) {
return Buffer.from(input, 'base64').toString()
}

return atob(input)
}

export const base64Encode = (input: string) => {
// eslint-disable-next-line n/prefer-global/buffer
const { Buffer } = globalThis

if (Buffer) {
return Buffer.from(input).toString('base64')
}

return btoa(input)
}

0 comments on commit 3148833

Please sign in to comment.