@happyvertical/smrt-core 0.60.9 → 0.60.10

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 (67) hide show
  1. package/agents/browser-entry.md +10 -0
  2. package/agents/change-feed.md +33 -0
  3. package/agents/object-runtime.md +8 -0
  4. package/agents/schema-paths.md +6 -0
  5. package/dist/browser.d.ts +2 -1
  6. package/dist/browser.d.ts.map +1 -1
  7. package/dist/browser.js +3 -2
  8. package/dist/cascade.d.ts +17 -3
  9. package/dist/cascade.d.ts.map +1 -1
  10. package/dist/cascade.js +30 -15
  11. package/dist/cascade.js.map +1 -1
  12. package/dist/collection.d.ts.map +1 -1
  13. package/dist/collection.js +8 -4
  14. package/dist/collection.js.map +1 -1
  15. package/dist/embedded-write-queue.d.ts +18 -7
  16. package/dist/embedded-write-queue.d.ts.map +1 -1
  17. package/dist/embedded-write-queue.js +40 -10
  18. package/dist/embedded-write-queue.js.map +1 -1
  19. package/dist/index.d.ts +2 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +3 -2
  22. package/dist/manifest/static-manifest.d.ts.map +1 -1
  23. package/dist/manifest/static-manifest.js +25 -7
  24. package/dist/manifest/static-manifest.js.map +1 -1
  25. package/dist/manifest/store.js +1 -1
  26. package/dist/manifest/store.js.map +1 -1
  27. package/dist/manifest.json +39 -7
  28. package/dist/migrations/differ.d.ts +4 -0
  29. package/dist/migrations/differ.d.ts.map +1 -1
  30. package/dist/migrations/differ.js +25 -9
  31. package/dist/migrations/differ.js.map +1 -1
  32. package/dist/migrations/orchestrate.d.ts +1 -1
  33. package/dist/migrations/orchestrate.d.ts.map +1 -1
  34. package/dist/migrations/orchestrate.js +5 -14
  35. package/dist/migrations/orchestrate.js.map +1 -1
  36. package/dist/native-duckdb.d.ts.map +1 -1
  37. package/dist/native-duckdb.js +5 -1
  38. package/dist/native-duckdb.js.map +1 -1
  39. package/dist/object.d.ts +26 -0
  40. package/dist/object.d.ts.map +1 -1
  41. package/dist/object.js +215 -55
  42. package/dist/object.js.map +1 -1
  43. package/dist/schema/generator.d.ts.map +1 -1
  44. package/dist/schema/generator.js +8 -4
  45. package/dist/schema/generator.js.map +1 -1
  46. package/dist/scripts/browser-entry-evaluation.mjs +63 -8
  47. package/dist/scripts/browser-unbundled-dev.mjs +5 -2
  48. package/dist/scripts/fixtures/browser-pglite-cascade.ts +107 -0
  49. package/dist/scripts/fixtures/pglite-peer/package.json +9 -0
  50. package/dist/scripts/fixtures/pglite-peer/peer.js +1 -0
  51. package/dist/scripts/fixtures/pglite-peer/pnpm-lock.yaml +22 -0
  52. package/dist/scripts/fixtures/pglite-peer/pnpm-workspace.yaml +1 -0
  53. package/dist/smrt-knowledge.json +19 -11
  54. package/dist/smrt-knowledge.okf.md +181 -0
  55. package/dist/sync-device.d.ts +6 -0
  56. package/dist/sync-device.d.ts.map +1 -0
  57. package/dist/sync-device.js +18 -0
  58. package/dist/sync-device.js.map +1 -0
  59. package/dist/sync-tombstones.d.ts +30 -0
  60. package/dist/sync-tombstones.d.ts.map +1 -0
  61. package/dist/sync-tombstones.js +112 -0
  62. package/dist/sync-tombstones.js.map +1 -0
  63. package/dist/system/schema.d.ts +5 -1
  64. package/dist/system/schema.d.ts.map +1 -1
  65. package/dist/system/schema.js +21 -4
  66. package/dist/system/schema.js.map +1 -1
  67. package/package.json +11 -11
@@ -11,18 +11,28 @@
11
11
  * Set PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH to use a Chromium other than
12
12
  * Playwright's own install.
13
13
  */
14
+ import { execFile } from 'node:child_process';
14
15
  import { existsSync } from 'node:fs';
