@happyvertical/smrt-core 0.60.9 → 0.60.11
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.
- package/agents/browser-entry.md +10 -0
- package/agents/change-feed.md +33 -0
- package/agents/object-runtime.md +8 -0
- package/agents/schema-paths.md +6 -0
- package/dist/browser.d.ts +2 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +3 -2
- package/dist/cascade.d.ts +17 -3
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +30 -15
- package/dist/cascade.js.map +1 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +8 -4
- package/dist/collection.js.map +1 -1
- package/dist/embedded-write-queue.d.ts +18 -7
- package/dist/embedded-write-queue.d.ts.map +1 -1
- package/dist/embedded-write-queue.js +40 -10
- package/dist/embedded-write-queue.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +25 -7
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +39 -7
- package/dist/migrations/differ.d.ts +4 -0
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +25 -9
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/orchestrate.d.ts +1 -1
- package/dist/migrations/orchestrate.d.ts.map +1 -1
- package/dist/migrations/orchestrate.js +5 -14
- package/dist/migrations/orchestrate.js.map +1 -1
- package/dist/native-duckdb.d.ts.map +1 -1
- package/dist/native-duckdb.js +5 -1
- package/dist/native-duckdb.js.map +1 -1
- package/dist/object.d.ts +26 -0
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +215 -55
- package/dist/object.js.map +1 -1
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +8 -4
- package/dist/schema/generator.js.map +1 -1
- package/dist/scripts/browser-entry-evaluation.mjs +63 -8
- package/dist/scripts/browser-unbundled-dev.mjs +5 -2
- package/dist/scripts/fixtures/browser-pglite-cascade.ts +107 -0
- package/dist/scripts/fixtures/pglite-peer/package.json +9 -0
- package/dist/scripts/fixtures/pglite-peer/peer.js +1 -0
- package/dist/scripts/fixtures/pglite-peer/pnpm-lock.yaml +22 -0
- package/dist/scripts/fixtures/pglite-peer/pnpm-workspace.yaml +1 -0
- package/dist/smrt-knowledge.json +19 -11
- package/dist/smrt-knowledge.okf.md +181 -0
- package/dist/sync-device.d.ts +6 -0
- package/dist/sync-device.d.ts.map +1 -0
- package/dist/sync-device.js +18 -0
- package/dist/sync-device.js.map +1 -0
- package/dist/sync-tombstones.d.ts +30 -0
- package/dist/sync-tombstones.d.ts.map +1 -0
- package/dist/sync-tombstones.js +112 -0
- package/dist/sync-tombstones.js.map +1 -0
- package/dist/system/schema.d.ts +5 -1
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +21 -4
- package/dist/system/schema.js.map +1 -1
- 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 {
|
|
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 = {
|
|
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
|
-
|
|
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: {
|
|
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(
|
|
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
|
|
111
|
-
|
|
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
|
-
|
|
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 @@
|
|
|
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: []
|