@nestm/storage 0.1.0-alpha.17 → 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 +6 -0
  2. package/README.md +21 -1
  3. package/dist/ai-sdk/ai-sdk-file-workflow-tools.d.ts +2 -0
  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 +19 -0
  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.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":"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","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);