@ontrails/core 1.0.0-beta.14 → 1.0.0-beta.15
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +11 -0
- package/README.md +17 -13
- package/dist/branded.d.ts +1 -1
- package/dist/branded.d.ts.map +1 -1
- package/dist/branded.js +1 -1
- package/dist/branded.js.map +1 -1
- package/dist/context.d.ts +13 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +19 -3
- package/dist/context.js.map +1 -1
- package/dist/contour.d.ts +81 -0
- package/dist/contour.d.ts.map +1 -0
- package/dist/contour.js +176 -0
- package/dist/contour.js.map +1 -0
- package/dist/cross-schema.d.ts +18 -0
- package/dist/cross-schema.d.ts.map +1 -0
- package/dist/cross-schema.js +29 -0
- package/dist/cross-schema.js.map +1 -0
- package/dist/draft.d.ts +5 -5
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +28 -27
- package/dist/draft.js.map +1 -1
- package/dist/errors.d.ts +62 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +44 -1
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +26 -9
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +441 -20
- package/dist/execute.js.map +1 -1
- package/dist/fire.d.ts +43 -0
- package/dist/fire.d.ts.map +1 -0
- package/dist/fire.js +185 -0
- package/dist/fire.js.map +1 -0
- package/dist/index.d.ts +31 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -13
- package/dist/index.js.map +1 -1
- package/dist/internal/cross-batch.d.ts +44 -0
- package/dist/internal/cross-batch.d.ts.map +1 -0
- package/dist/internal/cross-batch.js +58 -0
- package/dist/internal/cross-batch.js.map +1 -0
- package/dist/internal/fork-ctx.d.ts +41 -0
- package/dist/internal/fork-ctx.d.ts.map +1 -0
- package/dist/internal/fork-ctx.js +47 -0
- package/dist/internal/fork-ctx.js.map +1 -0
- package/dist/internal/signal-ref.d.ts +15 -0
- package/dist/internal/signal-ref.d.ts.map +1 -0
- package/dist/internal/signal-ref.js +41 -0
- package/dist/internal/signal-ref.js.map +1 -0
- package/dist/internal/topo-saves.d.ts +8 -2
- package/dist/internal/topo-saves.d.ts.map +1 -1
- package/dist/internal/topo-saves.js +162 -23
- package/dist/internal/topo-saves.js.map +1 -1
- package/dist/internal/topo-snapshots.d.ts +57 -0
- package/dist/internal/topo-snapshots.d.ts.map +1 -0
- package/dist/internal/topo-snapshots.js +372 -0
- package/dist/internal/topo-snapshots.js.map +1 -0
- package/dist/internal/topo-store-read.d.ts +23 -20
- package/dist/internal/topo-store-read.d.ts.map +1 -1
- package/dist/internal/topo-store-read.js +82 -83
- package/dist/internal/topo-store-read.js.map +1 -1
- package/dist/internal/topo-store.d.ts +19 -5
- package/dist/internal/topo-store.d.ts.map +1 -1
- package/dist/internal/topo-store.js +174 -124
- package/dist/internal/topo-store.js.map +1 -1
- package/dist/internal/tracing.d.ts +98 -0
- package/dist/internal/tracing.d.ts.map +1 -0
- package/dist/internal/tracing.js +102 -0
- package/dist/internal/tracing.js.map +1 -0
- package/dist/internal/trails-db.d.ts +2 -2
- package/dist/internal/trails-db.d.ts.map +1 -1
- package/dist/internal/trails-db.js +8 -8
- package/dist/internal/trails-db.js.map +1 -1
- package/dist/internal/zod-wrappers.d.ts +40 -0
- package/dist/internal/zod-wrappers.d.ts.map +1 -0
- package/dist/internal/zod-wrappers.js +59 -0
- package/dist/internal/zod-wrappers.js.map +1 -0
- package/dist/layer.d.ts +22 -5
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +20 -3
- package/dist/layer.js.map +1 -1
- package/dist/path-security.d.ts +1 -1
- package/dist/path-security.d.ts.map +1 -1
- package/dist/path-security.js +1 -1
- package/dist/path-security.js.map +1 -1
- package/dist/resilience.d.ts +1 -1
- package/dist/resilience.d.ts.map +1 -1
- package/dist/resilience.js +3 -3
- package/dist/resilience.js.map +1 -1
- package/dist/resource-config.d.ts +22 -0
- package/dist/resource-config.d.ts.map +1 -0
- package/dist/resource-config.js +210 -0
- package/dist/resource-config.js.map +1 -0
- package/dist/resource.d.ts +74 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/resource.js +61 -0
- package/dist/resource.js.map +1 -0
- package/dist/run.d.ts +4 -3
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +5 -4
- package/dist/run.js.map +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +2 -1
- package/dist/serialization.js.map +1 -1
- package/dist/store/accessor-protocol.d.ts +51 -0
- package/dist/store/accessor-protocol.d.ts.map +1 -0
- package/dist/store/accessor-protocol.js +2 -0
- package/dist/store/accessor-protocol.js.map +1 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +2 -0
- package/dist/store/index.js.map +1 -0
- package/dist/surface-filter.d.ts +13 -0
- package/dist/surface-filter.d.ts.map +1 -0
- package/dist/surface-filter.js +76 -0
- package/dist/surface-filter.js.map +1 -0
- package/dist/topo-store.d.ts +36 -22
- package/dist/topo-store.d.ts.map +1 -1
- package/dist/topo-store.js +215 -71
- package/dist/topo-store.js.map +1 -1
- package/dist/topo.d.ts +23 -9
- package/dist/topo.d.ts.map +1 -1
- package/dist/topo.js +175 -46
- package/dist/topo.js.map +1 -1
- package/dist/trail.d.ts +86 -19
- package/dist/trail.d.ts.map +1 -1
- package/dist/trail.js +59 -4
- package/dist/trail.js.map +1 -1
- package/dist/trails/derive-trail.d.ts +66 -0
- package/dist/trails/derive-trail.d.ts.map +1 -0
- package/dist/trails/derive-trail.js +378 -0
- package/dist/trails/derive-trail.js.map +1 -0
- package/dist/trails/index.d.ts +5 -0
- package/dist/trails/index.d.ts.map +1 -0
- package/dist/trails/index.js +3 -0
- package/dist/trails/index.js.map +1 -0
- package/dist/trails/ingest.d.ts +24 -0
- package/dist/trails/ingest.d.ts.map +1 -0
- package/dist/trails/ingest.js +63 -0
- package/dist/trails/ingest.js.map +1 -0
- package/dist/transport-error-map.d.ts +106 -0
- package/dist/transport-error-map.d.ts.map +1 -0
- package/dist/transport-error-map.js +24 -0
- package/dist/transport-error-map.js.map +1 -0
- package/dist/type-checks.test-d.d.ts +71 -0
- package/dist/type-checks.test-d.d.ts.map +1 -0
- package/dist/type-checks.test-d.js +12 -0
- package/dist/type-checks.test-d.js.map +1 -0
- package/dist/type-utils.d.ts +11 -2
- package/dist/type-utils.d.ts.map +1 -1
- package/dist/type-utils.js.map +1 -1
- package/dist/types.d.ts +113 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate-established-topo.js +2 -2
- package/dist/validate-established-topo.js.map +1 -1
- package/dist/validate-topo.d.ts.map +1 -1
- package/dist/validate-topo.js +24 -7
- package/dist/validate-topo.js.map +1 -1
- package/dist/workspace.d.ts +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +1 -1
- package/dist/workspace.js.map +1 -1
- package/package.json +8 -2
- package/src/__tests__/branded.test.ts +5 -5
- package/src/__tests__/context.test.ts +3 -3
- package/src/__tests__/contour.test.ts +263 -0
- package/src/__tests__/derive-trail.test.ts +668 -0
- package/src/__tests__/errors.test.ts +118 -0
- package/src/__tests__/execute.test.ts +1179 -141
- package/src/__tests__/fire.test.ts +1056 -0
- package/src/__tests__/fork-ctx.test.ts +83 -0
- package/src/__tests__/ingest.test.ts +191 -0
- package/src/__tests__/{gate.test.ts → layer.test.ts} +21 -21
- package/src/__tests__/path-security.test.ts +7 -7
- package/src/__tests__/resilience.test.ts +8 -8
- package/src/__tests__/resource.test.ts +204 -0
- package/src/__tests__/run.test.ts +13 -13
- package/src/__tests__/service-config.test.ts +28 -32
- package/src/__tests__/surface-filter.test.ts +196 -0
- package/src/__tests__/topo-store-read.test.ts +41 -36
- package/src/__tests__/topo-store.test.ts +493 -93
- package/src/__tests__/topo.test.ts +350 -54
- package/src/__tests__/trail.test.ts +189 -24
- package/src/__tests__/trails-db.test.ts +32 -25
- package/src/__tests__/transport-error-map.test.ts +82 -0
- package/src/__tests__/type-utils.test.ts +29 -1
- package/src/__tests__/validate-topo.test.ts +147 -25
- package/src/__tests__/workspace.test.ts +6 -6
- package/src/__tests__/zod-wrappers.test.ts +62 -0
- package/src/branded.ts +1 -1
- package/src/context.ts +24 -4
- package/src/contour.ts +344 -0
- package/src/cross-schema.ts +36 -0
- package/src/draft.ts +56 -52
- package/src/errors.ts +62 -18
- package/src/execute.ts +840 -29
- package/src/fire.ts +295 -0
- package/src/index.ts +116 -29
- package/src/internal/cross-batch.ts +77 -0
- package/src/internal/fork-ctx.ts +69 -0
- package/src/internal/signal-ref.ts +87 -0
- package/src/internal/topo-snapshots.ts +517 -0
- package/src/internal/topo-store-read.ts +140 -132
- package/src/internal/topo-store.ts +291 -186
- package/src/internal/tracing.ts +187 -0
- package/src/internal/trails-db.ts +9 -11
- package/src/internal/zod-wrappers.ts +78 -0
- package/src/layer.ts +50 -0
- package/src/path-security.ts +1 -1
- package/src/resilience.ts +3 -3
- package/src/{provision-config.ts → resource-config.ts} +109 -123
- package/src/resource.ts +155 -0
- package/src/run.ts +5 -4
- package/src/serialization.ts +2 -0
- package/src/store/accessor-protocol.ts +56 -0
- package/src/store/index.ts +4 -0
- package/src/surface-filter.ts +182 -0
- package/src/topo-store.ts +313 -104
- package/src/topo.ts +328 -57
- package/src/trail.ts +199 -31
- package/src/trails/derive-trail.ts +842 -0
- package/src/trails/index.ts +9 -0
- package/src/trails/ingest.ts +146 -0
- package/src/transport-error-map.ts +56 -0
- package/src/type-checks.test-d.ts +106 -0
- package/src/type-utils.ts +17 -2
- package/src/types.ts +148 -11
- package/src/validate-established-topo.ts +2 -2
- package/src/validate-topo.ts +32 -8
- package/src/workspace.ts +1 -1
- package/tsconfig.tests.json +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/adapters.d.ts +0 -39
- package/dist/adapters.d.ts.map +0 -1
- package/dist/adapters.js +0 -2
- package/dist/adapters.js.map +0 -1
- package/dist/dispatch.d.ts +0 -27
- package/dist/dispatch.d.ts.map +0 -1
- package/dist/dispatch.js +0 -34
- package/dist/dispatch.js.map +0 -1
- package/dist/event.d.ts +0 -8
- package/dist/event.d.ts.map +0 -1
- package/dist/event.js +0 -7
- package/dist/event.js.map +0 -1
- package/dist/gate.d.ts +0 -17
- package/dist/gate.d.ts.map +0 -1
- package/dist/gate.js +0 -21
- package/dist/gate.js.map +0 -1
- package/dist/health.d.ts +0 -18
- package/dist/health.d.ts.map +0 -1
- package/dist/health.js +0 -5
- package/dist/health.js.map +0 -1
- package/dist/job.d.ts +0 -24
- package/dist/job.d.ts.map +0 -1
- package/dist/job.js +0 -17
- package/dist/job.js.map +0 -1
- package/dist/provision-config.d.ts +0 -22
- package/dist/provision-config.d.ts.map +0 -1
- package/dist/provision-config.js +0 -210
- package/dist/provision-config.js.map +0 -1
- package/dist/provision.d.ts +0 -71
- package/dist/provision.d.ts.map +0 -1
- package/dist/provision.js +0 -56
- package/dist/provision.js.map +0 -1
- package/dist/service-config.d.ts +0 -22
- package/dist/service-config.d.ts.map +0 -1
- package/dist/service-config.js +0 -210
- package/dist/service-config.js.map +0 -1
- package/dist/service.d.ts +0 -71
- package/dist/service.d.ts.map +0 -1
- package/dist/service.js +0 -56
- package/dist/service.js.map +0 -1
- package/src/__tests__/service.test.ts +0 -197
- package/src/event.ts +0 -15
- package/src/gate.ts +0 -44
- package/src/internal/topo-saves.ts +0 -429
- package/src/provision.ts +0 -148
package/src/topo-store.ts
CHANGED
|
@@ -1,83 +1,222 @@
|
|
|
1
1
|
import type { SQLQueryBindings } from 'bun:sqlite';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
2
|
+
import { existsSync, statSync } from 'node:fs';
|
|
3
3
|
|
|
4
4
|
import { NotFoundError } from './errors.js';
|
|
5
|
-
import {
|
|
5
|
+
import type { Topo } from './topo.js';
|
|
6
|
+
import { resource } from './resource.js';
|
|
6
7
|
import { Result } from './result.js';
|
|
7
|
-
import
|
|
8
|
-
|
|
8
|
+
import {
|
|
9
|
+
TOPO_SCHEMA_VERSION,
|
|
10
|
+
ensureTopoSnapshotSchema,
|
|
11
|
+
listTopoSnapshots as listStoredTopoSnapshots,
|
|
12
|
+
pinTopoSnapshot as pinStoredTopoSnapshot,
|
|
13
|
+
unpinTopoSnapshot as unpinStoredTopoSnapshot,
|
|
14
|
+
} from './internal/topo-snapshots.js';
|
|
15
|
+
import type {
|
|
16
|
+
CreateTopoSnapshotInput,
|
|
17
|
+
ListTopoSnapshotsOptions,
|
|
18
|
+
TopoSnapshot,
|
|
19
|
+
} from './internal/topo-snapshots.js';
|
|
9
20
|
import type {
|
|
10
21
|
TopoStoreExportRecord,
|
|
11
|
-
|
|
22
|
+
TopoStoreResourceRecord,
|
|
12
23
|
TopoStoreRef,
|
|
13
24
|
TopoStoreTrailDetailRecord,
|
|
14
25
|
TopoStoreTrailRecord,
|
|
15
26
|
} from './internal/topo-store-read.js';
|
|
16
27
|
import {
|
|
17
28
|
getTopoStoreExport,
|
|
18
|
-
|
|
29
|
+
getTopoStoreResource,
|
|
19
30
|
getTopoStoreTrail,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
listTopoStoreSaves,
|
|
31
|
+
listTopoStoreResources,
|
|
32
|
+
listTopoStoreSnapshots,
|
|
23
33
|
listTopoStoreTrails,
|
|
24
34
|
queryTopoStore,
|
|
25
|
-
|
|
35
|
+
readTopoStoreSnapshot,
|
|
26
36
|
} from './internal/topo-store-read.js';
|
|
27
|
-
import {
|
|
37
|
+
import { createTopoSnapshot as storeTopoSnapshot } from './internal/topo-store.js';
|
|
38
|
+
import {
|
|
39
|
+
openReadTrailsDb,
|
|
40
|
+
openWriteTrailsDb,
|
|
41
|
+
deriveTrailsDbPath,
|
|
42
|
+
} from './internal/trails-db.js';
|
|
28
43
|
import type { TrailsDbLocationOptions } from './internal/trails-db.js';
|
|
29
44
|
|
|
45
|
+
interface MigratedDbIdentity {
|
|
46
|
+
readonly mtimeMs: number;
|
|
47
|
+
readonly size: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const migratedTopoDbPaths = new Map<string, MigratedDbIdentity>();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Test-only instrumentation counters. Incremented by the read-path migration
|
|
54
|
+
* check to let tests assert that a current-schema store does not escalate to
|
|
55
|
+
* a write-mode open, and that cache invalidation re-runs the check when the
|
|
56
|
+
* underlying file is replaced.
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export const __topoStoreMigrationStats = {
|
|
61
|
+
peekCalls: 0,
|
|
62
|
+
writeEscalations: 0,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const peekTopoSchemaVersion = (
|
|
66
|
+
options: TrailsDbLocationOptions | undefined
|
|
67
|
+
): number | undefined => {
|
|
68
|
+
let db: ReturnType<typeof openReadTrailsDb> | undefined;
|
|
69
|
+
try {
|
|
70
|
+
db = openReadTrailsDb(options);
|
|
71
|
+
const row = db
|
|
72
|
+
.query<{ version: number }, [string]>(
|
|
73
|
+
'SELECT version FROM meta_schema_versions WHERE subsystem = ?'
|
|
74
|
+
)
|
|
75
|
+
.get('topo');
|
|
76
|
+
return row?.version ?? 0;
|
|
77
|
+
} catch {
|
|
78
|
+
// Table missing or unexpected shape: caller will escalate to a write-mode
|
|
79
|
+
// open and run the migration, which rebuilds the schema.
|
|
80
|
+
return undefined;
|
|
81
|
+
} finally {
|
|
82
|
+
db?.close();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const statIdentity = (dbPath: string): MigratedDbIdentity | undefined => {
|
|
87
|
+
try {
|
|
88
|
+
const info = statSync(dbPath);
|
|
89
|
+
return { mtimeMs: info.mtimeMs, size: info.size };
|
|
90
|
+
} catch {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const identitiesEqual = (
|
|
96
|
+
a: MigratedDbIdentity,
|
|
97
|
+
b: MigratedDbIdentity
|
|
98
|
+
): boolean => a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
99
|
+
|
|
100
|
+
const runTopoMigrationEscalation = (
|
|
101
|
+
options: TrailsDbLocationOptions | undefined,
|
|
102
|
+
dbPath: string,
|
|
103
|
+
fallbackIdentity: MigratedDbIdentity
|
|
104
|
+
): void => {
|
|
105
|
+
__topoStoreMigrationStats.writeEscalations += 1;
|
|
106
|
+
const db = openWriteTrailsDb(options);
|
|
107
|
+
try {
|
|
108
|
+
ensureTopoSnapshotSchema(db);
|
|
109
|
+
} finally {
|
|
110
|
+
db.close();
|
|
111
|
+
}
|
|
112
|
+
// Re-stat after the migration so the cached identity matches the file we
|
|
113
|
+
// just touched, avoiding a spurious second escalation on the next read.
|
|
114
|
+
const postIdentity = statIdentity(dbPath) ?? fallbackIdentity;
|
|
115
|
+
migratedTopoDbPaths.set(dbPath, postIdentity);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const resolveIdentityIfFresh = (
|
|
119
|
+
dbPath: string
|
|
120
|
+
): MigratedDbIdentity | undefined => {
|
|
121
|
+
if (!existsSync(dbPath)) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const identity = statIdentity(dbPath);
|
|
125
|
+
if (identity === undefined) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const cached = migratedTopoDbPaths.get(dbPath);
|
|
129
|
+
if (cached !== undefined && identitiesEqual(cached, identity)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
return identity;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Ensure the topo snapshot schema is at the current version before any
|
|
137
|
+
* read-only access. Peeks the version through a read-only handle first and
|
|
138
|
+
* only escalates to a write-mode open + migration when the store is stale.
|
|
139
|
+
*
|
|
140
|
+
* Memoized per resolved DB path keyed on file identity (mtime + size) so a
|
|
141
|
+
* long-running process that deletes and recreates `trails.db` re-runs the
|
|
142
|
+
* migration check against the fresh file.
|
|
143
|
+
*
|
|
144
|
+
* If the DB file does not yet exist, this is a no-op — the downstream read
|
|
145
|
+
* path will surface its own NotFoundError.
|
|
146
|
+
*/
|
|
147
|
+
const ensureTopoMigratedIfExists = (
|
|
148
|
+
options?: TrailsDbLocationOptions
|
|
149
|
+
): void => {
|
|
150
|
+
const dbPath = deriveTrailsDbPath(options);
|
|
151
|
+
const identity = resolveIdentityIfFresh(dbPath);
|
|
152
|
+
if (identity === undefined) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
__topoStoreMigrationStats.peekCalls += 1;
|
|
157
|
+
const version = peekTopoSchemaVersion(options);
|
|
158
|
+
if (version !== undefined && version >= TOPO_SCHEMA_VERSION) {
|
|
159
|
+
// Already current — record identity and skip the write-mode open entirely,
|
|
160
|
+
// preserving the read-only contract for callers whose filesystem mounts
|
|
161
|
+
// `trails.db` read-only.
|
|
162
|
+
migratedTopoDbPaths.set(dbPath, identity);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
runTopoMigrationEscalation(options, dbPath, identity);
|
|
167
|
+
};
|
|
168
|
+
|
|
30
169
|
export type {
|
|
31
170
|
TopoStoreExportRecord,
|
|
32
|
-
|
|
171
|
+
TopoStoreResourceRecord,
|
|
33
172
|
TopoStoreRef,
|
|
34
173
|
TopoStoreTrailDetailRecord,
|
|
35
174
|
TopoStoreTrailRecord,
|
|
36
175
|
} from './internal/topo-store-read.js';
|
|
176
|
+
export type {
|
|
177
|
+
CreateTopoSnapshotInput,
|
|
178
|
+
ListTopoSnapshotsOptions,
|
|
179
|
+
TopoSnapshot,
|
|
180
|
+
} from './internal/topo-snapshots.js';
|
|
37
181
|
|
|
38
182
|
export interface ReadOnlyTopoStore {
|
|
39
183
|
readonly exports: {
|
|
40
184
|
get(ref?: TopoStoreRef): TopoStoreExportRecord | undefined;
|
|
41
185
|
};
|
|
42
|
-
readonly pins: {
|
|
43
|
-
get(name: string): TopoPinRecord | undefined;
|
|
44
|
-
list(): readonly TopoPinRecord[];
|
|
45
|
-
};
|
|
46
186
|
query<TRow extends Record<string, unknown>>(
|
|
47
187
|
sql: string,
|
|
48
188
|
bindings?: readonly SQLQueryBindings[]
|
|
49
189
|
): readonly TRow[];
|
|
50
|
-
readonly
|
|
190
|
+
readonly resources: {
|
|
51
191
|
get(
|
|
52
192
|
id: string,
|
|
53
|
-
options?: { readonly
|
|
54
|
-
):
|
|
193
|
+
options?: { readonly snapshot?: TopoStoreRef }
|
|
194
|
+
): TopoStoreResourceRecord | undefined;
|
|
55
195
|
list(options?: {
|
|
56
|
-
readonly
|
|
57
|
-
}): readonly
|
|
196
|
+
readonly snapshot?: TopoStoreRef;
|
|
197
|
+
}): readonly TopoStoreResourceRecord[];
|
|
58
198
|
};
|
|
59
|
-
readonly
|
|
60
|
-
get(ref?: TopoStoreRef):
|
|
61
|
-
latest():
|
|
62
|
-
list(): readonly
|
|
199
|
+
readonly snapshots: {
|
|
200
|
+
get(ref?: TopoStoreRef): TopoSnapshot | undefined;
|
|
201
|
+
latest(): TopoSnapshot | undefined;
|
|
202
|
+
list(options?: ListTopoSnapshotsOptions): readonly TopoSnapshot[];
|
|
63
203
|
};
|
|
64
204
|
readonly trails: {
|
|
65
205
|
get(
|
|
66
206
|
id: string,
|
|
67
|
-
options?: { readonly
|
|
207
|
+
options?: { readonly snapshot?: TopoStoreRef }
|
|
68
208
|
): TopoStoreTrailDetailRecord | undefined;
|
|
69
209
|
list(options?: {
|
|
70
210
|
readonly intent?: TopoStoreTrailRecord['intent'];
|
|
71
|
-
readonly
|
|
211
|
+
readonly snapshot?: TopoStoreRef;
|
|
72
212
|
}): readonly TopoStoreTrailRecord[];
|
|
73
213
|
};
|
|
74
214
|
}
|
|
75
215
|
|
|
76
216
|
export interface MockTopoStoreSeed {
|
|
77
217
|
readonly exports?: readonly TopoStoreExportRecord[];
|
|
78
|
-
readonly
|
|
79
|
-
readonly
|
|
80
|
-
readonly saves?: readonly TopoSaveRecord[];
|
|
218
|
+
readonly resources?: readonly TopoStoreResourceRecord[];
|
|
219
|
+
readonly snapshots?: readonly TopoSnapshot[];
|
|
81
220
|
readonly trails?: readonly TopoStoreTrailDetailRecord[];
|
|
82
221
|
}
|
|
83
222
|
|
|
@@ -90,17 +229,18 @@ const resolveStoreRootDir = (options?: TrailsDbLocationOptions): string =>
|
|
|
90
229
|
const requireReadDb = (
|
|
91
230
|
options?: TrailsDbLocationOptions
|
|
92
231
|
): ReturnType<typeof openReadTrailsDb> => {
|
|
93
|
-
const dbPath =
|
|
232
|
+
const dbPath = deriveTrailsDbPath(options);
|
|
94
233
|
if (!existsSync(dbPath)) {
|
|
95
234
|
throw new NotFoundError(missingStoreMessage);
|
|
96
235
|
}
|
|
236
|
+
ensureTopoMigratedIfExists(options);
|
|
97
237
|
return openReadTrailsDb(options);
|
|
98
238
|
};
|
|
99
239
|
|
|
100
240
|
const requireSavedTopoState = (
|
|
101
241
|
db: ReturnType<typeof openReadTrailsDb>
|
|
102
242
|
): void => {
|
|
103
|
-
if (
|
|
243
|
+
if (readTopoStoreSnapshot(db) === undefined) {
|
|
104
244
|
throw new NotFoundError(missingStoreMessage);
|
|
105
245
|
}
|
|
106
246
|
};
|
|
@@ -119,31 +259,26 @@ const withStoredTopoState = <T>(
|
|
|
119
259
|
};
|
|
120
260
|
|
|
121
261
|
const createSeedResolver = (seed?: MockTopoStoreSeed) => {
|
|
122
|
-
const
|
|
123
|
-
const pins = [...(seed?.pins ?? [])];
|
|
262
|
+
const snapshots = [...(seed?.snapshots ?? [])];
|
|
124
263
|
const trails = [...(seed?.trails ?? [])];
|
|
125
|
-
const
|
|
264
|
+
const resources = [...(seed?.resources ?? [])];
|
|
126
265
|
const exports = [...(seed?.exports ?? [])];
|
|
127
266
|
|
|
128
|
-
const
|
|
129
|
-
if (ref?.
|
|
130
|
-
return
|
|
267
|
+
const resolveSnapshot = (ref?: TopoStoreRef): TopoSnapshot | undefined => {
|
|
268
|
+
if (ref?.snapshotId !== undefined) {
|
|
269
|
+
return snapshots.find((snapshot) => snapshot.id === ref.snapshotId);
|
|
131
270
|
}
|
|
132
271
|
if (ref?.pin !== undefined) {
|
|
133
|
-
|
|
134
|
-
return pin === undefined
|
|
135
|
-
? undefined
|
|
136
|
-
: saves.find((save) => save.id === pin.saveId);
|
|
272
|
+
return snapshots.find((snapshot) => snapshot.pinnedAs === ref.pin);
|
|
137
273
|
}
|
|
138
|
-
return
|
|
274
|
+
return snapshots[0];
|
|
139
275
|
};
|
|
140
276
|
|
|
141
277
|
return {
|
|
142
278
|
exports,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
saves,
|
|
279
|
+
resolveSnapshot,
|
|
280
|
+
resources,
|
|
281
|
+
snapshots,
|
|
147
282
|
trails,
|
|
148
283
|
};
|
|
149
284
|
};
|
|
@@ -156,69 +291,85 @@ export const createMockTopoStore = (
|
|
|
156
291
|
return {
|
|
157
292
|
exports: {
|
|
158
293
|
get(ref?: TopoStoreRef) {
|
|
159
|
-
const
|
|
160
|
-
return
|
|
294
|
+
const snapshot = resolved.resolveSnapshot(ref);
|
|
295
|
+
return snapshot === undefined
|
|
161
296
|
? undefined
|
|
162
|
-
: resolved.exports.find((entry) => entry.
|
|
297
|
+
: resolved.exports.find((entry) => entry.snapshot.id === snapshot.id);
|
|
163
298
|
},
|
|
164
299
|
},
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
list() {
|
|
170
|
-
return resolved.pins;
|
|
171
|
-
},
|
|
300
|
+
query() {
|
|
301
|
+
throw new NotFoundError(
|
|
302
|
+
'Mock topoStore.query() is unsupported. Seed typed accessors instead.'
|
|
303
|
+
);
|
|
172
304
|
},
|
|
173
|
-
|
|
305
|
+
resources: {
|
|
174
306
|
get(id, options) {
|
|
175
|
-
const
|
|
176
|
-
return resolved.
|
|
307
|
+
const snapshot = resolved.resolveSnapshot(options?.snapshot);
|
|
308
|
+
return resolved.resources.find(
|
|
177
309
|
(item) =>
|
|
178
|
-
item.id === id &&
|
|
310
|
+
item.id === id &&
|
|
311
|
+
(snapshot === undefined || item.snapshotId === snapshot.id)
|
|
179
312
|
);
|
|
180
313
|
},
|
|
181
314
|
list(options) {
|
|
182
|
-
const
|
|
183
|
-
return
|
|
315
|
+
const snapshot = resolved.resolveSnapshot(options?.snapshot);
|
|
316
|
+
return snapshot === undefined
|
|
184
317
|
? []
|
|
185
|
-
: resolved.
|
|
318
|
+
: resolved.resources.filter(
|
|
319
|
+
(item) => item.snapshotId === snapshot.id
|
|
320
|
+
);
|
|
186
321
|
},
|
|
187
322
|
},
|
|
188
|
-
|
|
189
|
-
throw new NotFoundError(
|
|
190
|
-
'Mock topoStore.query() is unsupported. Seed typed accessors instead.'
|
|
191
|
-
);
|
|
192
|
-
},
|
|
193
|
-
saves: {
|
|
323
|
+
snapshots: {
|
|
194
324
|
get(ref?: TopoStoreRef) {
|
|
195
|
-
return resolved.
|
|
325
|
+
return resolved.resolveSnapshot(ref);
|
|
196
326
|
},
|
|
197
327
|
latest() {
|
|
198
|
-
return resolved.
|
|
328
|
+
return resolved.snapshots[0];
|
|
199
329
|
},
|
|
200
|
-
list() {
|
|
201
|
-
|
|
330
|
+
list(options) {
|
|
331
|
+
let snapshots =
|
|
332
|
+
options?.pinned === undefined
|
|
333
|
+
? resolved.snapshots
|
|
334
|
+
: resolved.snapshots.filter((snapshot) =>
|
|
335
|
+
options.pinned
|
|
336
|
+
? snapshot.pinnedAs !== undefined
|
|
337
|
+
: snapshot.pinnedAs === undefined
|
|
338
|
+
);
|
|
339
|
+
if (options?.before !== undefined) {
|
|
340
|
+
const target = snapshots.find((s) => s.id === options.before);
|
|
341
|
+
if (target !== undefined) {
|
|
342
|
+
snapshots = snapshots.filter(
|
|
343
|
+
(s) =>
|
|
344
|
+
s.createdAt < target.createdAt ||
|
|
345
|
+
(s.createdAt === target.createdAt && s.id < target.id)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (options?.limit !== undefined) {
|
|
350
|
+
snapshots = snapshots.slice(0, options.limit);
|
|
351
|
+
}
|
|
352
|
+
return snapshots;
|
|
202
353
|
},
|
|
203
354
|
},
|
|
204
355
|
trails: {
|
|
205
356
|
get(id, options) {
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
357
|
+
const snapshot = resolved.resolveSnapshot(options?.snapshot);
|
|
358
|
+
if (snapshot === undefined) {
|
|
208
359
|
return;
|
|
209
360
|
}
|
|
210
361
|
return resolved.trails.find(
|
|
211
|
-
(trail) => trail.id === id && trail.
|
|
362
|
+
(trail) => trail.id === id && trail.snapshotId === snapshot.id
|
|
212
363
|
);
|
|
213
364
|
},
|
|
214
365
|
list(options) {
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
366
|
+
const snapshot = resolved.resolveSnapshot(options?.snapshot);
|
|
367
|
+
if (snapshot === undefined) {
|
|
217
368
|
return [];
|
|
218
369
|
}
|
|
219
370
|
return resolved.trails.filter(
|
|
220
371
|
(trail) =>
|
|
221
|
-
trail.
|
|
372
|
+
trail.snapshotId === snapshot.id &&
|
|
222
373
|
(options?.intent === undefined || trail.intent === options.intent)
|
|
223
374
|
);
|
|
224
375
|
},
|
|
@@ -234,45 +385,39 @@ export const createTopoStore = (
|
|
|
234
385
|
return withStoredTopoState(options, (db) => getTopoStoreExport(db, ref));
|
|
235
386
|
},
|
|
236
387
|
},
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
388
|
+
query<TRow extends Record<string, unknown>>(
|
|
389
|
+
sql: string,
|
|
390
|
+
bindings?: readonly SQLQueryBindings[]
|
|
391
|
+
) {
|
|
392
|
+
return withStoredTopoState(options, (db) =>
|
|
393
|
+
queryTopoStore<TRow>(db, sql, bindings)
|
|
394
|
+
);
|
|
244
395
|
},
|
|
245
|
-
|
|
396
|
+
resources: {
|
|
246
397
|
get(id, queryOptions) {
|
|
247
398
|
return withStoredTopoState(options, (db) =>
|
|
248
|
-
|
|
399
|
+
getTopoStoreResource(db, id, queryOptions)
|
|
249
400
|
);
|
|
250
401
|
},
|
|
251
402
|
list(queryOptions) {
|
|
252
403
|
return withStoredTopoState(options, (db) =>
|
|
253
|
-
|
|
404
|
+
listTopoStoreResources(db, queryOptions)
|
|
254
405
|
);
|
|
255
406
|
},
|
|
256
407
|
},
|
|
257
|
-
|
|
258
|
-
sql: string,
|
|
259
|
-
bindings?: readonly SQLQueryBindings[]
|
|
260
|
-
) {
|
|
261
|
-
return withStoredTopoState(options, (db) =>
|
|
262
|
-
queryTopoStore<TRow>(db, sql, bindings)
|
|
263
|
-
);
|
|
264
|
-
},
|
|
265
|
-
saves: {
|
|
408
|
+
snapshots: {
|
|
266
409
|
get(ref?: TopoStoreRef) {
|
|
267
410
|
return withStoredTopoState(options, (db) =>
|
|
268
|
-
|
|
411
|
+
readTopoStoreSnapshot(db, ref)
|
|
269
412
|
);
|
|
270
413
|
},
|
|
271
414
|
latest() {
|
|
272
|
-
return withStoredTopoState(options, (db) =>
|
|
415
|
+
return withStoredTopoState(options, (db) => readTopoStoreSnapshot(db));
|
|
273
416
|
},
|
|
274
|
-
list() {
|
|
275
|
-
return withStoredTopoState(options, (db) =>
|
|
417
|
+
list(snapshotOptions) {
|
|
418
|
+
return withStoredTopoState(options, (db) =>
|
|
419
|
+
listTopoStoreSnapshots(db, snapshotOptions)
|
|
420
|
+
);
|
|
276
421
|
},
|
|
277
422
|
},
|
|
278
423
|
trails: {
|
|
@@ -289,7 +434,71 @@ export const createTopoStore = (
|
|
|
289
434
|
},
|
|
290
435
|
});
|
|
291
436
|
|
|
292
|
-
export const
|
|
437
|
+
export const createTopoSnapshot = (
|
|
438
|
+
topo: Topo,
|
|
439
|
+
options?: TrailsDbLocationOptions & CreateTopoSnapshotInput
|
|
440
|
+
): Result<TopoSnapshot, Error> => {
|
|
441
|
+
const db = openWriteTrailsDb(options);
|
|
442
|
+
try {
|
|
443
|
+
return storeTopoSnapshot(db, topo, options);
|
|
444
|
+
} finally {
|
|
445
|
+
db.close();
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
export const listTopoSnapshots = (
|
|
450
|
+
options?: TrailsDbLocationOptions & ListTopoSnapshotsOptions
|
|
451
|
+
): readonly TopoSnapshot[] => {
|
|
452
|
+
const dbPath = deriveTrailsDbPath(options);
|
|
453
|
+
if (!existsSync(dbPath)) {
|
|
454
|
+
return [];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
ensureTopoMigratedIfExists(options);
|
|
458
|
+
const db = openReadTrailsDb(options);
|
|
459
|
+
try {
|
|
460
|
+
return listStoredTopoSnapshots(db, options);
|
|
461
|
+
} finally {
|
|
462
|
+
db.close();
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
export const pinTopoSnapshot = (
|
|
467
|
+
id: string,
|
|
468
|
+
name: string,
|
|
469
|
+
options?: TrailsDbLocationOptions
|
|
470
|
+
): TopoSnapshot | undefined => {
|
|
471
|
+
const dbPath = deriveTrailsDbPath(options);
|
|
472
|
+
if (!existsSync(dbPath)) {
|
|
473
|
+
return undefined;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const db = openWriteTrailsDb(options);
|
|
477
|
+
try {
|
|
478
|
+
return pinStoredTopoSnapshot(db, { id, name });
|
|
479
|
+
} finally {
|
|
480
|
+
db.close();
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
export const unpinTopoSnapshot = (
|
|
485
|
+
nameOrId: string,
|
|
486
|
+
options?: TrailsDbLocationOptions
|
|
487
|
+
): TopoSnapshot | undefined => {
|
|
488
|
+
const dbPath = deriveTrailsDbPath(options);
|
|
489
|
+
if (!existsSync(dbPath)) {
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const db = openWriteTrailsDb(options);
|
|
494
|
+
try {
|
|
495
|
+
return unpinStoredTopoSnapshot(db, nameOrId);
|
|
496
|
+
} finally {
|
|
497
|
+
db.close();
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
export const topoStore = resource('topo.store', {
|
|
293
502
|
create: (svc) =>
|
|
294
503
|
Result.ok(
|
|
295
504
|
createTopoStore({
|