d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
168 lines
5.2 KiB
TypeScript
168 lines
5.2 KiB
TypeScript
import { recordMaterializedAccessKeys } from '@/lib/execution/payloads/access-keys'
|
|
import {
|
|
isLargeArrayManifest,
|
|
materializeLargeArrayManifest,
|
|
} from '@/lib/execution/payloads/large-array-manifest'
|
|
import {
|
|
getLargeValueMaterializationError,
|
|
isLargeValueRef,
|
|
} from '@/lib/execution/payloads/large-value-ref'
|
|
import {
|
|
assertInlineMaterializationSize,
|
|
type ExecutionMaterializationContext,
|
|
MAX_INLINE_MATERIALIZATION_BYTES,
|
|
} from '@/lib/execution/payloads/materialization.server'
|
|
import { materializeLargeValueRef } from '@/lib/execution/payloads/store'
|
|
|
|
interface InlineMaterializationOptions {
|
|
maxBytes?: number
|
|
}
|
|
|
|
type InlineMaterializationMemo = WeakMap<object, Promise<unknown>>
|
|
|
|
interface MaterializedInlineValue {
|
|
value: unknown
|
|
byteLength: number | undefined
|
|
}
|
|
|
|
export function getInlineJsonByteLength(value: unknown): number | undefined {
|
|
const json = JSON.stringify(value)
|
|
return json === undefined ? undefined : Buffer.byteLength(json, 'utf8')
|
|
}
|
|
|
|
function getArrayItemByteLength(value: MaterializedInlineValue): number {
|
|
return value.byteLength ?? Buffer.byteLength('null', 'utf8')
|
|
}
|
|
|
|
function getObjectEntryByteLength(key: string, value: MaterializedInlineValue): number | undefined {
|
|
if (value.byteLength === undefined) {
|
|
return undefined
|
|
}
|
|
return Buffer.byteLength(JSON.stringify(key), 'utf8') + 1 + value.byteLength
|
|
}
|
|
|
|
function withMaterializedAccessKeys(
|
|
context: ExecutionMaterializationContext | undefined,
|
|
materializedValue: unknown
|
|
): ExecutionMaterializationContext | undefined {
|
|
if (!context) {
|
|
return context
|
|
}
|
|
recordMaterializedAccessKeys(context, materializedValue)
|
|
return {
|
|
...context,
|
|
largeValueKeys: context.largeValueKeys,
|
|
fileKeys: context.fileKeys,
|
|
}
|
|
}
|
|
|
|
export async function materializeInlineExecutionValue(
|
|
value: unknown,
|
|
context: ExecutionMaterializationContext | undefined,
|
|
options: InlineMaterializationOptions = {}
|
|
): Promise<unknown> {
|
|
const materialized = await materializeInlineExecutionValueWithinBudget(
|
|
value,
|
|
context,
|
|
options.maxBytes ?? MAX_INLINE_MATERIALIZATION_BYTES,
|
|
new WeakMap<object, Promise<unknown>>()
|
|
)
|
|
return materialized.value
|
|
}
|
|
|
|
async function materializeInlineExecutionValueWithinBudget(
|
|
value: unknown,
|
|
context: ExecutionMaterializationContext | undefined,
|
|
maxBytes: number,
|
|
memo: InlineMaterializationMemo
|
|
): Promise<MaterializedInlineValue> {
|
|
if (isLargeArrayManifest(value)) {
|
|
assertInlineMaterializationSize(value.byteSize, maxBytes)
|
|
const materialized = await materializeLargeArrayManifest(value, {
|
|
...context,
|
|
maxBytes,
|
|
})
|
|
return materializeInlineExecutionValueWithinBudget(
|
|
materialized,
|
|
withMaterializedAccessKeys(context, materialized),
|
|
maxBytes,
|
|
memo
|
|
)
|
|
}
|
|
|
|
if (isLargeValueRef(value)) {
|
|
assertInlineMaterializationSize(value.size, maxBytes)
|
|
const materialized = await materializeLargeValueRef(value, {
|
|
...context,
|
|
maxBytes,
|
|
})
|
|
if (materialized === undefined) {
|
|
throw getLargeValueMaterializationError(value)
|
|
}
|
|
return materializeInlineExecutionValueWithinBudget(
|
|
materialized,
|
|
withMaterializedAccessKeys(context, materialized),
|
|
maxBytes,
|
|
memo
|
|
)
|
|
}
|
|
|
|
if (!value || typeof value !== 'object') {
|
|
const valueBytes = getInlineJsonByteLength(value)
|
|
if (valueBytes !== undefined) {
|
|
assertInlineMaterializationSize(valueBytes, maxBytes)
|
|
}
|
|
return { value, byteLength: valueBytes }
|
|
}
|
|
|
|
const cached = memo.get(value)
|
|
if (cached) {
|
|
return { value: await cached, byteLength: 0 }
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
const result: unknown[] = []
|
|
memo.set(value, Promise.resolve(result))
|
|
let usedBytes = Buffer.byteLength('[]', 'utf8')
|
|
for (const item of value) {
|
|
const commaBytes = result.length > 0 ? 1 : 0
|
|
const remainingBytes = maxBytes - usedBytes - commaBytes
|
|
assertInlineMaterializationSize(0, remainingBytes)
|
|
const materializedItem = await materializeInlineExecutionValueWithinBudget(
|
|
item,
|
|
context,
|
|
remainingBytes,
|
|
memo
|
|
)
|
|
const itemBytes = getArrayItemByteLength(materializedItem)
|
|
usedBytes += commaBytes + itemBytes
|
|
assertInlineMaterializationSize(usedBytes, maxBytes)
|
|
result.push(materializedItem.value)
|
|
}
|
|
return { value: result, byteLength: usedBytes }
|
|
}
|
|
|
|
const result: Record<string, unknown> = {}
|
|
memo.set(value, Promise.resolve(result))
|
|
let usedBytes = Buffer.byteLength('{}', 'utf8')
|
|
for (const [key, entryValue] of Object.entries(value as Record<string, unknown>)) {
|
|
const keyBytes = Buffer.byteLength(JSON.stringify(key), 'utf8') + 1
|
|
const commaBytes = Object.keys(result).length > 0 ? 1 : 0
|
|
const remainingBytes = maxBytes - usedBytes - commaBytes - keyBytes
|
|
assertInlineMaterializationSize(0, remainingBytes)
|
|
const materializedEntryValue = await materializeInlineExecutionValueWithinBudget(
|
|
entryValue,
|
|
context,
|
|
remainingBytes,
|
|
memo
|
|
)
|
|
const entryBytes = getObjectEntryByteLength(key, materializedEntryValue)
|
|
if (entryBytes !== undefined) {
|
|
usedBytes += commaBytes + entryBytes
|
|
assertInlineMaterializationSize(usedBytes, maxBytes)
|
|
}
|
|
result[key] = materializedEntryValue.value
|
|
}
|
|
return { value: result, byteLength: usedBytes }
|
|
}
|