15
- import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
16
+ import {
17
+ copyFile,
18
+ mkdir,
19
+ mkdtemp,
20
+ readFile,
21
+ rm,
22
+ writeFile,
23
+ } from 'node:fs/promises';
16
24
  import { createServer } from 'node:http';
17
25
  import { tmpdir } from 'node:os';
18
26
  import { dirname, extname, resolve } from 'node:path';
19
- import { fileURLToPath } from 'node:url';
27
+ import { fileURLToPath, pathToFileURL } from 'node:url';
28
+ import { promisify } from 'node:util';
20
29
  import { chromium } from 'playwright-core';
21
30
  import { build, createLogger } from 'vite';
22
31
 
23
32
  const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
24
33
  const browserEntry = resolve(packageDir, 'dist/browser.js');
25
34
  const browserHost = resolve(packageDir, 'dist/host.browser.js');
35
+ const cascadeProbe = process.argv.includes('--pglite-cascade');
26
36
  if (!existsSync(browserEntry)) {
27
37
  throw new Error(`${browserEntry} is missing; run \`pnpm build\` first`);
28
38
  }
@@ -30,9 +40,38 @@ if (!existsSync(browserEntry)) {
30
40
  const workDir = await mkdtemp(resolve(tmpdir(), 'smrt-core-browser-entry-'));
31
41
  const outputDir = resolve(workDir, 'dist');
32
42
  const entry = resolve(workDir, 'entry.ts');
33
- const contentTypes = { '.js': 'text/javascript', '.map': 'application/json' };
43
+ const contentTypes = {
44
+ '.js': 'text/javascript',
45
+ '.map': 'application/json',
46
+ '.wasm': 'application/wasm',
47
+ };
48
+
49
+ async function provisionPglite() {
50
+ const source = resolve(packageDir, 'scripts/fixtures/pglite-peer');
51
+ const scratch = resolve(workDir, 'pglite-peer');
52
+ await mkdir(scratch);
53
+ await Promise.all(
54
+ ['package.json', 'peer.js', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'].map(
55
+ (file) => copyFile(resolve(source, file), resolve(scratch, file)),
56
+ ),
57
+ );
58
+ await promisify(execFile)('pnpm', [
59
+ '--dir',
60
+ scratch,
61
+ 'install',
62
+ '--frozen-lockfile',
63
+ ]);
64
+
65
+ // Serve the peer's published ESM and WASM. Its Emscripten loader guards
66
+ // Node imports at runtime, but Vite warns about them when bundling the peer.
67
+ const { pgliteEntry } = await import(
68
+ pathToFileURL(resolve(scratch, 'peer.js')).href
69
+ );
70
+ return dirname(fileURLToPath(pgliteEntry));
71
+ }
34
72
 
35
73
  try {
74
+ const pgliteDir = cascadeProbe ? await provisionPglite() : undefined;
36
75
  await writeFile(
37
76
  entry,
38
77
  `import * as core from ${JSON.stringify(browserEntry)};
@@ -63,7 +102,12 @@ const widget = new BrowserWidget({ name: 'probe', size: 3 });
63
102
  if (widget.name !== 'probe' || widget.size !== 3) throw new Error('widget fields were not assigned');
64
103
  const registered = ObjectRegistry.getClass('BrowserWidget');
65
104
  if (!registered) throw new Error('decorated class did not register');
66
- document.body.textContent = 'browser entry evaluated: ' + Object.keys(core).length + ' exports';
105
+ ${cascadeProbe ? `
106
+ import { runPgliteCascade } from ${JSON.stringify(resolve(packageDir, 'scripts/fixtures/browser-pglite-cascade.ts'))};
107
+ await runPgliteCascade('memory://');
108
+ await runPgliteCascade('idb://smrt-cascade-dialect');
109
+ document.body.textContent = 'browser entry evaluated: PGlite SET NULL cascade (memory:// and idb://)';
110
+ ` : "document.body.textContent = 'browser entry evaluated: ' + Object.keys(core).length + ' exports';"}
67
111
  `,
68
112
  );
69
113
 
@@ -89,7 +133,11 @@ document.body.textContent = 'browser entry evaluated: ' + Object.keys(core).leng
89
133
  build: {
90
134
  outDir: outputDir,
91
135
  minify: false,
92
- rollupOptions: { input: entry, output: { entryFileNames: 'entry.js' } },
136
+ rollupOptions: {
137
+ input: entry,
138
+ ...(cascadeProbe ? { external: ['@electric-sql/pglite'] } : {}),
139
+ output: { entryFileNames: 'entry.js' },
140
+ },
93
141
  target: 'es2022',
94
142
  },
95
143
  });
