amalgm 0.1.238 → 0.1.240

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 (86) hide show
  1. package/lib/shared-realtime-tunnel.js +22 -14
  2. package/package.json +1 -1
  3. package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +68 -20
  4. package/runtime/scripts/amalgm-mcp/project-context/store.js +87 -19
  5. package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
  6. package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
  7. package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
  8. package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
  9. package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
  10. package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
  11. package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
  12. package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
  13. package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
  14. package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
  15. package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
  16. package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
  17. package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
  18. package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
  19. package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
  20. package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
  21. package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
  22. package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
  23. package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
  24. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
  25. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
  26. package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
  27. package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
  28. package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
  29. package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +81 -3
  30. package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +114 -8
  31. package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
  32. package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
  33. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
  34. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
  35. package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
  36. package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
  37. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
  38. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
  39. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
  40. package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
  41. package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
  42. package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
  43. package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
  44. package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
  45. package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
  46. package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
  47. package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
  48. package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
  49. package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
  50. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
  51. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
  52. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
  53. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +72 -0
  54. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
  55. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
  56. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
  57. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
  58. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
  59. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
  60. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
  61. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
  62. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
  63. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
  64. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
  65. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
  66. package/runtime/scripts/amalgm-mcp/workspace/objects.js +119 -54
  67. package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
  68. package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
  69. package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
  70. package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
  71. package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
  72. package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
  73. package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
  74. package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
  75. package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
  76. package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
  77. package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
  78. package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
  79. package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
  80. package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
  81. package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
  82. package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
  83. package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
  84. package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
  85. package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
  86. package/runtime/scripts/amalgm-mcp/workspace/wall.js +352 -497
@@ -8,12 +8,36 @@
8
8
  * is already backed by this WAL database.
9
9
  */
10
10
 
11
+ const fs = require('fs');
11
12
  const path = require('path');
12
13
  const { ensureDir } = require('../lib/storage');
13
14
  const { LOCAL_DB_FILE, STORAGE_DIR } = require('../config');
14
15
 
15
16
  let db = null;
16
17
 
18
+ const SHARED_RESOURCE_ATTACHMENTS_TABLE_SQL = `
19
+ CREATE TABLE IF NOT EXISTS shared_resource_attachments (
20
+ attachment_id TEXT PRIMARY KEY,
21
+ resource_id TEXT NOT NULL UNIQUE,
22
+ local_path TEXT NOT NULL UNIQUE,
23
+ attachment_state TEXT NOT NULL DEFAULT 'pending'
24
+ CHECK (attachment_state IN ('pending', 'syncing', 'attached', 'rejected')),
25
+ attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
26
+ next_attempt_at TEXT,
27
+ last_error TEXT,
28
+ -- First-attachment fence: local document mutations after this cursor
29
+ -- are overlay work that the returned cloud snapshot cannot contain.
30
+ journal_cursor INTEGER NOT NULL DEFAULT 0,
31
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
32
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
33
+ );
34
+ `;
35
+
36
+ const SHARED_RESOURCE_ATTACHMENTS_INDEX_SQL = `
37
+ CREATE INDEX IF NOT EXISTS shared_resource_attachments_outbox_idx
38
+ ON shared_resource_attachments (attachment_state, next_attempt_at, created_at);
39
+ `;
40
+
17
41
  // Prepared statements cached per database handle: preparing is a parse+plan
18
42
  // every time, and the realtime hot paths (event insert, replay reads, mirror
19
43
  // upserts, doc persistence) run the same fixed SQL for the process lifetime.
@@ -110,10 +134,12 @@ function migrate(database = openLocalDb()) {
110
134
  )),
111
135
  local_revision INTEGER,
112
136
  materialized_at TEXT,
137
+ official_projected_at TEXT,
113
138
  event_seq INTEGER,
114
139
  attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
115
140
  next_attempt_at TEXT,
116
141
  last_error TEXT,
142
+ failure_code TEXT,
117
143
  cloud_committed_at TEXT,
118
144
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
119
145
  updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
