@nestm/storage 0.1.0-alpha.16 → 0.1.0-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +21 -1
  3. package/dist/ai-sdk/ai-sdk-file-workflow-tools.d.ts +4 -2
  4. package/dist/ai-sdk/ai-sdk-file-workflow-tools.d.ts.map +1 -1
  5. package/dist/ai-sdk/ai-sdk-file-workflow-tools.js +23 -1
  6. package/dist/ai-sdk/ai-sdk-file-workflow-tools.js.map +1 -1
  7. package/dist/ai-sdk/index.d.ts +1 -1
  8. package/dist/ai-sdk/index.d.ts.map +1 -1
  9. package/dist/ai-sdk/index.js +1 -1
  10. package/dist/ai-sdk/index.js.map +1 -1
  11. package/dist/core/index.d.ts +1 -0
  12. package/dist/core/index.d.ts.map +1 -1
  13. package/dist/core/index.js +1 -0
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/core/storage-text-stream-edit.d.ts +8 -0
  16. package/dist/core/storage-text-stream-edit.d.ts.map +1 -0
  17. package/dist/core/storage-text-stream-edit.js +114 -0
  18. package/dist/core/storage-text-stream-edit.js.map +1 -0
  19. package/dist/files-sdk/provider/index.d.ts.map +1 -1
  20. package/dist/files-sdk/provider/index.js +14 -0
  21. package/dist/files-sdk/provider/index.js.map +1 -1
  22. package/dist/workspace/index.d.ts +1 -0
  23. package/dist/workspace/index.d.ts.map +1 -1
  24. package/dist/workspace/index.js +1 -0
  25. package/dist/workspace/index.js.map +1 -1
  26. package/dist/workspace/storage-catalog-checkout.d.ts +1 -1
  27. package/dist/workspace/storage-catalog-checkout.d.ts.map +1 -1
  28. package/dist/workspace/storage-catalog-checkout.js +49 -35
  29. package/dist/workspace/storage-catalog-checkout.js.map +1 -1
  30. package/dist/workspace/storage-draft-stream.d.ts +3 -0
  31. package/dist/workspace/storage-draft-stream.d.ts.map +1 -0
  32. package/dist/workspace/storage-draft-stream.js +57 -0
  33. package/dist/workspace/storage-draft-stream.js.map +1 -0
  34. package/dist/workspace/storage-file-workflow.d.ts.map +1 -1
  35. package/dist/workspace/storage-file-workflow.js +96 -10
  36. package/dist/workspace/storage-file-workflow.js.map +1 -1
  37. package/dist/workspace/storage-file-workflow.protection.d.ts.map +1 -1
  38. package/dist/workspace/storage-file-workflow.protection.js +10 -0
  39. package/dist/workspace/storage-file-workflow.protection.js.map +1 -1
  40. package/dist/workspace/storage-file-workflow.types.d.ts +25 -1
  41. package/dist/workspace/storage-file-workflow.types.d.ts.map +1 -1
  42. package/dist/workspace/storage-file-workflow.types.js.map +1 -1
  43. package/dist/workspace/storage-text-draft.d.ts +2 -1
  44. package/dist/workspace/storage-text-draft.d.ts.map +1 -1
  45. package/dist/workspace/storage-text-draft.js +15 -18
  46. package/dist/workspace/storage-text-draft.js.map +1 -1
  47. package/dist/workspace/storage-working-files.d.ts +34 -0
  48. package/dist/workspace/storage-working-files.d.ts.map +1 -0
  49. package/dist/workspace/storage-working-files.js +291 -0
  50. package/dist/workspace/storage-working-files.js.map +1 -0
  51. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/files-sdk/provider/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Adapter } from 'files-sdk';\nimport { loadFiles, type LoadFilesOptions } from 'files-sdk/loader';\nimport {\n PROVIDER_NAMES,\n getProvider,\n getSecretEnvVars,\n listEnvVars,\n type EnvVar,\n type Provider,\n type ProviderSlug,\n} from 'files-sdk/providers';\n\nimport { StorageError, StorageErrorCode } from '../../storage.error.js';\nimport {\n createFilesSdkDriver,\n mapFilesSdkError,\n type FilesSdkDriverOptions,\n type FilesSdkStorageDriver,\n} from '../files-sdk.driver.js';\nimport type { S3Adapter, S3ProviderProfile } from '../s3/index.js';\nimport {\n recordS3ConditionalRequestPermission,\n recordS3ConstructionMetadata,\n} from '../s3/construction-metadata.js';\n\n/** Slug of a storage provider this package can build a driver for. */\nexport type StorageProviderName = ProviderSlug;\n\n/**\n * Flat, provider-specific settings — `bucket` and `region` for an object store,\n * `root` for the filesystem, `accountName` and `container` for Azure, and so\n * on. Every provider reads what it needs and ignores the rest, so one config\n * shape serves a deployment that switches providers by name.\n *\n * Credentials may be omitted for any provider whose SDK resolves its own chain\n * (an IAM role, Application Default Credentials, a shared profile).\n */\nexport type StorageProviderConfig = Omit<LoadFilesOptions, 'provider'>;\n\nexport interface ProviderStorageDriverOptions extends Omit<\n FilesSdkDriverOptions<Adapter>,\n 'adapter'\n> {\n /** Which provider to build. Validate untrusted input with {@link isStorageProvider}. */\n provider: StorageProviderName;\n config?: StorageProviderConfig;\n /** Verified operation profile for the `s3` slug, especially custom endpoints. */\n s3ProviderProfile?: S3ProviderProfile;\n}\n\n/**\n * Builds a {@link FilesSdkStorageDriver} for a provider named at runtime,\n * importing that provider's adapter — and only that one — on demand. A\n * deployment selects its store with a string (`'s3'`, `'gcs'`, `'azure'`,\n * `'r2'`, `'fs'`, …) and installs one native SDK, instead of the application\n * hard-coding a driver per backend.\n *\n * The `s3` slug additionally gets the exact verified provider profile and\n * signed-policy capabilities {@link createS3StorageDriver} attaches. Providers\n * not backed by the AWS S3 SDK expose what their adapter declares;\n * noncanonical S3-backed providers default to an unverified, read-only profile.\n * Import `@nestm/storage/files-sdk/s3` directly when the provider is known at\n * build time and the extra indirection buys nothing.\n *\n * @throws StorageError `INVALID_ARGUMENT` for an unknown slug, and whatever the\n * adapter reports (mapped) when required config or credentials are missing.\n */\nexport async function createProviderStorageDriver(\n options: ProviderStorageDriverOptions,\n): Promise<FilesSdkStorageDriver> {\n const { provider, config, s3ProviderProfile, ...filesOptions } = options;\n if (!isStorageProvider(provider)) {\n throw unknownProvider(provider);\n }\n\n // `loadFiles` owns the slug → adapter mapping and keeps the import lazy; the\n // client it returns is discarded so the caller's own driver options (prefix,\n // hooks, plugins, readonly, retries) apply to the instance the bridge builds.\n if (s3ProviderProfile !== undefined && provider !== 's3') {\n throw new StorageError(\n 's3ProviderProfile can only be used with the \"s3\" provider.',\n { code: StorageErrorCode.INVALID_ARGUMENT, permanent: true },\n );\n }\n const adapter = await resolveAdapter(provider, config, s3ProviderProfile);\n return createFilesSdkDriver({\n ...filesOptions,\n adapter,\n });\n}\n\nfunction isS3BackedAdapter(adapter: Adapter): adapter is S3Adapter {\n try {\n const raw = adapter.raw;\n if (typeof raw !== 'object' || raw === null) return false;\n const candidate = raw as {\n readonly config?: unknown;\n readonly send?: unknown;\n };\n return (\n typeof candidate.send === 'function' &&\n typeof candidate.config === 'object' &&\n candidate.config !== null &&\n (candidate.config as { readonly serviceId?: unknown }).serviceId === 'S3'\n );\n } catch {\n return false;\n }\n}\n\nasync function resolveAdapter(\n provider: StorageProviderName,\n config: StorageProviderConfig | undefined,\n s3ProviderProfile: S3ProviderProfile | undefined,\n): Promise<Adapter> {\n const loaderConfig = withVerifiedS3ConditionalOptIn(\n provider,\n config,\n s3ProviderProfile,\n );\n let resolved;\n try {\n resolved = await loadFiles({ ...loaderConfig, provider });\n } catch (error) {\n throw mapFilesSdkError(error);\n }\n const { adapter } = resolved.files;\n if (provider === 'azure') {\n const { withAzureConditionalOperations } =\n await import('../azure/azure-conditional.js');\n return withAzureConditionalOperations(\n adapter as Parameters<typeof withAzureConditionalOperations>[0],\n );\n }\n if (provider === 'memory') {\n const { withMemoryConditionalOperations } = await import('../memory.js');\n return withMemoryConditionalOperations(\n adapter as Parameters<typeof withMemoryConditionalOperations>[0],\n );\n }\n if (provider === 'fs') {\n const { withFsConditionalMutation } = await import('../fs/index.js');\n return withFsConditionalMutation(\n adapter as Parameters<typeof withFsConditionalMutation>[0],\n );\n }\n if (!isS3BackedAdapter(adapter)) {\n return adapter;\n }\n // Every AWS-SDK-backed wrapper gets fail-closed S3 provenance and endpoint\n // hardening. Only the canonical `s3` slug may infer native AWS or accept an\n // explicit verified profile; named compatibles remain unverified/read-only.\n const { withS3Capabilities } = await import('../s3/index.js');\n const configJsonPublicBaseUrl = config?.configJson?.publicBaseUrl;\n const publicBaseUrl =\n config?.publicBaseUrl ??\n (typeof configJsonPublicBaseUrl === 'string'\n ? configJsonPublicBaseUrl\n : undefined);\n const s3Adapter = adapter as Parameters<typeof withS3Capabilities>[0];\n if (provider === 's3') {\n recordS3ConditionalRequestPermission(\n s3Adapter.raw,\n config?.configJson?.conditional !== false,\n );\n }\n recordS3ConstructionMetadata(s3Adapter.raw, {\n publicBaseUrlConfigured:\n provider !== 's3' ||\n config?.publicBaseUrl !== undefined ||\n configJsonPublicBaseUrl !== undefined,\n });\n return withS3Capabilities(s3Adapter, {\n ...(typeof config?.endpoint === 'string' && {\n endpoint: config.endpoint,\n }),\n ...(publicBaseUrl !== undefined && {\n publicBaseUrl,\n }),\n ...(s3ProviderProfile !== undefined && {\n providerProfile: s3ProviderProfile,\n }),\n });\n}\n\nfunction withVerifiedS3ConditionalOptIn(\n provider: StorageProviderName,\n config: StorageProviderConfig | undefined,\n s3ProviderProfile: S3ProviderProfile | undefined,\n): StorageProviderConfig | undefined {\n const configJsonEndpoint = config?.configJson?.endpoint;\n const hasCustomEndpoint =\n config?.endpoint !== undefined || configJsonEndpoint !== undefined;\n if (\n provider !== 's3' ||\n s3ProviderProfile === undefined ||\n config === undefined ||\n !hasCustomEndpoint ||\n config.configJson?.conditional !== undefined\n ) {\n return config;\n }\n\n // files-sdk requires this construction-time opt-in before it exposes native\n // conditional primitives for a custom endpoint. The branded provider profile\n // still narrows the exposed operations in withS3Capabilities below.\n return {\n ...config,\n configJson: { ...config.configJson, conditional: true },\n };\n}\n\nfunction unknownProvider(provider: string): StorageError {\n return new StorageError(\n `Unknown storage provider: ${JSON.stringify(provider)}. Known providers: ${PROVIDER_NAMES.join(', ')}.`,\n { code: StorageErrorCode.INVALID_ARGUMENT, permanent: true },\n );\n}\n\n/** Narrows an untrusted string — an env var, a config file — to a known slug. */\nexport function isStorageProvider(value: string): value is StorageProviderName {\n return getProvider(value) !== undefined;\n}\n\n/** Every provider that can be named, sorted by slug. */\nexport function listStorageProviders(): readonly Provider[] {\n return PROVIDER_NAMES.flatMap((slug) => getProvider(slug) ?? []);\n}\n\n/** One provider's display name, description, native SDKs, and env contract. */\nexport function getStorageProvider(slug: string): Provider | undefined {\n return getProvider(slug);\n}\n\n/**\n * Every environment variable a provider reads, flattened across required, all\n * credential modes, and optional. Useful for validating a deployment's config\n * before the first upload rather than at it.\n */\nexport function listStorageProviderEnvVars(slug: string): EnvVar[] {\n return listEnvVars(slug);\n}\n\n/** The subset of {@link listStorageProviderEnvVars} that carries secrets. */\nexport function listStorageProviderSecretEnvVars(slug: string): EnvVar[] {\n return getSecretEnvVars(slug);\n}\n\nexport type {\n EnvVar as StorageProviderEnvVar,\n Provider as StorageProviderInfo,\n} from 'files-sdk/providers';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/files-sdk/provider/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Adapter } from 'files-sdk';\nimport { loadFiles, type LoadFilesOptions } from 'files-sdk/loader';\nimport {\n PROVIDER_NAMES,\n getProvider,\n getSecretEnvVars,\n listEnvVars,\n type EnvVar,\n type Provider,\n type ProviderSlug,\n} from 'files-sdk/providers';\n\nimport { StorageError, StorageErrorCode } from '../../storage.error.js';\nimport {\n createFilesSdkDriver,\n mapFilesSdkError,\n type FilesSdkDriverOptions,\n type FilesSdkStorageDriver,\n} from '../files-sdk.driver.js';\nimport type { S3Adapter, S3ProviderProfile } from '../s3/index.js';\nimport {\n recordS3ConditionalRequestPermission,\n recordS3ConstructionMetadata,\n} from '../s3/construction-metadata.js';\n\n/** Slug of a storage provider this package can build a driver for. */\nexport type StorageProviderName = ProviderSlug;\n\n/**\n * Flat, provider-specific settings — `bucket` and `region` for an object store,\n * `root` for the filesystem, `accountName` and `container` for Azure, and so\n * on. Every provider reads what it needs and ignores the rest, so one config\n * shape serves a deployment that switches providers by name.\n *\n * Credentials may be omitted for any provider whose SDK resolves its own chain\n * (an IAM role, Application Default Credentials, a shared profile).\n */\nexport type StorageProviderConfig = Omit<LoadFilesOptions, 'provider'>;\n\nexport interface ProviderStorageDriverOptions extends Omit<\n FilesSdkDriverOptions<Adapter>,\n 'adapter'\n> {\n /** Which provider to build. Validate untrusted input with {@link isStorageProvider}. */\n provider: StorageProviderName;\n config?: StorageProviderConfig;\n /** Verified operation profile for the `s3` slug, especially custom endpoints. */\n s3ProviderProfile?: S3ProviderProfile;\n}\n\n/**\n * Builds a {@link FilesSdkStorageDriver} for a provider named at runtime,\n * importing that provider's adapter — and only that one — on demand. A\n * deployment selects its store with a string (`'s3'`, `'gcs'`, `'azure'`,\n * `'r2'`, `'fs'`, …) and installs one native SDK, instead of the application\n * hard-coding a driver per backend.\n *\n * The `s3` slug additionally gets the exact verified provider profile and\n * signed-policy capabilities {@link createS3StorageDriver} attaches. Providers\n * not backed by the AWS S3 SDK expose what their adapter declares;\n * noncanonical S3-backed providers default to an unverified, read-only profile.\n * Import `@nestm/storage/files-sdk/s3` directly when the provider is known at\n * build time and the extra indirection buys nothing.\n *\n * @throws StorageError `INVALID_ARGUMENT` for an unknown slug, and whatever the\n * adapter reports (mapped) when required config or credentials are missing.\n */\nexport async function createProviderStorageDriver(\n options: ProviderStorageDriverOptions,\n): Promise<FilesSdkStorageDriver> {\n const { provider, config, s3ProviderProfile, ...filesOptions } = options;\n if (!isStorageProvider(provider)) {\n throw unknownProvider(provider);\n }\n\n // `loadFiles` owns the slug → adapter mapping and keeps the import lazy; the\n // client it returns is discarded so the caller's own driver options (prefix,\n // hooks, plugins, readonly, retries) apply to the instance the bridge builds.\n if (s3ProviderProfile !== undefined && provider !== 's3') {\n throw new StorageError(\n 's3ProviderProfile can only be used with the \"s3\" provider.',\n { code: StorageErrorCode.INVALID_ARGUMENT, permanent: true },\n );\n }\n const adapter = await resolveAdapter(provider, config, s3ProviderProfile);\n return createFilesSdkDriver({\n ...filesOptions,\n ...(LAZY_S3_PROVIDERS.has(provider) ? { readonly: true } : {}),\n adapter,\n });\n}\n\n// Files SDK 2.4 defers these AWS clients until the first operation; fetch engines\n// expose a different raw client. Neither form constitutes verified S3 authority.\nconst LAZY_S3_PROVIDERS = new Set<StorageProviderName>([\n 'minio',\n 'r2',\n 's3-fetch',\n]);\n\nfunction isS3BackedAdapter(adapter: Adapter): adapter is S3Adapter {\n try {\n const raw = adapter.raw;\n if (typeof raw !== 'object' || raw === null) return false;\n const candidate = raw as {\n readonly config?: unknown;\n readonly send?: unknown;\n };\n return (\n typeof candidate.send === 'function' &&\n typeof candidate.config === 'object' &&\n candidate.config !== null &&\n (candidate.config as { readonly serviceId?: unknown }).serviceId === 'S3'\n );\n } catch {\n return false;\n }\n}\n\nasync function resolveAdapter(\n provider: StorageProviderName,\n config: StorageProviderConfig | undefined,\n s3ProviderProfile: S3ProviderProfile | undefined,\n): Promise<Adapter> {\n const loaderConfig = withVerifiedS3ConditionalOptIn(\n provider,\n config,\n s3ProviderProfile,\n );\n let resolved;\n try {\n resolved = await loadFiles({ ...loaderConfig, provider });\n } catch (error) {\n throw mapFilesSdkError(error);\n }\n const { adapter } = resolved.files;\n if (provider === 'azure') {\n const { withAzureConditionalOperations } =\n await import('../azure/azure-conditional.js');\n return withAzureConditionalOperations(\n adapter as Parameters<typeof withAzureConditionalOperations>[0],\n );\n }\n if (provider === 'memory') {\n const { withMemoryConditionalOperations } = await import('../memory.js');\n return withMemoryConditionalOperations(\n adapter as Parameters<typeof withMemoryConditionalOperations>[0],\n );\n }\n if (provider === 'fs') {\n const { withFsConditionalMutation } = await import('../fs/index.js');\n return withFsConditionalMutation(\n adapter as Parameters<typeof withFsConditionalMutation>[0],\n );\n }\n if (!isS3BackedAdapter(adapter)) {\n if (LAZY_S3_PROVIDERS.has(provider)) {\n return Object.assign(Object.create(adapter) as Adapter, {\n physicalKey: Object.freeze({ maxBytes: 1024 }),\n signedDownloadPolicy: Object.freeze({ expiresIn: false }),\n });\n }\n return adapter;\n }\n // Every AWS-SDK-backed wrapper gets fail-closed S3 provenance and endpoint\n // hardening. Only the canonical `s3` slug may infer native AWS or accept an\n // explicit verified profile; named compatibles remain unverified/read-only.\n const { withS3Capabilities } = await import('../s3/index.js');\n const configJsonPublicBaseUrl = config?.configJson?.publicBaseUrl;\n const publicBaseUrl =\n config?.publicBaseUrl ??\n (typeof configJsonPublicBaseUrl === 'string'\n ? configJsonPublicBaseUrl\n : undefined);\n const s3Adapter = adapter as Parameters<typeof withS3Capabilities>[0];\n if (provider === 's3') {\n recordS3ConditionalRequestPermission(\n s3Adapter.raw,\n config?.configJson?.conditional !== false,\n );\n }\n recordS3ConstructionMetadata(s3Adapter.raw, {\n publicBaseUrlConfigured:\n provider !== 's3' ||\n config?.publicBaseUrl !== undefined ||\n configJsonPublicBaseUrl !== undefined,\n });\n return withS3Capabilities(s3Adapter, {\n ...(typeof config?.endpoint === 'string' && {\n endpoint: config.endpoint,\n }),\n ...(publicBaseUrl !== undefined && {\n publicBaseUrl,\n }),\n ...(s3ProviderProfile !== undefined && {\n providerProfile: s3ProviderProfile,\n }),\n });\n}\n\nfunction withVerifiedS3ConditionalOptIn(\n provider: StorageProviderName,\n config: StorageProviderConfig | undefined,\n s3ProviderProfile: S3ProviderProfile | undefined,\n): StorageProviderConfig | undefined {\n const configJsonEndpoint = config?.configJson?.endpoint;\n const hasCustomEndpoint =\n config?.endpoint !== undefined || configJsonEndpoint !== undefined;\n if (\n provider !== 's3' ||\n s3ProviderProfile === undefined ||\n config === undefined ||\n !hasCustomEndpoint ||\n config.configJson?.conditional !== undefined\n ) {\n return config;\n }\n\n // files-sdk requires this construction-time opt-in before it exposes native\n // conditional primitives for a custom endpoint. The branded provider profile\n // still narrows the exposed operations in withS3Capabilities below.\n return {\n ...config,\n configJson: { ...config.configJson, conditional: true },\n };\n}\n\nfunction unknownProvider(provider: string): StorageError {\n return new StorageError(\n `Unknown storage provider: ${JSON.stringify(provider)}. Known providers: ${PROVIDER_NAMES.join(', ')}.`,\n { code: StorageErrorCode.INVALID_ARGUMENT, permanent: true },\n );\n}\n\n/** Narrows an untrusted string — an env var, a config file — to a known slug. */\nexport function isStorageProvider(value: string): value is StorageProviderName {\n return getProvider(value) !== undefined;\n}\n\n/** Every provider that can be named, sorted by slug. */\nexport function listStorageProviders(): readonly Provider[] {\n return PROVIDER_NAMES.flatMap((slug) => getProvider(slug) ?? []);\n}\n\n/** One provider's display name, description, native SDKs, and env contract. */\nexport function getStorageProvider(slug: string): Provider | undefined {\n return getProvider(slug);\n}\n\n/**\n * Every environment variable a provider reads, flattened across required, all\n * credential modes, and optional. Useful for validating a deployment's config\n * before the first upload rather than at it.\n */\nexport function listStorageProviderEnvVars(slug: string): EnvVar[] {\n return listEnvVars(slug);\n}\n\n/** The subset of {@link listStorageProviderEnvVars} that carries secrets. */\nexport function listStorageProviderSecretEnvVars(slug: string): EnvVar[] {\n return getSecretEnvVars(slug);\n}\n\nexport type {\n EnvVar as StorageProviderEnvVar,\n Provider as StorageProviderInfo,\n} from 'files-sdk/providers';\n"]}
@@ -8,4 +8,5 @@ export { Aes256GcmStorageWorkspaceCursorCodec, STORAGE_WORKSPACE_CURSOR_VERSION,
8
8
  export { createStorageWorkspace, mountStorageWorkspace, } from './storage-workspace.js';
9
9
  export { DEFAULT_STORAGE_WORKSPACE_LIMITS, STORAGE_WORKSPACE_PERMISSIONS, type MountStorageWorkspaceOptions, type StorageWorkspaceBody, type StorageWorkspaceByteFile, type StorageWorkspaceCopyOptions, type StorageWorkspaceDeleteOptions, type StorageWorkspace, type StorageWorkspaceDirectory, type StorageWorkspaceEntry, type StorageWorkspaceFile, type StorageWorkspaceLimits, type StorageWorkspaceListOptions, type StorageWorkspaceMountOptions, type StorageWorkspaceMutationOptions, type StorageWorkspaceOverwriteOptions, type StorageWorkspacePage, type StorageWorkspacePermission, type StorageWorkspaceReadOptions, type StorageWorkspaceSearchMatch, type StorageWorkspaceSearchOptions, type StorageWorkspaceTextFile, type StorageWorkspaceUnconditionalDeleteOptions, type StorageWorkspaceWriteOptions, } from './storage-workspace.types.js';
10
10
  export { checkoutStorageCatalogText } from './storage-catalog-checkout.js';
11
+ export { StorageWorkingFiles, type StorageWorkingFile, } from './storage-working-files.js';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AACxF,mBAAmB,kCAAkC,CAAC;AACtD,mBAAmB,iCAAiC,CAAC;AACrD,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,0CAA0C,GAChD,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,oCAAoC,EACpC,gCAAgC,EAChC,kCAAkC,EAClC,KAAK,2CAA2C,EAChD,KAAK,2BAA2B,EAChC,KAAK,mCAAmC,EACxC,KAAK,mCAAmC,GACzC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,KAAK,gCAAgC,EACrC,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,KAAK,0CAA0C,EAC/C,KAAK,4BAA4B,GAClC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AACxF,mBAAmB,kCAAkC,CAAC;AACtD,mBAAmB,iCAAiC,CAAC;AACrD,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,0CAA0C,GAChD,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,oCAAoC,EACpC,gCAAgC,EAChC,kCAAkC,EAClC,KAAK,2CAA2C,EAChD,KAAK,2BAA2B,EAChC,KAAK,mCAAmC,EACxC,KAAK,mCAAmC,GACzC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,KAAK,gCAAgC,EACrC,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,KAAK,0CAA0C,EAC/C,KAAK,4BAA4B,GAClC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAE3E,OAAO,EACL,mBAAmB,EACnB,KAAK,kBAAkB,GACxB,MAAM,4BAA4B,CAAC"}
@@ -6,4 +6,5 @@ export { Aes256GcmStorageWorkspaceCursorCodec, STORAGE_WORKSPACE_CURSOR_VERSION,
6
6
  export { createStorageWorkspace, mountStorageWorkspace, } from './storage-workspace.js';
7
7
  export { DEFAULT_STORAGE_WORKSPACE_LIMITS, STORAGE_WORKSPACE_PERMISSIONS, } from './storage-workspace.types.js';
8
8
  export { checkoutStorageCatalogText } from './storage-catalog-checkout.js';
9
+ export { StorageWorkingFiles, } from './storage-working-files.js';
9
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AAGxF,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,sBAAsB,EACtB,qBAAqB,GAItB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,oCAAoC,EACpC,gCAAgC,EAChC,kCAAkC,GAKnC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gCAAgC,EAChC,6BAA6B,GAuB9B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC","sourcesContent":["export {\n StorageWorkspaceError,\n isStorageWorkspaceError,\n} from './storage-workspace.error.js';\nexport { StorageFileWorkflow } from './storage-file-workflow.js';\nexport { DEFAULT_STORAGE_FILE_WORKFLOW_LIMITS } from './storage-file-workflow.types.js';\nexport type * from './storage-file-workflow.types.js';\nexport type * from './storage-file-catalog.types.js';\nexport {\n protectStorageFileWorkflowWorkspace,\n protectStorageFileWorkflowOperation,\n getStorageFileWorkflow,\n getStorageFileCatalog,\n type StorageFileWorkflowProtection,\n type StorageFileWorkflowWorkspace,\n type ProtectStorageFileWorkflowWorkspaceOptions,\n} from './storage-file-workflow.protection.js';\nexport {\n Aes256GcmStorageWorkspaceCursorCodec,\n STORAGE_WORKSPACE_CURSOR_VERSION,\n STORAGE_WORKSPACE_MAX_CURSOR_BYTES,\n type Aes256GcmStorageWorkspaceCursorCodecOptions,\n type StorageWorkspaceCursorCodec,\n type StorageWorkspaceCursorConfiguration,\n type StorageWorkspaceCursorEncodeOptions,\n} from './storage-workspace.cursor.js';\nexport {\n createStorageWorkspace,\n mountStorageWorkspace,\n} from './storage-workspace.js';\nexport {\n DEFAULT_STORAGE_WORKSPACE_LIMITS,\n STORAGE_WORKSPACE_PERMISSIONS,\n type MountStorageWorkspaceOptions,\n type StorageWorkspaceBody,\n type StorageWorkspaceByteFile,\n type StorageWorkspaceCopyOptions,\n type StorageWorkspaceDeleteOptions,\n type StorageWorkspace,\n type StorageWorkspaceDirectory,\n type StorageWorkspaceEntry,\n type StorageWorkspaceFile,\n type StorageWorkspaceLimits,\n type StorageWorkspaceListOptions,\n type StorageWorkspaceMountOptions,\n type StorageWorkspaceMutationOptions,\n type StorageWorkspaceOverwriteOptions,\n type StorageWorkspacePage,\n type StorageWorkspacePermission,\n type StorageWorkspaceReadOptions,\n type StorageWorkspaceSearchMatch,\n type StorageWorkspaceSearchOptions,\n type StorageWorkspaceTextFile,\n type StorageWorkspaceUnconditionalDeleteOptions,\n type StorageWorkspaceWriteOptions,\n} from './storage-workspace.types.js';\n\nexport { checkoutStorageCatalogText } from './storage-catalog-checkout.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AAGxF,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,sBAAsB,EACtB,qBAAqB,GAItB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,oCAAoC,EACpC,gCAAgC,EAChC,kCAAkC,GAKnC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gCAAgC,EAChC,6BAA6B,GAuB9B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAE3E,OAAO,EACL,mBAAmB,GAEpB,MAAM,4BAA4B,CAAC","sourcesContent":["export {\n StorageWorkspaceError,\n isStorageWorkspaceError,\n} from './storage-workspace.error.js';\nexport { StorageFileWorkflow } from './storage-file-workflow.js';\nexport { DEFAULT_STORAGE_FILE_WORKFLOW_LIMITS } from './storage-file-workflow.types.js';\nexport type * from './storage-file-workflow.types.js';\nexport type * from './storage-file-catalog.types.js';\nexport {\n protectStorageFileWorkflowWorkspace,\n protectStorageFileWorkflowOperation,\n getStorageFileWorkflow,\n getStorageFileCatalog,\n type StorageFileWorkflowProtection,\n type StorageFileWorkflowWorkspace,\n type ProtectStorageFileWorkflowWorkspaceOptions,\n} from './storage-file-workflow.protection.js';\nexport {\n Aes256GcmStorageWorkspaceCursorCodec,\n STORAGE_WORKSPACE_CURSOR_VERSION,\n STORAGE_WORKSPACE_MAX_CURSOR_BYTES,\n type Aes256GcmStorageWorkspaceCursorCodecOptions,\n type StorageWorkspaceCursorCodec,\n type StorageWorkspaceCursorConfiguration,\n type StorageWorkspaceCursorEncodeOptions,\n} from './storage-workspace.cursor.js';\nexport {\n createStorageWorkspace,\n mountStorageWorkspace,\n} from './storage-workspace.js';\nexport {\n DEFAULT_STORAGE_WORKSPACE_LIMITS,\n STORAGE_WORKSPACE_PERMISSIONS,\n type MountStorageWorkspaceOptions,\n type StorageWorkspaceBody,\n type StorageWorkspaceByteFile,\n type StorageWorkspaceCopyOptions,\n type StorageWorkspaceDeleteOptions,\n type StorageWorkspace,\n type StorageWorkspaceDirectory,\n type StorageWorkspaceEntry,\n type StorageWorkspaceFile,\n type StorageWorkspaceLimits,\n type StorageWorkspaceListOptions,\n type StorageWorkspaceMountOptions,\n type StorageWorkspaceMutationOptions,\n type StorageWorkspaceOverwriteOptions,\n type StorageWorkspacePage,\n type StorageWorkspacePermission,\n type StorageWorkspaceReadOptions,\n type StorageWorkspaceSearchMatch,\n type StorageWorkspaceSearchOptions,\n type StorageWorkspaceTextFile,\n type StorageWorkspaceUnconditionalDeleteOptions,\n type StorageWorkspaceWriteOptions,\n} from './storage-workspace.types.js';\n\nexport { checkoutStorageCatalogText } from './storage-catalog-checkout.js';\n\nexport {\n StorageWorkingFiles,\n type StorageWorkingFile,\n} from './storage-working-files.js';\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { StorageFileCatalogCapability, StorageCatalogCommand } from './storage-file-catalog.types.js';
2
2
  import type { StorageFileWorkflowCapability } from './storage-file-workflow.types.js';
3
- /** Bounded, exact-revision catalog read into a sealed text checkpoint. */
3
+ /** Stream exact catalog windows into a sealed checkpoint without buffering the file. */
4
4
  export declare function checkoutStorageCatalogText<Receipt>(catalog: StorageFileCatalogCapability<Receipt>, workflow: StorageFileWorkflowCapability<Receipt>, input: StorageCatalogCommand & {
5
5
  readonly expectedEtag: string;
6
6
  }): Promise<import("./storage-file-workflow.types.js").StorageFileDraft<Receipt>>;
@@ -1 +1 @@
1
- {"version":3,"file":"storage-catalog-checkout.d.ts","sourceRoot":"","sources":["../../src/workspace/storage-catalog-checkout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAEtF,0EAA0E;AAC1E,wBAAsB,0BAA0B,CAAC,OAAO,EACtD,OAAO,EAAE,4BAA4B,CAAC,OAAO,CAAC,EAC9C,QAAQ,EAAE,6BAA6B,CAAC,OAAO,CAAC,EAChD,KAAK,EAAE,qBAAqB,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,iFA8CjE"}
1
+ {"version":3,"file":"storage-catalog-checkout.d.ts","sourceRoot":"","sources":["../../src/workspace/storage-catalog-checkout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAEtF,wFAAwF;AACxF,wBAAsB,0BAA0B,CAAC,OAAO,EACtD,OAAO,EAAE,4BAA4B,CAAC,OAAO,CAAC,EAC9C,QAAQ,EAAE,6BAA6B,CAAC,OAAO,CAAC,EAChD,KAAK,EAAE,qBAAqB,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,iFAkEjE"}
@@ -1,46 +1,60 @@
1
1
  import { StorageError } from '../storage.error.js';
2
- /** Bounded, exact-revision catalog read into a sealed text checkpoint. */
2
+ /** Stream exact catalog windows into a sealed checkpoint without buffering the file. */
3
3
  export async function checkoutStorageCatalogText(catalog, workflow, input) {
4
4
  const file = await catalog.stat(input);
5
5
  if (file.etag !== input.expectedEtag)
6
6
  throw new StorageError('The catalog revision changed.', {
7
7
  code: 'CONFLICT',
8
8
  });
9
- if (file.size > workflow.limits.maxTextBytes)
10
- throw new StorageError('Checkout exceeds the buffered text limit.', {
11
- code: 'LIMIT_EXCEEDED',
12
- });
13
- const pieces = [];
14
- let offset = 0;
15
- do {
16
- const page = await catalog.readWindow({ ...input, offset });
17
- if (page.etag !== input.expectedEtag ||
18
- page.totalBytes !== file.size ||
19
- page.offset !== offset)
20
- throw new StorageError('The catalog revision changed.', {
21
- code: 'CONFLICT',
22
- });
23
- if (page.content === null)
24
- throw new StorageError('Checkout requires a text file.', {
25
- code: 'INVALID_ARGUMENT',
26
- });
27
- const next = offset + new TextEncoder().encode(page.content).byteLength;
28
- if (next > file.size ||
29
- (page.nextOffset === null
30
- ? next !== file.size
31
- : page.nextOffset !== next || next <= offset))
32
- throw new StorageError('Invalid catalog text window.', {
33
- code: 'PROVIDER',
34
- });
35
- pieces.push(page.content);
36
- offset = next;
37
- if (page.nextOffset === null)
38
- break;
39
- } while (offset < file.size);
40
- return workflow.stageText({
41
- ...input,
42
- content: pieces.join(''),
9
+ return workflow.stageStream({
10
+ path: input.path,
11
+ expectedEtag: input.expectedEtag,
12
+ text: true,
13
+ signal: input.signal,
43
14
  idempotencyKey: input.commandId,
15
+ contentIdentity: JSON.stringify([file.fileId, file.etag, file.size]),
16
+ body: () => {
17
+ let offset = 0;
18
+ return new ReadableStream({
19
+ async pull(controller) {
20
+ try {
21
+ const page = await catalog.readWindow({ ...input, offset });
22
+ if (page.etag !== input.expectedEtag ||
23
+ page.totalBytes !== file.size ||
24
+ page.offset !== offset)
25
+ throw new StorageError('The catalog revision changed.', {
26
+ code: 'CONFLICT',
27
+ });
28
+ if (page.content === null)
29
+ throw new StorageError('Checkout requires a text file.', {
30
+ code: 'INVALID_ARGUMENT',
31
+ });
32
+ const next = offset + new TextEncoder().encode(page.content).byteLength;
33
+ if (next > file.size ||
34
+ (page.nextOffset === null
35
+ ? next !== file.size
36
+ : page.nextOffset !== next || next <= offset))
37
+ throw new StorageError('Invalid catalog text window.', {
38
+ code: 'PROVIDER',
39
+ });
40
+ controller.enqueue(new TextEncoder().encode(page.content));
41
+ offset = next;
42
+ if (page.nextOffset === null) {
43
+ const current = await catalog.stat(input);
44
+ if (current.etag !== input.expectedEtag ||
45
+ current.size !== file.size)
46
+ throw new StorageError('The catalog revision changed.', {
47
+ code: 'CONFLICT',
48
+ });
49
+ controller.close();
50
+ }
51
+ }
52
+ catch (error) {
53
+ controller.error(error);
54
+ }
55
+ },
56
+ }, { highWaterMark: 0 });
57
+ },
44
58
  });
45
59
  }
46
60
  //# sourceMappingURL=storage-catalog-checkout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-catalog-checkout.js","sourceRoot":"","sources":["../../src/workspace/storage-catalog-checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOnD,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAA8C,EAC9C,QAAgD,EAChD,KAAgE;IAEhE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,YAAY;QAClC,MAAM,IAAI,YAAY,CAAC,+BAA+B,EAAE;YACtD,IAAI,EAAE,UAAU;SACjB,CAAC,CAAC;IACL,IAAI,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,YAAY;QAC1C,MAAM,IAAI,YAAY,CAAC,2CAA2C,EAAE;YAClE,IAAI,EAAE,gBAAgB;SACvB,CAAC,CAAC;IACL,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,GAAG,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5D,IACE,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,YAAY;YAChC,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,IAAI;YAC7B,IAAI,CAAC,MAAM,KAAK,MAAM;YAEtB,MAAM,IAAI,YAAY,CAAC,+BAA+B,EAAE;gBACtD,IAAI,EAAE,UAAU;aACjB,CAAC,CAAC;QACL,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YACvB,MAAM,IAAI,YAAY,CAAC,gCAAgC,EAAE;gBACvD,IAAI,EAAE,kBAAkB;aACzB,CAAC,CAAC;QACL,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;QACxE,IACE,IAAI,GAAG,IAAI,CAAC,IAAI;YAChB,CAAC,IAAI,CAAC,UAAU,KAAK,IAAI;gBACvB,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;gBACpB,CAAC,CAAC,IAAI,CAAC,UAAU,KAAK,IAAI,IAAI,IAAI,IAAI,MAAM,CAAC;YAE/C,MAAM,IAAI,YAAY,CAAC,8BAA8B,EAAE;gBACrD,IAAI,EAAE,UAAU;aACjB,CAAC,CAAC;QACL,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,GAAG,IAAI,CAAC;QACd,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI;YAAE,MAAM;IACtC,CAAC,QAAQ,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE;IAC7B,OAAO,QAAQ,CAAC,SAAS,CAAC;QACxB,GAAG,KAAK;QACR,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,cAAc,EAAE,KAAK,CAAC,SAAS;KAChC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { StorageError } from '../storage.error.js';\nimport type {\n StorageFileCatalogCapability,\n StorageCatalogCommand,\n} from './storage-file-catalog.types.js';\nimport type { StorageFileWorkflowCapability } from './storage-file-workflow.types.js';\n\n/** Bounded, exact-revision catalog read into a sealed text checkpoint. */\nexport async function checkoutStorageCatalogText<Receipt>(\n catalog: StorageFileCatalogCapability<Receipt>,\n workflow: StorageFileWorkflowCapability<Receipt>,\n input: StorageCatalogCommand & { readonly expectedEtag: string },\n) {\n const file = await catalog.stat(input);\n if (file.etag !== input.expectedEtag)\n throw new StorageError('The catalog revision changed.', {\n code: 'CONFLICT',\n });\n if (file.size > workflow.limits.maxTextBytes)\n throw new StorageError('Checkout exceeds the buffered text limit.', {\n code: 'LIMIT_EXCEEDED',\n });\n const pieces: string[] = [];\n let offset = 0;\n do {\n const page = await catalog.readWindow({ ...input, offset });\n if (\n page.etag !== input.expectedEtag ||\n page.totalBytes !== file.size ||\n page.offset !== offset\n )\n throw new StorageError('The catalog revision changed.', {\n code: 'CONFLICT',\n });\n if (page.content === null)\n throw new StorageError('Checkout requires a text file.', {\n code: 'INVALID_ARGUMENT',\n });\n const next = offset + new TextEncoder().encode(page.content).byteLength;\n if (\n next > file.size ||\n (page.nextOffset === null\n ? next !== file.size\n : page.nextOffset !== next || next <= offset)\n )\n throw new StorageError('Invalid catalog text window.', {\n code: 'PROVIDER',\n });\n pieces.push(page.content);\n offset = next;\n if (page.nextOffset === null) break;\n } while (offset < file.size);\n return workflow.stageText({\n ...input,\n content: pieces.join(''),\n idempotencyKey: input.commandId,\n });\n}\n"]}
1
+ {"version":3,"file":"storage-catalog-checkout.js","sourceRoot":"","sources":["../../src/workspace/storage-catalog-checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOnD,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAA8C,EAC9C,QAAgD,EAChD,KAAgE;IAEhE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,YAAY;QAClC,MAAM,IAAI,YAAY,CAAC,+BAA+B,EAAE;YACtD,IAAI,EAAE,UAAU;SACjB,CAAC,CAAC;IACL,OAAO,QAAQ,CAAC,WAAW,CAAC;QAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,cAAc,EAAE,KAAK,CAAC,SAAS;QAC/B,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,IAAI,EAAE,GAAG,EAAE;YACT,IAAI,MAAM,GAAG,CAAC,CAAC;YACf,OAAO,IAAI,cAAc,CACvB;gBACE,KAAK,CAAC,IAAI,CAAC,UAAU;oBACnB,IAAI,CAAC;wBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;wBAC5D,IACE,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,YAAY;4BAChC,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,IAAI;4BAC7B,IAAI,CAAC,MAAM,KAAK,MAAM;4BAEtB,MAAM,IAAI,YAAY,CAAC,+BAA+B,EAAE;gCACtD,IAAI,EAAE,UAAU;6BACjB,CAAC,CAAC;wBACL,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;4BACvB,MAAM,IAAI,YAAY,CAAC,gCAAgC,EAAE;gCACvD,IAAI,EAAE,kBAAkB;6BACzB,CAAC,CAAC;wBACL,MAAM,IAAI,GACR,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;wBAC7D,IACE,IAAI,GAAG,IAAI,CAAC,IAAI;4BAChB,CAAC,IAAI,CAAC,UAAU,KAAK,IAAI;gCACvB,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;gCACpB,CAAC,CAAC,IAAI,CAAC,UAAU,KAAK,IAAI,IAAI,IAAI,IAAI,MAAM,CAAC;4BAE/C,MAAM,IAAI,YAAY,CAAC,8BAA8B,EAAE;gCACrD,IAAI,EAAE,UAAU;6BACjB,CAAC,CAAC;wBACL,UAAU,CAAC,OAAO,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;wBAC3D,MAAM,GAAG,IAAI,CAAC;wBACd,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;4BAC7B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;4BAC1C,IACE,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,YAAY;gCACnC,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;gCAE1B,MAAM,IAAI,YAAY,CAAC,+BAA+B,EAAE;oCACtD,IAAI,EAAE,UAAU;iCACjB,CAAC,CAAC;4BACL,UAAU,CAAC,KAAK,EAAE,CAAC;wBACrB,CAAC;oBACH,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBAC1B,CAAC;gBACH,CAAC;aACF,EACD,EAAE,aAAa,EAAE,CAAC,EAAE,CACrB,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { StorageError } from '../storage.error.js';\nimport type {\n StorageFileCatalogCapability,\n StorageCatalogCommand,\n} from './storage-file-catalog.types.js';\nimport type { StorageFileWorkflowCapability } from './storage-file-workflow.types.js';\n\n/** Stream exact catalog windows into a sealed checkpoint without buffering the file. */\nexport async function checkoutStorageCatalogText<Receipt>(\n catalog: StorageFileCatalogCapability<Receipt>,\n workflow: StorageFileWorkflowCapability<Receipt>,\n input: StorageCatalogCommand & { readonly expectedEtag: string },\n) {\n const file = await catalog.stat(input);\n if (file.etag !== input.expectedEtag)\n throw new StorageError('The catalog revision changed.', {\n code: 'CONFLICT',\n });\n return workflow.stageStream({\n path: input.path,\n expectedEtag: input.expectedEtag,\n text: true,\n signal: input.signal,\n idempotencyKey: input.commandId,\n contentIdentity: JSON.stringify([file.fileId, file.etag, file.size]),\n body: () => {\n let offset = 0;\n return new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n try {\n const page = await catalog.readWindow({ ...input, offset });\n if (\n page.etag !== input.expectedEtag ||\n page.totalBytes !== file.size ||\n page.offset !== offset\n )\n throw new StorageError('The catalog revision changed.', {\n code: 'CONFLICT',\n });\n if (page.content === null)\n throw new StorageError('Checkout requires a text file.', {\n code: 'INVALID_ARGUMENT',\n });\n const next =\n offset + new TextEncoder().encode(page.content).byteLength;\n if (\n next > file.size ||\n (page.nextOffset === null\n ? next !== file.size\n : page.nextOffset !== next || next <= offset)\n )\n throw new StorageError('Invalid catalog text window.', {\n code: 'PROVIDER',\n });\n controller.enqueue(new TextEncoder().encode(page.content));\n offset = next;\n if (page.nextOffset === null) {\n const current = await catalog.stat(input);\n if (\n current.etag !== input.expectedEtag ||\n current.size !== file.size\n )\n throw new StorageError('The catalog revision changed.', {\n code: 'CONFLICT',\n });\n controller.close();\n }\n } catch (error) {\n controller.error(error);\n }\n },\n },\n { highWaterMark: 0 },\n );\n },\n });\n}\n"]}
@@ -0,0 +1,3 @@
1
+ /** Bounded byte rechunking; each text chunk ends at a complete Unicode scalar. */
2
+ export declare function chunkStorageDraftStream(source: ReadableStream<Uint8Array>, maxBytes: number, text: boolean, signal: AbortSignal): AsyncGenerator<Uint8Array<ArrayBufferLike>, void, unknown>;
3
+ //# sourceMappingURL=storage-draft-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-draft-stream.d.ts","sourceRoot":"","sources":["../../src/workspace/storage-draft-stream.ts"],"names":[],"mappings":"AAGA,kFAAkF;AAClF,wBAAuB,uBAAuB,CAC5C,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,WAAW,8DAiDpB"}
@@ -0,0 +1,57 @@
1
+ import { trimStorageUtf8Chunk } from '../bytes/index.js';
2
+ import { StorageError } from '../storage.error.js';
3
+ /** Bounded byte rechunking; each text chunk ends at a complete Unicode scalar. */
4
+ export async function* chunkStorageDraftStream(source, maxBytes, text, signal) {
5
+ if (maxBytes < (text ? 4 : 1)) {
6
+ await source.cancel().catch(() => { });
7
+ throw new StorageError('Chunk limit is too small.', {
8
+ code: 'LIMIT_EXCEEDED',
9
+ });
10
+ }
11
+ let buffer = new Uint8Array(maxBytes);
12
+ let filled = 0;
13
+ const reader = source.getReader();
14
+ const abort = () => {
15
+ void reader.cancel(signal.reason).catch(() => { });
16
+ };
17
+ signal.addEventListener('abort', abort, { once: true });
18
+ try {
19
+ signal.throwIfAborted();
20
+ for (;;) {
21
+ const next = await reader.read();
22
+ signal.throwIfAborted();
23
+ if (next.done)
24
+ break;
25
+ for (let at = 0; at < next.value.length;) {
26
+ const count = Math.min(maxBytes - filled, next.value.length - at);
27
+ buffer.set(next.value.subarray(at, at + count), filled);
28
+ filled += count;
29
+ at += count;
30
+ if (filled === maxBytes) {
31
+ const complete = text
32
+ ? trimStorageUtf8Chunk(buffer, { final: false })
33
+ : buffer;
34
+ if (text)
35
+ new TextDecoder('utf-8', { fatal: true }).decode(complete);
36
+ yield complete;
37
+ const rest = buffer.subarray(complete.length);
38
+ buffer = new Uint8Array(maxBytes);
39
+ buffer.set(rest);
40
+ filled = rest.length;
41
+ }
42
+ }
43
+ }
44
+ if (filled) {
45
+ const final = buffer.subarray(0, filled);
46
+ if (text)
47
+ new TextDecoder('utf-8', { fatal: true }).decode(final);
48
+ yield final;
49
+ }
50
+ }
51
+ finally {
52
+ signal.removeEventListener('abort', abort);
53
+ await reader.cancel().catch(() => { });
54
+ reader.releaseLock();
55
+ }
56
+ }
57
+ //# sourceMappingURL=storage-draft-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-draft-stream.js","sourceRoot":"","sources":["../../src/workspace/storage-draft-stream.ts"],"names":[],"mappings":"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","sourcesContent":["import { trimStorageUtf8Chunk } from '../bytes/index.js';\nimport { StorageError } from '../storage.error.js';\n\n/** Bounded byte rechunking; each text chunk ends at a complete Unicode scalar. */\nexport async function* chunkStorageDraftStream(\n source: ReadableStream<Uint8Array>,\n maxBytes: number,\n text: boolean,\n signal: AbortSignal,\n) {\n if (maxBytes < (text ? 4 : 1)) {\n await source.cancel().catch(() => {});\n throw new StorageError('Chunk limit is too small.', {\n code: 'LIMIT_EXCEEDED',\n });\n }\n let buffer = new Uint8Array(maxBytes);\n let filled = 0;\n const reader = source.getReader();\n const abort = () => {\n void reader.cancel(signal.reason).catch(() => {});\n };\n signal.addEventListener('abort', abort, { once: true });\n try {\n signal.throwIfAborted();\n for (;;) {\n const next = await reader.read();\n signal.throwIfAborted();\n if (next.done) break;\n for (let at = 0; at < next.value.length;) {\n const count = Math.min(maxBytes - filled, next.value.length - at);\n buffer.set(next.value.subarray(at, at + count), filled);\n filled += count;\n at += count;\n if (filled === maxBytes) {\n const complete = text\n ? trimStorageUtf8Chunk(buffer, { final: false })\n : buffer;\n if (text) new TextDecoder('utf-8', { fatal: true }).decode(complete);\n yield complete;\n const rest = buffer.subarray(complete.length);\n buffer = new Uint8Array(maxBytes);\n buffer.set(rest);\n filled = rest.length;\n }\n }\n }\n if (filled) {\n const final = buffer.subarray(0, filled);\n if (text) new TextDecoder('utf-8', { fatal: true }).decode(final);\n yield final;\n }\n } finally {\n signal.removeEventListener('abort', abort);\n await reader.cancel().catch(() => {});\n reader.releaseLock();\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"storage-file-workflow.d.ts","sourceRoot":"","sources":["../../src/workspace/storage-file-workflow.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,+BAA+B,EAGpC,KAAK,6BAA6B,EAGlC,KAAK,0BAA0B,EAIhC,MAAM,kCAAkC,CAAC;AAE1C,qBAAa,mBAAmB,CAAC,KAAK,EAAE,OAAO;;IAE7C,YAAY,OAAO,EAAE,0BAA0B,CAAC,KAAK,EAAE,OAAO,CAAC,EAE9D;IAED,KAAK,CACH,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE,+BAAoC,GAC5C,6BAA6B,CAAC,OAAO,CAAC,CAoiBxC;CA6FF"}
1
+ {"version":3,"file":"storage-file-workflow.d.ts","sourceRoot":"","sources":["../../src/workspace/storage-file-workflow.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,+BAA+B,EAGpC,KAAK,6BAA6B,EAGlC,KAAK,0BAA0B,EAIhC,MAAM,kCAAkC,CAAC;AAE1C,qBAAa,mBAAmB,CAAC,KAAK,EAAE,OAAO;;IAE7C,YAAY,OAAO,EAAE,0BAA0B,CAAC,KAAK,EAAE,OAAO,CAAC,EAE9D;IAED,KAAK,CACH,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE,+BAAoC,GAC5C,6BAA6B,CAAC,OAAO,CAAC,CAkpBxC;CA6FF"}
@@ -1,4 +1,5 @@
1
- import { applyStorageTextEdit } from '../core/storage-text-edit.js';
1
+ import { searchStorageText } from '../core/storage-text.js';
2
+ import { editStorageTextStream } from '../core/storage-text-stream-edit.js';
2
3
  import { stageStorageTextDraft } from './storage-text-draft.js';
3
4
  import { createHash, randomUUID } from 'node:crypto';
4
5
  import { StorageError } from '../storage.error.js';
@@ -59,6 +60,14 @@ export class StorageFileWorkflow {
59
60
  signal: AbortSignal.any([boundSignal, restriction.signal].filter((signal) => signal !== undefined)),
60
61
  });
61
62
  },
63
+ lookup: async (input) => {
64
+ const signal = operation('read', input);
65
+ validateIdentity(input.idempotencyKey);
66
+ return transaction(signal, async (tx) => {
67
+ const draft = await tx.findDraftByKey(input.idempotencyKey);
68
+ return draft === null ? null : summary(draft);
69
+ });
70
+ },
62
71
  begin: async (input) => {
63
72
  const signal = operation('write', input);
64
73
  requireMutation(input.expectedEtag);
@@ -138,6 +147,61 @@ export class StorageFileWorkflow {
138
147
  }),
139
148
  }));
140
149
  },
150
+ stageStream: async (request) => {
151
+ const input = { ...request };
152
+ if (input.sourceDraftId !== undefined)
153
+ operation('read', input);
154
+ const signal = operation('write', input);
155
+ requireMutation(input.expectedEtag);
156
+ validateIdentity(input.idempotencyKey);
157
+ assertWorkspacePath(input.path, limits.maxPathBytes, {
158
+ allowRoot: false,
159
+ });
160
+ if (typeof input.text !== 'boolean' ||
161
+ typeof input.contentIdentity !== 'string' ||
162
+ !input.contentIdentity.length ||
163
+ input.contentIdentity.length > 1024)
164
+ invalid('Invalid streaming draft identity.');
165
+ if (input.expectedEtag !== undefined &&
166
+ (!input.expectedEtag.length || input.expectedEtag.length > 1024))
167
+ invalid('Invalid ETag.');
168
+ return summary(await stageStorageTextDraft({
169
+ scope,
170
+ signal,
171
+ limits,
172
+ content,
173
+ requireMutation,
174
+ transaction: (work) => transaction(signal, work),
175
+ idempotencyKey: input.idempotencyKey,
176
+ fingerprint: digest(new TextEncoder().encode(JSON.stringify([
177
+ 'stage-stream',
178
+ input.path,
179
+ input.expectedEtag ?? null,
180
+ input.text,
181
+ input.contentIdentity,
182
+ input.sourceDraftId ?? null,
183
+ input.expectedSize ?? null,
184
+ ]))),
185
+ load: async () => {
186
+ const source = input.sourceDraftId === undefined
187
+ ? null
188
+ : await transaction(signal, (tx) => requireDraft(tx, input.sourceDraftId));
189
+ if (source !== null &&
190
+ (source.path !== input.path ||
191
+ source.expectedEtag !== (input.expectedEtag ?? null) ||
192
+ source.size !== input.expectedSize ||
193
+ !['open', 'sealed'].includes(source.status)))
194
+ conflict('The source checkpoint changed.');
195
+ return {
196
+ path: input.path,
197
+ expectedEtag: input.expectedEtag ?? null,
198
+ source,
199
+ text: input.text,
200
+ content: input.body(),
201
+ };
202
+ },
203
+ }));
204
+ },
141
205
  reviseText: async (request) => {
142
206
  const input = {
143
207
  ...request,
@@ -147,10 +211,11 @@ export class StorageFileWorkflow {
147
211
  const signal = operation('write', input);
148
212
  validateIdentity(input.idempotencyKey);
149
213
  validateIdentity(input.draftId);
214
+ if (input.requestIdentity !== undefined &&
215
+ input.requestIdentity.length > 4096)
216
+ invalid('Invalid edit request identity.');
150
217
  storageInteger(input.expectedSize, 'expectedSize');
151
- if (input.expectedSize > limits.maxTextBytes ||
152
- input.changes.length < 1 ||
153
- input.changes.length > limits.maxEdits)
218
+ if (input.changes.length < 1 || input.changes.length > limits.maxEdits)
154
219
  throw new StorageError('Text editing exceeds the configured limits.', { code: 'LIMIT_EXCEEDED' });
155
220
  return summary(await stageStorageTextDraft({
156
221
  scope,
@@ -165,6 +230,9 @@ export class StorageFileWorkflow {
165
230
  input.draftId,
166
231
  input.expectedSize,
167
232
  input.changes,
233
+ ...(input.requestIdentity === undefined
234
+ ? []
235
+ : [input.requestIdentity]),
168
236
  ]))),
169
237
  load: async () => {
170
238
  const source = await transaction(signal, (tx) => requireDraft(tx, input.draftId));
@@ -173,16 +241,15 @@ export class StorageFileWorkflow {
173
241
  !['open', 'sealed'].includes(source.status) ||
174
242
  source.size !== input.expectedSize)
175
243
  conflict('The source must be an unfinished text draft at the expected size.');
176
- const bytes = await collectStorageBytes(this.#stream(scope, source, limits, signal), limits.maxTextBytes, signal);
177
- const original = new TextDecoder('utf-8', {
178
- fatal: true,
179
- ignoreBOM: true,
180
- }).decode(bytes);
181
244
  return {
182
245
  source,
183
246
  path: source.path,
184
247
  expectedEtag: source.expectedEtag,
185
- content: applyStorageTextEdit(original, { kind: 'batch', changes: input.changes }, { maxBytes: limits.maxTextBytes, maxEdits: limits.maxEdits }),
248
+ content: editStorageTextStream(this.#stream(scope, source, limits, signal), input.changes, {
249
+ maxEditBytes: limits.maxTextBytes,
250
+ maxEdits: limits.maxEdits,
251
+ signal,
252
+ }),
186
253
  };
187
254
  },
188
255
  }));
@@ -222,6 +289,25 @@ export class StorageFileWorkflow {
222
289
  : { content: null, offset, nextOffset: null };
223
290
  return { ...summary(draft), ...window };
224
291
  },
292
+ searchText: async (input) => {
293
+ const signal = operation('read', input);
294
+ const draft = await transaction(signal, (tx) => requireDraft(tx, input.draftId));
295
+ if (!draft.text ||
296
+ draft.status === 'cancelled' ||
297
+ draft.size !== input.expectedSize)
298
+ conflict('The source checkpoint changed.');
299
+ const result = await searchStorageText(async (range) => this.#stream(scope, draft, limits, signal, range.start, range.end + 1), {
300
+ size: draft.size,
301
+ query: input.query,
302
+ offset: input.offset,
303
+ maxScanBytes: 262144,
304
+ maxMatches: 12,
305
+ maxSnippetCharacters: 320,
306
+ maxReadBytes: limits.maxReadBytes,
307
+ signal,
308
+ });
309
+ return { ...result, path: draft.path, etag: draft.id };
310
+ },
225
311
  readText: async (request) => {
226
312
  const input = { ...request };
227
313
  const signal = operation('read', input);