@@ -103,12 +151,19 @@ document.body.textContent = 'browser entry evaluated: ' + Object.keys(core).leng
103
151
  const pathname = new URL(request.url ?? '/', 'http://localhost').pathname;
104
152
  if (pathname === '/') {
105
153
  response.writeHead(200, { 'content-type': 'text/html' });
106
- response.end('<script type="module" src="/entry.js"></script>');
154
+ response.end(
155
+ (cascadeProbe
156
+ ? '<script type="importmap">{"imports":{"@electric-sql/pglite":"/pglite/index.js"}}</script>'
157
+ : '') + '<script type="module" src="/entry.js"></script>',
158
+ );
107
159
  return;
108
160
  }
109
161
  if (pathname === '/favicon.ico') return response.writeHead(204).end();
110
- const file = resolve(outputDir, `.${pathname}`);
111
- if (!file.startsWith(`${outputDir}/`)) return response.writeHead(403).end();
162
+ const servingPglite = pgliteDir && pathname.startsWith('/pglite/');
163
+ const assetDir = servingPglite ? pgliteDir : outputDir;
164
+ const assetPath = servingPglite ? pathname.slice('/pglite'.length) : pathname;
165
+ const file = resolve(assetDir, `.${assetPath}`);
166
+ if (!file.startsWith(`${assetDir}/`)) return response.writeHead(403).end();
112
167
  try {
113
168
  const content = await readFile(file);
114
169
  response.writeHead(200, {
@@ -15,7 +15,7 @@
15
15
  * Playwright's own install.
16
16
  */
17
17
  import { existsSync } from 'node:fs';
18
- import { mkdtemp, rm, writeFile } from 'node:fs/promises';
18
+ import { mkdtemp, realpath, rm, writeFile } from 'node:fs/promises';
19
19
  import { tmpdir } from 'node:os';
20
20
  import { dirname, resolve } from 'node:path';
21
21
  import { fileURLToPath } from 'node:url';
@@ -28,7 +28,10 @@ if (!existsSync(namingModule)) {
28
28
  throw new Error(`${namingModule} is missing; run \`pnpm build\` first`);
29
29
  }
30
30
 
31
- const workDir = await mkdtemp(resolve(tmpdir(), 'smrt-core-unbundled-dev-'));
31
+ // Vite keys inline HTML modules by their canonical path.
32
+ const workDir = await mkdtemp(
33
+ resolve(await realpath(tmpdir()), 'smrt-core-unbundled-dev-'),
34
+ );
32
35
  let server;
33
36
  let browser;
34
37
  try {
@@ -0,0 +1,107 @@
1
+ import * as core from '../../dist/browser.js';
2
+ import { usesPostgresDialect } from '../../dist/embedded-write-queue.js';
3
+ import { getDatabase } from '../../dist/host.browser.js';
4
+
5
+ const { SmrtObject, smrt } = core;
6
+
7
+ // Exercise the complete model delete through the SDK's PGlite transaction.
8
+ @smrt()
9
+ class BrowserCascadeParent extends SmrtObject {}
10
+
11
+ @smrt()
12
+ class BrowserCascadeChild extends SmrtObject {
13
+ @core.foreignKey(BrowserCascadeParent, { onDelete: 'SET NULL' })
14
+ parentId;
15
+ }
16
+
17
+ export async function runPgliteCascade(dataDir: string): Promise<void> {
18
+ const db = await getDatabase({ type: 'pglite', dataDir });
19
+ try {
20
+ const assertEmbeddedPostgres = (handle: typeof db) => {
21
+ if (
22
+ handle.url !== dataDir ||
23
+ !usesPostgresDialect(handle) ||
24
+ !core.isEmbeddedDatabase(handle) ||
25
+ core.isPostgresDatabase(handle)
26
+ ) {
27
+ throw new Error(
28
+ 'PGlite handle must use PostgreSQL syntax as an embedded database',
29
+ );
30
+ }
31
+ };
32
+ assertEmbeddedPostgres(db);
33
+ await db.transaction(async (tx) => assertEmbeddedPostgres(tx));
34
+ await core.ensureSystemTables(db, 'postgres');
35
+ const parent = new BrowserCascadeParent({ db, id: crypto.randomUUID() });
36
+ const child = new BrowserCascadeChild({ db });
37
+ const localDeviceId = crypto.randomUUID();
38
+ const trackedId = crypto.randomUUID();
39
+ const untrackedId = crypto.randomUUID();
40
+ const before = new Date(Date.now() + 86_400_000).toISOString();
41
+ core.setDatabaseSyncDeviceId(db, localDeviceId);
42
+ await db.query(`
43
+ CREATE TABLE ${parent.tableName} (
44
+ id UUID PRIMARY KEY, slug TEXT NOT NULL, context TEXT NOT NULL DEFAULT '',
45
+ created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL,
46
+ _sync_origin_device_id TEXT, _sync_revision BIGINT
47
+ );
48
+ CREATE TABLE ${child.tableName} (
49
+ id UUID PRIMARY KEY, updated_at TIMESTAMP NOT NULL,
50
+ _sync_origin_device_id TEXT, _sync_revision BIGINT,
51
+ parent_id UUID REFERENCES ${parent.tableName}(id) ON DELETE SET NULL
52
+ );
53
+ `);
54
+ await db.query(
55
+ `INSERT INTO ${parent.tableName} (id, slug, created_at, updated_at)
56
+ VALUES ($1, 'parent', $2, $2)`,
57
+ parent.id,
58
+ before,
59
+ );
60
+ await db.query(
61
+ `INSERT INTO ${child.tableName}
62
+ (id, parent_id, updated_at, _sync_origin_device_id, _sync_revision)
63
+ VALUES ($1, $2, $3, 'remote', 21), ($4, $2, $3, NULL, NULL)`,
64
+ trackedId,
65
+ parent.id,
66
+ before,
67
+ untrackedId,
68
+ );
69
+
70
+ await parent.initialize();
71
+ await parent.delete();
72
+
73
+ const { rows } = await db.query(
74
+ `SELECT *, updated_at = $3::timestamp AS unchanged,
75
+ updated_at = $3::timestamp + INTERVAL '1 millisecond' AS advanced
76
+ FROM ${child.tableName} WHERE id IN ($1, $2)`,
77
+ trackedId,
78
+ untrackedId,
79
+ before,
80
+ );
81
+ const tracked = rows.find((row) => row.id === trackedId);
82
+ const untracked = rows.find((row) => row.id === untrackedId);
83
+ if (await db.get(parent.tableName, { id: parent.id })) {
84
+ throw new Error('PGlite cascade left the parent row');
85
+ }
86
+ if (
87
+ tracked?.parent_id !== null ||
88
+ tracked._sync_origin_device_id !== localDeviceId ||
89
+ Number(tracked._sync_revision) !== 22 ||
90
+ tracked.advanced !== true
91
+ ) {
92
+ throw new Error(
93
+ 'PGlite SET NULL did not advance the tracked row revision',
94
+ );
95
+ }
96
+ if (
97
+ untracked?.parent_id !== null ||
98
+ untracked._sync_origin_device_id !== null ||
99
+ untracked._sync_revision !== null ||
100
+ untracked.unchanged !== true
101
+ ) {
102
+ throw new Error('PGlite SET NULL changed the untracked row revision');
103
+ }
104
+ } finally {
105
+ await db.close?.();
106
+ }
107
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "smrt-pglite-browser-peer",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "dependencies": {
7
+ "@electric-sql/pglite": "0.5.15"
8
+ }
9
+ }
@@ -0,0 +1 @@
1
+ export const pgliteEntry = import.meta.resolve('@electric-sql/pglite');
@@ -0,0 +1,22 @@
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+
9
+ .:
10
+ dependencies:
11
+ '@electric-sql/pglite':
12
+ specifier: 0.5.15
13
+ version: 0.5.15
14
+
15
+ packages:
16
+
17
+ '@electric-sql/pglite@0.5.15':
18
+ resolution: {integrity: sha512-ERZWgaRTxueGV4cvL1tfpAOzFCWTXoUtbCBqnf2FhHDVs/so9eAKaanvrwWF9Dntx7BYsmFQujVQ0/B4S8Mmiw==}
19
+
20
+ snapshots:
21
+
22
+ '@electric-sql/pglite@0.5.15': {}
@@ -0,0 +1 @@
1
+ packages: []