@@ -202,22 +228,6 @@ function migrate(database = openLocalDb()) {
202
228
  CREATE INDEX IF NOT EXISTS shared_resource_promotions_outbox_idx
203
229
  ON shared_resource_promotions (promotion_state, next_attempt_at, created_at);
204
230
 
205
- CREATE TABLE IF NOT EXISTS shared_resource_attachments (
206
- attachment_id TEXT PRIMARY KEY,
207
- resource_id TEXT NOT NULL UNIQUE,
208
- local_path TEXT NOT NULL UNIQUE,
209
- attachment_state TEXT NOT NULL DEFAULT 'pending'
210
- CHECK (attachment_state IN ('pending', 'syncing', 'attached', 'rejected')),
211
- attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
212
- next_attempt_at TEXT,
213
- last_error TEXT,
214
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
215
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
216
- );
217
-
218
- CREATE INDEX IF NOT EXISTS shared_resource_attachments_outbox_idx
219
- ON shared_resource_attachments (attachment_state, next_attempt_at, created_at);
220
-
221
231
  CREATE TABLE IF NOT EXISTS local_meta (
222
232
  key TEXT PRIMARY KEY,
223
233
  value TEXT,
@@ -557,6 +567,7 @@ function migrate(database = openLocalDb()) {
557
567
  device_id TEXT,
558
568
  inode TEXT,
559
569
  trashed_at TEXT,
570
+ trash_component_id TEXT,
560
571
  purged_at TEXT,
561
572
  missing_at TEXT,
562
573
  created_at TEXT NOT NULL,
@@ -566,7 +577,7 @@ function migrate(database = openLocalDb()) {
566
577
  );
567
578
 
568
579
  DROP INDEX IF EXISTS workspace_nodes_live_path_idx;
569
- CREATE UNIQUE INDEX workspace_nodes_live_path_idx
580
+ CREATE UNIQUE INDEX IF NOT EXISTS workspace_nodes_live_path_idx
570
581
  ON workspace_nodes(workspace_id, relative_path)
571
582
  WHERE purged_at IS NULL AND missing_at IS NULL AND trashed_at IS NULL;
572
583
 
@@ -577,6 +588,23 @@ function migrate(database = openLocalDb()) {
577
588
  ON workspace_nodes(workspace_id, device_id, inode)
578
589
  WHERE device_id IS NOT NULL AND inode IS NOT NULL AND purged_at IS NULL;
579
590
 
591
+ -- A safe filesystem ambiguity is still a sync failure if it is visible
592
+ -- only in one observer's return value. Keep every nonterminal tree wait
593
+ -- durable and addressable so restart and Local Live diagnostics see the
594
+ -- same held state until a later observation clears it.
595
+ CREATE TABLE IF NOT EXISTS workspace_tree_observation_holds (
596
+ workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON UPDATE CASCADE ON DELETE CASCADE,
597
+ hold_key TEXT NOT NULL,
598
+ owner_kind TEXT NOT NULL DEFAULT 'observer',
599
+ owner_id TEXT NOT NULL DEFAULT 'workspace-tree-observer',
600
+ reason TEXT NOT NULL,
601
+ node_id TEXT,
602
+ operation_kind TEXT,
603
+ paths_json TEXT NOT NULL,
604
+ first_observed_at TEXT NOT NULL,
605
+ PRIMARY KEY (workspace_id, hold_key)
606
+ );
607
+
580
608
  CREATE TABLE IF NOT EXISTS workspace_access_state (
581
609
  workspace_id TEXT PRIMARY KEY REFERENCES workspaces(id) ON UPDATE CASCADE ON DELETE CASCADE,
582
610
  authority_epoch INTEGER NOT NULL DEFAULT 1 CHECK (authority_epoch > 0),
@@ -694,15 +722,40 @@ function migrate(database = openLocalDb()) {
694
722
  ON browser_login_sessions(expires_at);
695
723
 
696
724
  `);
725
+ database.exec(SHARED_RESOURCE_ATTACHMENTS_TABLE_SQL);
726
+ database.exec(SHARED_RESOURCE_ATTACHMENTS_INDEX_SQL);
697
727
 
698
728
  // event_log is already present on every installed machine. These nullable
699
729
  // projection fields are additive and leave every legacy row/wire shape
700
730
  // untouched while new SDK mutations carry their stable identity.
701
731
  ensureColumn(database, 'event_log', 'mutation_id', 'TEXT');
702
732
  ensureColumn(database, 'event_log', 'shared_resource_id', 'TEXT');
733
+ ensureColumn(database, 'mutation_journal', 'failure_code', 'TEXT');
734
+ ensureColumn(database, 'mutation_journal', 'official_projected_at', 'TEXT');
735
+ ensureColumn(database, 'workspace_nodes', 'trash_component_id', 'TEXT');
736
+ ensureColumn(
737
+ database,
738
+ 'workspace_tree_observation_holds',
739
+ 'owner_kind',
740
+ "TEXT NOT NULL DEFAULT 'observer'",
741
+ );
742
+ ensureColumn(
743
+ database,
744
+ 'workspace_tree_observation_holds',
745
+ 'owner_id',
746
+ "TEXT NOT NULL DEFAULT 'workspace-tree-observer'",
747
+ );
748
+ backfillWorkspaceTrashComponents(database);
703
749
  // Wave-3 canaries created shared_resource_promotions before the fence
704
750
  // cursor existed.
705
751
  ensureColumn(database, 'shared_resource_promotions', 'journal_cursor', 'INTEGER NOT NULL DEFAULT 0');
752
+ ensureColumn(
753
+ database,
754
+ 'shared_resource_attachments',
755
+ 'journal_cursor',
756
+ 'INTEGER NOT NULL DEFAULT 0',
757
+ );
758
+ enforceAttachmentJournalFence(database);
706
759
  ensureColumn(
707
760
  database,
708
761
  'shared_resource_replicas',
@@ -719,6 +772,108 @@ function ensureColumn(database, tableName, columnName, declaration) {
719
772
  database.exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${declaration}`);
720
773
  }
721
774
 
775
+ function backfillWorkspaceTrashComponents(database) {
776
+ database.prepare(`
777
+ UPDATE workspace_nodes
778
+ SET trash_component_id = NULL
779
+ WHERE purged_at IS NOT NULL AND trash_component_id IS NOT NULL
780
+ `).run();
781
+ const workspaces = database.prepare(
782
+ 'SELECT id, path FROM workspaces WHERE path IS NOT NULL',
783
+ ).all();
784
+ const update = database.prepare(`
785
+ UPDATE workspace_nodes SET trash_component_id = ? WHERE id = ?
786
+ `);
787
+ database.transaction(() => {
788
+ for (const workspace of workspaces) {
789
+ const allRows = database.prepare(`
790
+ SELECT id, parent_id, trashed_at
791
+ FROM workspace_nodes
792
+ WHERE workspace_id = ? AND trashed_at IS NOT NULL AND purged_at IS NULL
793
+ `).all(workspace.id);
794
+ const nodes = new Map(allRows.map((row) => [row.id, row]));
795
+ const unstamped = database.prepare(`
796
+ SELECT id FROM workspace_nodes
797
+ WHERE workspace_id = ? AND trashed_at IS NOT NULL
798
+ AND purged_at IS NULL AND trash_component_id IS NULL
799
+ `).all(workspace.id);
800
+ for (const { id } of unstamped) {
801
+ const row = nodes.get(id);
802
+ let current = row;
803
+ let physicalRoot = null;
804
+ while (current) {
805
+ const candidate = path.join(
806
+ path.dirname(path.resolve(workspace.path)),
807
+ `.amalgm-tree-trash-${workspace.id}`,
808
+ current.id,
809
+ );
810
+ if (fs.existsSync(candidate)) {
811
+ physicalRoot = current.id;
812
+ break;
813
+ }
814
+ current = nodes.get(current.parent_id) || null;
815
+ }
816
+ // Missing physical evidence is not permission to infer ownership
817
+ // from a timestamp. Recovery may temporarily hold the carrier in
818
+ // private staging; rerun this proof after recovery or remount.
819
+ if (physicalRoot) update.run(physicalRoot, row.id);
820
+ }
821
+ }
822
+ })();
823
+ }
824
+
825
+ function enforceAttachmentJournalFence(database) {
826
+ const cursor = database.prepare('PRAGMA table_info(shared_resource_attachments)')
827
+ .all()
828
+ .find((column) => column.name === 'journal_cursor');
829
+ if (!cursor) {
830
+ throw new Error('shared attachment journal fence is missing');
831
+ }
832
+ database.prepare(`
833
+ UPDATE shared_resource_attachments
834
+ SET journal_cursor = 0
835
+ WHERE journal_cursor IS NULL
836
+ `).run();
837
+ if (cursor.notnull === 1 && String(cursor.dflt_value) === '0') return;
838
+
839
+ database.transaction(() => {
840
+ database.exec('DROP INDEX IF EXISTS shared_resource_attachments_outbox_idx');
841
+ database.exec(`
842
+ ALTER TABLE shared_resource_attachments
843
+ RENAME TO shared_resource_attachments_legacy_fence
844
+ `);
845
+ database.exec(SHARED_RESOURCE_ATTACHMENTS_TABLE_SQL);
846
+ database.exec(`
847
+ INSERT INTO shared_resource_attachments (
848
+ attachment_id,
849
+ resource_id,
850
+ local_path,
851
+ attachment_state,
852
+ attempt_count,
853
+ next_attempt_at,
854
+ last_error,
855
+ journal_cursor,
856
+ created_at,
857
+ updated_at
858
+ )
859
+ SELECT
860
+ attachment_id,
861
+ resource_id,
862
+ local_path,
863
+ attachment_state,
864
+ attempt_count,
865
+ next_attempt_at,
866
+ last_error,
867
+ COALESCE(journal_cursor, 0),
868
+ created_at,
869
+ updated_at
870
+ FROM shared_resource_attachments_legacy_fence
871
+ `);
872
+ database.exec('DROP TABLE shared_resource_attachments_legacy_fence');
873
+ database.exec(SHARED_RESOURCE_ATTACHMENTS_INDEX_SQL);
874
+ })();
875
+ }
876
+
722
877
  // repo-card identity moved from the path to minted replica ids
723
878
  // (repo-card@2). Cards are derived observations, so the v1 table is
724
879
  // dropped rather than migrated — the next refresh rebuilds every card
@@ -764,7 +919,47 @@ function ensureDocStatesSchema(database = openLocalDb()) {
764
919
  CREATE UNIQUE INDEX IF NOT EXISTS doc_states_node_id_idx
765
920
  ON doc_states(node_id)
766
921
  WHERE node_id IS NOT NULL;
922
+
923
+ CREATE TABLE IF NOT EXISTS doc_disk_materializations (
924
+ channel_id TEXT NOT NULL,
925
+ mutation_id TEXT NOT NULL,
926
+ local_resource TEXT NOT NULL,
927
+ root TEXT NOT NULL,
928
+ root_dev TEXT NOT NULL,
929
+ root_ino TEXT NOT NULL,
930
+ relative_path TEXT NOT NULL,
931
+ transaction_id TEXT NOT NULL,
932
+ operation_id TEXT NOT NULL,
933
+ expected_json TEXT,
934
+ observed_text TEXT,
935
+ mutation_source TEXT,
936
+ target_json TEXT,
937
+ target_content BLOB,
938
+ plan_kind TEXT NOT NULL DEFAULT 'mutation'
939
+ CHECK (plan_kind IN ('mutation', 'snapshot')),
940
+ snapshot_payload_json TEXT,
941
+ snapshot_event_seq INTEGER,
942
+ materialization_state TEXT NOT NULL DEFAULT 'based'
943
+ CHECK (materialization_state IN ('based', 'prepared', 'crossed', 'superseded')),
944
+ successor_channel_id TEXT,
945
+ successor_mutation_id TEXT,
946
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
947
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
948
+ PRIMARY KEY (channel_id, mutation_id)
949
+ );
950
+
951
+ CREATE INDEX IF NOT EXISTS doc_disk_materializations_resource_idx
952
+ ON doc_disk_materializations(local_resource, created_at);
767
953
  `);
954
+ ensureColumn(
955
+ database,
956
+ 'doc_disk_materializations',
957
+ 'plan_kind',
958
+ "TEXT NOT NULL DEFAULT 'mutation' CHECK (plan_kind IN ('mutation', 'snapshot'))",
959
+ );
960
+ ensureColumn(database, 'doc_disk_materializations', 'snapshot_payload_json', 'TEXT');
961
+ ensureColumn(database, 'doc_disk_materializations', 'snapshot_event_seq', 'INTEGER');
962
+ ensureColumn(database, 'doc_disk_materializations', 'mutation_source', 'TEXT');
768
963
  }
769
964
 
770
965
  // App-registered resources (state/app-resources.js): the engine-side mirror
@@ -795,9 +990,13 @@ function closeLocalDb() {
795
990
  }
796
991
 
797
992
  module.exports = {
993
+ backfillWorkspaceTrashComponents,
798
994
  cachedStatement,
799
995
  closeLocalDb,
800
996
  ensureAppResourcesSchema,
801
997
  ensureDocStatesSchema,
802
998
  openLocalDb,
999
+ _private: {
1000
+ migrate,
1001
+ },
803
1002
  };