Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove usage of Node built-ins #154

Merged
merged 1 commit into from Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
}