@superblocksteam/workspace-sync 0.0.0-stage → 2.0.178-next.1

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 (123) hide show
  1. package/LICENSE.txt +87 -0
  2. package/README.md +43 -2
  3. package/dist/atomic-write.d.ts +7 -0
  4. package/dist/atomic-write.d.ts.map +1 -0
  5. package/dist/atomic-write.js +33 -0
  6. package/dist/atomic-write.js.map +1 -0
  7. package/dist/branch-write-guard.d.ts +32 -0
  8. package/dist/branch-write-guard.d.ts.map +1 -0
  9. package/dist/branch-write-guard.js +38 -0
  10. package/dist/branch-write-guard.js.map +1 -0
  11. package/dist/control.d.ts +237 -0
  12. package/dist/control.d.ts.map +1 -0
  13. package/dist/control.js +242 -0
  14. package/dist/control.js.map +1 -0
  15. package/dist/dbfs-transport.d.ts +52 -0
  16. package/dist/dbfs-transport.d.ts.map +1 -0
  17. package/dist/dbfs-transport.js +21 -0
  18. package/dist/dbfs-transport.js.map +1 -0
  19. package/dist/download.d.ts +54 -0
  20. package/dist/download.d.ts.map +1 -0
  21. package/dist/download.js +475 -0
  22. package/dist/download.js.map +1 -0
  23. package/dist/draft-helpers.d.ts +114 -0
  24. package/dist/draft-helpers.d.ts.map +1 -0
  25. package/dist/draft-helpers.js +760 -0
  26. package/dist/draft-helpers.js.map +1 -0
  27. package/dist/draft-journal-store.d.ts +30 -0
  28. package/dist/draft-journal-store.d.ts.map +1 -0
  29. package/dist/draft-journal-store.js +7 -0
  30. package/dist/draft-journal-store.js.map +1 -0
  31. package/dist/draft-state-report-metrics.d.ts +5 -0
  32. package/dist/draft-state-report-metrics.d.ts.map +1 -0
  33. package/dist/draft-state-report-metrics.js +36 -0
  34. package/dist/draft-state-report-metrics.js.map +1 -0
  35. package/dist/file-draft-journal-store.d.ts +26 -0
  36. package/dist/file-draft-journal-store.d.ts.map +1 -0
  37. package/dist/file-draft-journal-store.js +123 -0
  38. package/dist/file-draft-journal-store.js.map +1 -0
  39. package/dist/flush-scheduler.d.ts +24 -0
  40. package/dist/flush-scheduler.d.ts.map +1 -0
  41. package/dist/flush-scheduler.js +36 -0
  42. package/dist/flush-scheduler.js.map +1 -0
  43. package/dist/hash-cache.d.ts +16 -0
  44. package/dist/hash-cache.d.ts.map +1 -0
  45. package/dist/hash-cache.js +62 -0
  46. package/dist/hash-cache.js.map +1 -0
  47. package/dist/hash-dir-tree.d.ts +10 -0
  48. package/dist/hash-dir-tree.d.ts.map +1 -0
  49. package/dist/hash-dir-tree.js +53 -0
  50. package/dist/hash-dir-tree.js.map +1 -0
  51. package/dist/index.d.ts +27 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +27 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/list-dir.d.ts +31 -0
  56. package/dist/list-dir.d.ts.map +1 -0
  57. package/dist/list-dir.js +153 -0
  58. package/dist/list-dir.js.map +1 -0
  59. package/dist/logger.d.ts +16 -0
  60. package/dist/logger.d.ts.map +1 -0
  61. package/dist/logger.js +16 -0
  62. package/dist/logger.js.map +1 -0
  63. package/dist/native-draft-session.d.ts +28 -0
  64. package/dist/native-draft-session.d.ts.map +1 -0
  65. package/dist/native-draft-session.js +154 -0
  66. package/dist/native-draft-session.js.map +1 -0
  67. package/dist/rpc-client.d.ts +7 -0
  68. package/dist/rpc-client.d.ts.map +1 -0
  69. package/dist/rpc-client.js +2 -0
  70. package/dist/rpc-client.js.map +1 -0
  71. package/dist/safe-directory-reader.d.ts +28 -0
  72. package/dist/safe-directory-reader.d.ts.map +1 -0
  73. package/dist/safe-directory-reader.js +136 -0
  74. package/dist/safe-directory-reader.js.map +1 -0
  75. package/dist/server-tree.d.ts +56 -0
  76. package/dist/server-tree.d.ts.map +1 -0
  77. package/dist/server-tree.js +179 -0
  78. package/dist/server-tree.js.map +1 -0
  79. package/dist/session-draft-journal-store.d.ts +42 -0
  80. package/dist/session-draft-journal-store.d.ts.map +1 -0
  81. package/dist/session-draft-journal-store.js +177 -0
  82. package/dist/session-draft-journal-store.js.map +1 -0
  83. package/dist/snapshot/take-snapshot.d.ts +41 -0
  84. package/dist/snapshot/take-snapshot.d.ts.map +1 -0
  85. package/dist/snapshot/take-snapshot.js +244 -0
  86. package/dist/snapshot/take-snapshot.js.map +1 -0
  87. package/dist/snapshot/types.d.ts +25 -0
  88. package/dist/snapshot/types.d.ts.map +1 -0
  89. package/dist/snapshot/types.js +3 -0
  90. package/dist/snapshot/types.js.map +1 -0
  91. package/dist/snapshot/upload-snapshot.d.ts +33 -0
  92. package/dist/snapshot/upload-snapshot.d.ts.map +1 -0
  93. package/dist/snapshot/upload-snapshot.js +113 -0
  94. package/dist/snapshot/upload-snapshot.js.map +1 -0
  95. package/dist/socket-dbfs-transport.d.ts +32 -0
  96. package/dist/socket-dbfs-transport.d.ts.map +1 -0
  97. package/dist/socket-dbfs-transport.js +126 -0
  98. package/dist/socket-dbfs-transport.js.map +1 -0
  99. package/dist/sync-credential-provider.d.ts +27 -0
  100. package/dist/sync-credential-provider.d.ts.map +1 -0
  101. package/dist/sync-credential-provider.js +22 -0
  102. package/dist/sync-credential-provider.js.map +1 -0
  103. package/dist/sync-engine.d.ts +119 -0
  104. package/dist/sync-engine.d.ts.map +1 -0
  105. package/dist/sync-engine.js +358 -0
  106. package/dist/sync-engine.js.map +1 -0
  107. package/dist/sync-paths.d.ts +61 -0
  108. package/dist/sync-paths.d.ts.map +1 -0
  109. package/dist/sync-paths.js +107 -0
  110. package/dist/sync-paths.js.map +1 -0
  111. package/dist/sync-telemetry.d.ts +12 -0
  112. package/dist/sync-telemetry.d.ts.map +1 -0
  113. package/dist/sync-telemetry.js +33 -0
  114. package/dist/sync-telemetry.js.map +1 -0
  115. package/dist/testing.d.ts +66 -0
  116. package/dist/testing.d.ts.map +1 -0
  117. package/dist/testing.js +180 -0
  118. package/dist/testing.js.map +1 -0
  119. package/dist/workspace-state-store.d.ts +52 -0
  120. package/dist/workspace-state-store.d.ts.map +1 -0
  121. package/dist/workspace-state-store.js +45 -0
  122. package/dist/workspace-state-store.js.map +1 -0
  123. package/package.json +59 -3
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Path rules that decide what the DBFS sync uploads. Moved here from
3
+ * `vite-plugin-file-sync/src/file-system-helpers.ts`, which re-exports them.
4
+ */
5
+ /**
6
+ * App-root names that hold private configuration (credentials, registry
7
+ * tokens, package-manager hooks). The native sandbox denies the agent access
8
+ * to them. Matched case-insensitively.
9
+ */
10
+ export const PRIVATE_CONFIG_NAMES = [
11
+ ".aws",
12
+ ".env",
13
+ ".env.development",
14
+ ".env.development.local",
15
+ ".env.local",
16
+ ".env.production",
17
+ ".env.production.local",
18
+ ".env.test",
19
+ ".env.test.local",
20
+ ".netrc",
21
+ ".npmrc",
22
+ ".npmrc.default",
23
+ ".pnpmfile.cjs",
24
+ ".pnpmfile.mjs",
25
+ ".pnpmrc",
26
+ ".ssh",
27
+ ".superblocks",
28
+ ];
29
+ export const PRIVATE_CONFIG_PREFIXES = [".env.", ".npmrc."];
30
+ export function isPrivateConfigName(name) {
31
+ const lower = name.toLowerCase();
32
+ return (PRIVATE_CONFIG_NAMES.includes(lower) ||
33
+ PRIVATE_CONFIG_PREFIXES.some((prefix) => lower.startsWith(prefix)));
34
+ }
35
+ /**
36
+ * Names no upload snapshot includes. `pnpm-workspace.yaml` is read-only to
37
+ * native tools but still persisted, because it holds the app's pnpm overrides.
38
+ */
39
+ export function isSnapshotExcludedName(name) {
40
+ return codeModeExcludedFiles.includes(name) || name === ".superblocks";
41
+ }
42
+ /** Native system skills are generated per pod and never synced. */
43
+ export const NATIVE_SYSTEM_SKILLS_ROOT = "skills/system";
44
+ export const CHECKLIST_PERSISTED_RELATIVE_PATH = ".superblocks/ai/checklist.json";
45
+ const CHECKLIST_PERSISTED_DIR_PREFIX = ".superblocks/ai";
46
+ /**
47
+ * Sentinel written at the end of the CLI-side brownfield migration that
48
+ * strips public-host `resolved` URLs from `package-lock.json` and repopulates
49
+ * them via `npm install` against the active private registry. Lives under
50
+ * `.superblocks/` (otherwise excluded from sync) but is explicitly persisted
51
+ * so the migration survives across pod cycles. Path is stable because both
52
+ * the CLI (writer) and the sync allowlist (reader) need to agree.
53
+ */
54
+ export const PRIVATE_REGISTRY_MIGRATION_SENTINEL_RELATIVE_PATH = ".superblocks/private-registry-migrated";
55
+ /**
56
+ * What the CLI's npm-to-pnpm app migration keeps so it can turn a converted
57
+ * app back into an npm app once pnpm is off for its org: the npm lockfile it
58
+ * replaced and what it changed. Persisted like the sentinel above, because the
59
+ * rollback happens on a later pod.
60
+ */
61
+ export const NPM_TO_PNPM_STATE_RELATIVE_PATH = ".superblocks/npm-to-pnpm.json";
62
+ /** The template ranges whose upgrade failed, so later pods don't retry them. */
63
+ export const TEMPLATE_DEPENDENCIES_STATE_RELATIVE_PATH = ".superblocks/template-dependencies.json";
64
+ export const codeModeExcludedFiles = [".env", ".npmrc"];
65
+ /**
66
+ * Suffix of the incremental build-info cache files TypeScript writes in build
67
+ * mode (`tsc -b`). The prefix varies per project (e.g. `tsconfig.tsbuildinfo`,
68
+ * `tsconfig.client.tsbuildinfo`), so exclusion must be suffix-based rather than
69
+ * exact-name based.
70
+ */
71
+ export const TSBUILDINFO_FILE_SUFFIX = ".tsbuildinfo";
72
+ /**
73
+ * Whether a file (by basename) should be excluded from the dbfs sync. Covers
74
+ * the enumerated `codeModeExcludedFiles` plus any `*.tsbuildinfo` build cache
75
+ * emitted by `tsc -b`.
76
+ */
77
+ export function isCodeModeExcludedFile(name) {
78
+ return (codeModeExcludedFiles.includes(name) ||
79
+ name.endsWith(TSBUILDINFO_FILE_SUFFIX));
80
+ }
81
+ function isPersistedChecklistPath(relativePath) {
82
+ return relativePath === CHECKLIST_PERSISTED_RELATIVE_PATH;
83
+ }
84
+ function isPrivateRegistryMigrationSentinelPath(relativePath) {
85
+ return relativePath === PRIVATE_REGISTRY_MIGRATION_SENTINEL_RELATIVE_PATH;
86
+ }
87
+ /**
88
+ * Allowlist for files that live under `.superblocks/` but must still round-
89
+ * trip through DBFS sync. The directory is otherwise excluded from upload
90
+ * (see `listLocalDirectory`); anything that needs to persist across pod
91
+ * cycles has to be enumerated here.
92
+ */
93
+ export function isPersistedSuperblocksFile(relativePath) {
94
+ return (isPersistedChecklistPath(relativePath) ||
95
+ isPrivateRegistryMigrationSentinelPath(relativePath) ||
96
+ relativePath === NPM_TO_PNPM_STATE_RELATIVE_PATH ||
97
+ relativePath === TEMPLATE_DEPENDENCIES_STATE_RELATIVE_PATH);
98
+ }
99
+ function isPersistedChecklistAncestorPath(relativePath) {
100
+ return (relativePath === ".superblocks" ||
101
+ relativePath === CHECKLIST_PERSISTED_DIR_PREFIX);
102
+ }
103
+ export function isPersistedChecklistPathOrAncestor(relativePath) {
104
+ return (isPersistedChecklistPath(relativePath) ||
105
+ isPersistedChecklistAncestorPath(relativePath));
106
+ }
107
+ //# sourceMappingURL=sync-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-paths.js","sourceRoot":"","sources":["../src/sync-paths.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAsB;IACrD,MAAM;IACN,MAAM;IACN,kBAAkB;IAClB,wBAAwB;IACxB,YAAY;IACZ,iBAAiB;IACjB,uBAAuB;IACvB,WAAW;IACX,iBAAiB;IACjB,QAAQ;IACR,QAAQ;IACR,gBAAgB;IAChB,eAAe;IACf,eAAe;IACf,SAAS;IACT,MAAM;IACN,cAAc;CACf,CAAC;AACF,MAAM,CAAC,MAAM,uBAAuB,GAAsB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAE/E,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,CACL,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpC,uBAAuB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CACnE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,cAAc,CAAC;AACzE,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,MAAM,yBAAyB,GAAG,eAAe,CAAC;AAEzD,MAAM,CAAC,MAAM,iCAAiC,GAC5C,gCAAgC,CAAC;AACnC,MAAM,8BAA8B,GAAG,iBAAiB,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iDAAiD,GAC5D,wCAAwC,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,+BAA+B,CAAC;AAE/E,gFAAgF;AAChF,MAAM,CAAC,MAAM,yCAAyC,GACpD,yCAAyC,CAAC;AAE5C,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,cAAc,CAAC;AAEtD;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,CACL,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CACvC,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,YAAoB;IACpD,OAAO,YAAY,KAAK,iCAAiC,CAAC;AAC5D,CAAC;AAED,SAAS,sCAAsC,CAAC,YAAoB;IAClE,OAAO,YAAY,KAAK,iDAAiD,CAAC;AAC5E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,YAAoB;IAC7D,OAAO,CACL,wBAAwB,CAAC,YAAY,CAAC;QACtC,sCAAsC,CAAC,YAAY,CAAC;QACpD,YAAY,KAAK,+BAA+B;QAChD,YAAY,KAAK,yCAAyC,CAC3D,CAAC;AACJ,CAAC;AAED,SAAS,gCAAgC,CAAC,YAAoB;IAC5D,OAAO,CACL,YAAY,KAAK,cAAc;QAC/B,YAAY,KAAK,8BAA8B,CAChD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kCAAkC,CAChD,YAAoB;IAEpB,OAAO,CACL,wBAAwB,CAAC,YAAY,CAAC;QACtC,gCAAgC,CAAC,YAAY,CAAC,CAC/C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type Counter, type Gauge, type Histogram, type Meter, type Span, type Tracer } from "@opentelemetry/api";
2
+ export type CommitOutcome = "committed" | "unchanged" | "rejected" | "error";
3
+ export interface SyncTelemetry {
4
+ tracer: Tracer;
5
+ commitDuration: Histogram;
6
+ rehydrateOutcome: Counter;
7
+ unsynced: Gauge;
8
+ }
9
+ export declare function createSyncTelemetry(tracer: Tracer | undefined, meter: Meter | undefined): SyncTelemetry;
10
+ /** Runs `run` in an active span, recording a thrown error on it. */
11
+ export declare function inSpan<T>(tracer: Tracer, name: string, run: (span: Span) => Promise<T>): Promise<T>;
12
+ //# sourceMappingURL=sync-telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-telemetry.d.ts","sourceRoot":"","sources":["../src/sync-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,KAAK,EAEV,KAAK,IAAI,EAGT,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAC;AAI5B,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AAE7E,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,SAAS,CAAC;IAC1B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,KAAK,GAAG,SAAS,GACvB,aAAa,CAoBf;AAED,oEAAoE;AACpE,wBAAsB,MAAM,CAAC,CAAC,EAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9B,OAAO,CAAC,CAAC,CAAC,CAYZ"}
@@ -0,0 +1,33 @@
1
+ import { metrics, SpanStatusCode, trace, } from "@opentelemetry/api";
2
+ const SCOPE = "superblocks.workspace.sync";
3
+ export function createSyncTelemetry(tracer, meter) {
4
+ const resolvedMeter = meter ?? metrics.getMeter(SCOPE);
5
+ return {
6
+ tracer: tracer ?? trace.getTracer(SCOPE),
7
+ commitDuration: resolvedMeter.createHistogram("workspace.sync.commit.duration", {
8
+ description: "Turn-end commit latency, from reconcile to state write.",
9
+ unit: "s",
10
+ }),
11
+ rehydrateOutcome: resolvedMeter.createCounter("workspace.sync.rehydrate.outcome", { description: "Workspace rehydrate results by outcome." }),
12
+ unsynced: resolvedMeter.createGauge("workspace.sync.unsynced", {
13
+ description: "1 while the workspace has changes DBFS has not committed, else 0.",
14
+ }),
15
+ };
16
+ }
17
+ /** Runs `run` in an active span, recording a thrown error on it. */
18
+ export async function inSpan(tracer, name, run) {
19
+ return tracer.startActiveSpan(name, async (span) => {
20
+ try {
21
+ return await run(span);
22
+ }
23
+ catch (error) {
24
+ span.recordException(error);
25
+ span.setStatus({ code: SpanStatusCode.ERROR });
26
+ throw error;
27
+ }
28
+ finally {
29
+ span.end();
30
+ }
31
+ });
32
+ }
33
+ //# sourceMappingURL=sync-telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-telemetry.js","sourceRoot":"","sources":["../src/sync-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,OAAO,EAEP,cAAc,EACd,KAAK,GAEN,MAAM,oBAAoB,CAAC;AAE5B,MAAM,KAAK,GAAG,4BAA4B,CAAC;AAW3C,MAAM,UAAU,mBAAmB,CACjC,MAA0B,EAC1B,KAAwB;IAExB,MAAM,aAAa,GAAG,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACvD,OAAO;QACL,MAAM,EAAE,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC;QACxC,cAAc,EAAE,aAAa,CAAC,eAAe,CAC3C,gCAAgC,EAChC;YACE,WAAW,EAAE,yDAAyD;YACtE,IAAI,EAAE,GAAG;SACV,CACF;QACD,gBAAgB,EAAE,aAAa,CAAC,aAAa,CAC3C,kCAAkC,EAClC,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAC3D;QACD,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,yBAAyB,EAAE;YAC7D,WAAW,EACT,mEAAmE;SACtE,CAAC;KACH,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,MAAc,EACd,IAAY,EACZ,GAA+B;IAE/B,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACjD,IAAI,CAAC;YACH,OAAO,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,eAAe,CAAC,KAAc,CAAC,CAAC;YACrC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC;YAC/C,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Test doubles for `@superblocksteam/workspace-sync`, published as the
3
+ * `@superblocksteam/workspace-sync/testing` subpath. Not for production use.
4
+ */
5
+ import { type DirectoryEntry } from "@superblocksteam/shared";
6
+ import type { DbfsFileRef, DbfsTransport, SetHashRequest, SetHashResult } from "./dbfs-transport.js";
7
+ import type { PutManyItems } from "./snapshot/upload-snapshot.js";
8
+ /** A file in a {@link FakeDbfs.seed} description. */
9
+ export type FakeDbfsSeedEntry = string | Buffer | {
10
+ contents: string | Buffer;
11
+ executable?: boolean;
12
+ } | {
13
+ symlink: string;
14
+ };
15
+ export interface FakeDbfsCalls {
16
+ /** Item count of each validateMany call. */
17
+ validateMany: number[];
18
+ /** Items of each putMany call. */
19
+ putMany: PutManyItems[];
20
+ setHash: SetHashRequest[];
21
+ setDraftState: boolean[];
22
+ getDirectory: number;
23
+ getContents: number;
24
+ getBlob: number;
25
+ }
26
+ /**
27
+ * An in-memory, content-addressed DBFS plus one branch's hash pointer. Like
28
+ * the server, `putMany` refuses a listing whose children are missing and
29
+ * `validateMany` refuses excluded directory names, so an upload that skips
30
+ * something it should have sent fails here too.
31
+ */
32
+ export declare class FakeDbfs implements DbfsTransport {
33
+ readonly files: Map<string, Buffer<ArrayBufferLike>>;
34
+ readonly directories: Map<string, DirectoryEntry[]>;
35
+ serverHash: string | undefined;
36
+ readonly calls: FakeDbfsCalls;
37
+ private commits;
38
+ /** Total putMany items since the last {@link resetCalls}. */
39
+ get putItemCount(): number;
40
+ resetCalls(): void;
41
+ /**
42
+ * Stores a tree described by slash-separated paths and points the branch at
43
+ * it, as another writer would. Returns the tree's hash.
44
+ */
45
+ seed(tree: Record<string, FakeDbfsSeedEntry>): Promise<string>;
46
+ /** Stores a tree without moving the branch. Returns its hash. */
47
+ storeTree(tree: Record<string, FakeDbfsSeedEntry>): Promise<string>;
48
+ /** Every file and symlink in tree `hash`, by slash-separated path. */
49
+ readTree(hash?: string | undefined): Promise<Map<string, {
50
+ contents: Buffer;
51
+ executable: boolean;
52
+ } | {
53
+ symlink: string;
54
+ }>>;
55
+ validateMany(items: PutManyItems): Promise<void>;
56
+ putMany(items: PutManyItems): Promise<string[]>;
57
+ getDirectory(hash: string): Promise<DirectoryEntry[]>;
58
+ getContents(rootHash: string, files: DbfsFileRef[]): Promise<Buffer[]>;
59
+ getBlob(hash: string): Promise<Buffer>;
60
+ getServerHash(): Promise<string>;
61
+ setHash(request: SetHashRequest): Promise<SetHashResult>;
62
+ setDraftState(hasDraft: boolean): Promise<void>;
63
+ private directory;
64
+ private blob;
65
+ }
66
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,qDAAqD;AACrD,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,MAAM,GACN;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACnD;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExB,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kCAAkC;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,qBAAa,QAAS,YAAW,aAAa;IAC5C,QAAQ,CAAC,KAAK,uCAA6B;IAC3C,QAAQ,CAAC,WAAW,gCAAuC;IAC3D,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAQ3B;IACF,OAAO,CAAC,OAAO,CAAK;IAEpB,6DAA6D;IAC7D,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,UAAU,IAAI,IAAI,CAQjB;IAED;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAInE;IAED,iEAAiE;IAC3D,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAwCxE;IAED,sEAAsE;IAChE,QAAQ,CACZ,IAAI,qBAAkB,GACrB,OAAO,CACR,GAAG,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAC7E,CAqBA;IAEK,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAYrD;IAEK,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA0BpD;IAEK,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAG1D;IAEK,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAI3E;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG3C;IAEK,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAIrC;IAEK,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAK7D;IAEK,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpD;IAED,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,IAAI;CAKb"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Test doubles for `@superblocksteam/workspace-sync`, published as the
3
+ * `@superblocksteam/workspace-sync/testing` subpath. Not for production use.
4
+ */
5
+ import { codeModeDownloadExcludedDirectories, hashDirectoryContents, hashFileContents, } from "@superblocksteam/shared";
6
+ /**
7
+ * An in-memory, content-addressed DBFS plus one branch's hash pointer. Like
8
+ * the server, `putMany` refuses a listing whose children are missing and
9
+ * `validateMany` refuses excluded directory names, so an upload that skips
10
+ * something it should have sent fails here too.
11
+ */
12
+ export class FakeDbfs {
13
+ files = new Map();
14
+ directories = new Map();
15
+ serverHash;
16
+ calls = {
17
+ validateMany: [],
18
+ putMany: [],
19
+ setHash: [],
20
+ setDraftState: [],
21
+ getDirectory: 0,
22
+ getContents: 0,
23
+ getBlob: 0,
24
+ };
25
+ commits = 0;
26
+ /** Total putMany items since the last {@link resetCalls}. */
27
+ get putItemCount() {
28
+ return this.calls.putMany.reduce((total, items) => total + items.length, 0);
29
+ }
30
+ resetCalls() {
31
+ this.calls.validateMany.length = 0;
32
+ this.calls.putMany.length = 0;
33
+ this.calls.setHash.length = 0;
34
+ this.calls.setDraftState.length = 0;
35
+ this.calls.getDirectory = 0;
36
+ this.calls.getContents = 0;
37
+ this.calls.getBlob = 0;
38
+ }
39
+ /**
40
+ * Stores a tree described by slash-separated paths and points the branch at
41
+ * it, as another writer would. Returns the tree's hash.
42
+ */
43
+ async seed(tree) {
44
+ const hash = await this.storeTree(tree);
45
+ this.serverHash = hash;
46
+ return hash;
47
+ }
48
+ /** Stores a tree without moving the branch. Returns its hash. */
49
+ async storeTree(tree) {
50
+ const top = { files: new Map(), dirs: new Map() };
51
+ for (const [entryPath, entry] of Object.entries(tree)) {
52
+ const parts = entryPath.split("/");
53
+ let node = top;
54
+ for (const part of parts.slice(0, -1)) {
55
+ let next = node.dirs.get(part);
56
+ if (!next)
57
+ node.dirs.set(part, (next = { files: new Map(), dirs: new Map() }));
58
+ node = next;
59
+ }
60
+ node.files.set(parts.at(-1), entry);
61
+ }
62
+ const store = async (node) => {
63
+ const contents = [];
64
+ for (const [name, child] of node.dirs)
65
+ contents.push({ type: "d", name, hash: await store(child) });
66
+ for (const [name, entry] of node.files) {
67
+ if (typeof entry === "object" && "symlink" in entry) {
68
+ contents.push({ type: "l", name, target: entry.symlink });
69
+ continue;
70
+ }
71
+ const { contents: raw, executable = false } = typeof entry === "string" || Buffer.isBuffer(entry)
72
+ ? { contents: entry }
73
+ : entry;
74
+ const bytes = Buffer.from(raw);
75
+ const hash = await hashFileContents(bytes);
76
+ this.files.set(hash, bytes);
77
+ contents.push({ type: "-", name, hash, executable });
78
+ }
79
+ const hash = await hashDirectoryContents(contents);
80
+ this.directories.set(hash, contents);
81
+ return hash;
82
+ };
83
+ return store(top);
84
+ }
85
+ /** Every file and symlink in tree `hash`, by slash-separated path. */
86
+ async readTree(hash = this.serverHash) {
87
+ if (hash === undefined)
88
+ throw new Error("FakeDbfs has no server hash");
89
+ const result = new Map();
90
+ const walk = (directoryHash, prefix) => {
91
+ for (const entry of this.directory(directoryHash)) {
92
+ const entryPath = prefix + entry.name;
93
+ if (entry.type === "d")
94
+ walk(entry.hash, `${entryPath}/`);
95
+ else if (entry.type === "l")
96
+ result.set(entryPath, { symlink: entry.target });
97
+ else
98
+ result.set(entryPath, {
99
+ contents: this.blob(entry.hash),
100
+ executable: entry.executable,
101
+ });
102
+ }
103
+ };
104
+ walk(hash, "");
105
+ return result;
106
+ }
107
+ async validateMany(items) {
108
+ this.calls.validateMany.push(items.length);
109
+ for (const item of items) {
110
+ if (item.type !== "directoryContents")
111
+ continue;
112
+ const excluded = item.contents.find((entry) => codeModeDownloadExcludedDirectories.includes(entry.name));
113
+ if (excluded)
114
+ throw new Error(`Excluded application path "${excluded.name}" cannot be uploaded`);
115
+ }
116
+ }
117
+ async putMany(items) {
118
+ this.calls.putMany.push(items);
119
+ const hashes = [];
120
+ for (const item of items) {
121
+ if (item.type === "fileContents") {
122
+ const bytes = Buffer.from(item.contents, "base64");
123
+ const hash = await hashFileContents(bytes);
124
+ this.files.set(hash, bytes);
125
+ hashes.push(hash);
126
+ continue;
127
+ }
128
+ for (const entry of item.contents) {
129
+ if (entry.type === "-" && !this.files.has(entry.hash))
130
+ throw new Error(`putMany listing references missing file ${entry.name}`);
131
+ if (entry.type === "d" && !this.directories.has(entry.hash))
132
+ throw new Error(`putMany listing references missing directory ${entry.name}`);
133
+ }
134
+ const hash = await hashDirectoryContents(item.contents);
135
+ this.directories.set(hash, item.contents);
136
+ hashes.push(hash);
137
+ }
138
+ return hashes;
139
+ }
140
+ async getDirectory(hash) {
141
+ this.calls.getDirectory++;
142
+ return this.directory(hash);
143
+ }
144
+ async getContents(rootHash, files) {
145
+ this.calls.getContents++;
146
+ this.directory(rootHash);
147
+ return files.map((file) => this.blob(file.hash));
148
+ }
149
+ async getBlob(hash) {
150
+ this.calls.getBlob++;
151
+ return this.blob(hash);
152
+ }
153
+ async getServerHash() {
154
+ if (this.serverHash === undefined)
155
+ throw new Error("No live edit hash found");
156
+ return this.serverHash;
157
+ }
158
+ async setHash(request) {
159
+ this.directory(request.hash);
160
+ this.calls.setHash.push(request);
161
+ this.serverHash = request.hash;
162
+ return { hash: request.hash, commitId: `commit-${++this.commits}` };
163
+ }
164
+ async setDraftState(hasDraft) {
165
+ this.calls.setDraftState.push(hasDraft);
166
+ }
167
+ directory(hash) {
168
+ const contents = this.directories.get(hash);
169
+ if (!contents)
170
+ throw new Error(`DBFS directory ${hash} not found`);
171
+ return contents;
172
+ }
173
+ blob(hash) {
174
+ const bytes = this.files.get(hash);
175
+ if (!bytes)
176
+ throw new Error(`DBFS file ${hash} not found`);
177
+ return Buffer.from(bytes);
178
+ }
179
+ }
180
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /** What a workspace remembers across restarts. */
2
+ export interface WorkspaceState {
3
+ /** The last hash this workspace committed to, or loaded from, DBFS. */
4
+ dbfsHash: string;
5
+ }
6
+ /** A file in a local tree that differs from the server tree. */
7
+ export type OrphanedEntry = {
8
+ path: string;
9
+ type: "-";
10
+ hash: string;
11
+ executable: boolean;
12
+ } | {
13
+ path: string;
14
+ type: "l";
15
+ target: string;
16
+ };
17
+ /**
18
+ * Local changes a rehydrate could not commit because the server moved. File
19
+ * contents were put to DBFS first, so every hash here resolves.
20
+ */
21
+ export interface OrphanedDiff {
22
+ createdAt: string;
23
+ serverHash: string;
24
+ storedHash: string | undefined;
25
+ localHash: string;
26
+ /** Added or changed entries. */
27
+ changed: OrphanedEntry[];
28
+ /** Paths the server tree has and the local tree does not. */
29
+ deleted: string[];
30
+ }
31
+ export interface WorkspaceStateStore {
32
+ read(): Promise<WorkspaceState | undefined>;
33
+ /** Replaces the state atomically. */
34
+ write(state: WorkspaceState): Promise<void>;
35
+ /** Keeps an orphaned diff; returns where it was written. */
36
+ writeOrphan(diff: OrphanedDiff): Promise<string>;
37
+ }
38
+ /**
39
+ * Keeps `state.json` and `orphaned/` in a session state directory (on the VM,
40
+ * `/mnt/builder-sessions/workspace`). Writes go through a temporary file,
41
+ * fsync and rename, so a kill leaves the old or the new state.
42
+ */
43
+ export declare class FileWorkspaceStateStore implements WorkspaceStateStore {
44
+ readonly stateDir: string;
45
+ constructor(stateDir: string);
46
+ get file(): string;
47
+ get orphanedDir(): string;
48
+ read(): Promise<WorkspaceState | undefined>;
49
+ write(state: WorkspaceState): Promise<void>;
50
+ writeOrphan(diff: OrphanedDiff): Promise<string>;
51
+ }
52
+ //# sourceMappingURL=workspace-state-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-state-store.d.ts","sourceRoot":"","sources":["../src/workspace-state-store.ts"],"names":[],"mappings":"AAOA,kDAAkD;AAClD,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,gEAAgE;AAChE,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAC5C,qCAAqC;IACrC,KAAK,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,4DAA4D;IAC5D,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAClD;AAID;;;;GAIG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAArC,YAAqB,QAAQ,EAAE,MAAM,EAAI;IAEzC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAEK,IAAI,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAShD;IAEK,KAAK,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhD;IAEK,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAQrD;CACF"}
@@ -0,0 +1,45 @@
1
+ import * as fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ import { writeFileAtomic } from "./atomic-write.js";
5
+ const stateSchema = z.object({ dbfsHash: z.string().min(1) });
6
+ /**
7
+ * Keeps `state.json` and `orphaned/` in a session state directory (on the VM,
8
+ * `/mnt/builder-sessions/workspace`). Writes go through a temporary file,
9
+ * fsync and rename, so a kill leaves the old or the new state.
10
+ */
11
+ export class FileWorkspaceStateStore {
12
+ stateDir;
13
+ constructor(stateDir) {
14
+ this.stateDir = stateDir;
15
+ }
16
+ get file() {
17
+ return path.join(this.stateDir, "state.json");
18
+ }
19
+ get orphanedDir() {
20
+ return path.join(this.stateDir, "orphaned");
21
+ }
22
+ async read() {
23
+ let contents;
24
+ try {
25
+ contents = await fs.readFile(this.file, "utf8");
26
+ }
27
+ catch (error) {
28
+ if (error.code === "ENOENT")
29
+ return undefined;
30
+ throw error;
31
+ }
32
+ return stateSchema.parse(JSON.parse(contents));
33
+ }
34
+ async write(state) {
35
+ await fs.mkdir(this.stateDir, { recursive: true, mode: 0o700 });
36
+ await writeFileAtomic(this.file, JSON.stringify(stateSchema.parse(state)));
37
+ }
38
+ async writeOrphan(diff) {
39
+ await fs.mkdir(this.orphanedDir, { recursive: true, mode: 0o700 });
40
+ const file = path.join(this.orphanedDir, `${Date.parse(diff.createdAt)}.json`);
41
+ await writeFileAtomic(file, JSON.stringify(diff));
42
+ return file;
43
+ }
44
+ }
45
+ //# sourceMappingURL=workspace-state-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-state-store.js","sourceRoot":"","sources":["../src/workspace-state-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAoCpD,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAE9D;;;;GAIG;AACH,MAAM,OAAO,uBAAuB;IACb,QAAQ;IAA7B,YAAqB,QAAgB;wBAAhB,QAAQ;IAAW,CAAC;IAEzC,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAC;YACzE,MAAM,KAAK,CAAC;QACd,CAAC;QACD,OAAO,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAqB;QAC/B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAChE,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAkB;QAClC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CACpB,IAAI,CAAC,WAAW,EAChB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CACrC,CAAC;QACF,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "@superblocksteam/workspace-sync",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "2.0.178-next.1",
4
+ "description": "DBFS sync primitives for an app workspace: snapshot, upload, download and draft markers",
5
+ "license": "Superblocks Community Software License",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "type": "module",
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./*": {
18
+ "types": "./dist/*.d.ts",
19
+ "import": "./dist/*.js"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "@opentelemetry/api": "^1.9.1",
24
+ "@superblocksteam/shared": "0.9620.2",
25
+ "@superblocksteam/util": "2.0.178-next.1",
26
+ "diff": "^8.0.4",
27
+ "zod": "3.25.76"
28
+ },
29
+ "devDependencies": {
30
+ "@eslint/js": "^9.39.5",
31
+ "@types/node": "^24.19.2",
32
+ "@vitest/eslint-plugin": "^1.6.28",
33
+ "eslint": "^9.39.2",
34
+ "eslint-config-prettier": "^10.1.8",
35
+ "eslint-config-turbo": "^2.9.16",
36
+ "eslint-plugin-import": "^2.32.0",
37
+ "eslint-plugin-unicorn": "^62.0.0",
38
+ "eslint-plugin-unused-imports": "^4.3.0",
39
+ "globals": "^17.13.0",
40
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
41
+ "typescript-eslint": "^8.72.0",
42
+ "vitest": "^4.1.12"
43
+ },
44
+ "engines": {
45
+ "node": ">=24"
46
+ },
47
+ "publishConfig": {
48
+ "registry": "https://registry.npmjs.org"
49
+ },
50
+ "scripts": {
51
+ "build": "tsc --build tsconfig.build.json",
52
+ "check": "pnpm lint && pnpm typecheck",
53
+ "clean": "premove dist *.tsbuildinfo",
54
+ "dev": "tsc --build tsconfig.build.json --watch --preserveWatchOutput",
55
+ "lint": "eslint --concurrency=auto",
56
+ "lint:fix": "eslint --fix --concurrency=auto",
57
+ "publish-package": "pnpm publish",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
60
+ "typecheck": "tsc --build tsconfig.json"
61
+ }
6
62
  }