@contractspec/lib.runtime-sandbox 2.7.6 → 2.7.9
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/README.md +42 -22
- package/dist/browser/index.js +364 -120
- package/dist/index.js +364 -120
- package/dist/node/index.js +364 -120
- package/dist/web/index.d.ts +3 -3
- package/package.json +7 -7
- package/src/adapters/pglite/adapter.ts +132 -132
- package/src/index.ts +9 -9
- package/src/ports/database.port.ts +53 -53
- package/src/ports/index.ts +2 -2
- package/src/types/database.types.ts +22 -22
- package/src/web/database/migrations.ts +195 -195
- package/src/web/database/schema.ts +419 -419
- package/src/web/events/local-pubsub.ts +22 -22
- package/src/web/graphql/local-client.ts +495 -496
- package/src/web/index.ts +6 -11
- package/src/web/runtime/seeders/index.ts +611 -352
- package/src/web/runtime/services.ts +104 -105
- package/src/web/storage/indexeddb.ts +98 -98
- package/src/web/utils/id.ts +5 -5
package/README.md
CHANGED
|
@@ -1,40 +1,60 @@
|
|
|
1
1
|
# @contractspec/lib.runtime-sandbox
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Browser-compatible database abstraction built on PGLite for client-side SQL execution.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What It Provides
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **Layer**: lib.
|
|
8
|
+
- **Consumers**: bundles, apps.
|
|
9
|
+
- `src/adapters/` contains runtime, provider, or environment-specific adapters.
|
|
10
|
+
- Related ContractSpec packages include `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
|
|
11
|
+
- `src/adapters/` contains runtime, provider, or environment-specific adapters.
|
|
8
12
|
|
|
9
13
|
## Installation
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
bun add @contractspec/lib.runtime-sandbox
|
|
13
|
-
```
|
|
15
|
+
`npm install @contractspec/lib.runtime-sandbox`
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
or
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
`bun add @contractspec/lib.runtime-sandbox`
|
|
18
20
|
|
|
19
21
|
## Usage
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
import { createPGLiteAdapter } from "@contractspec/lib.runtime-sandbox";
|
|
23
|
+
Import the root entrypoint from `@contractspec/lib.runtime-sandbox`, or choose a documented subpath when you only need one part of the package surface.
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
await db.init();
|
|
25
|
+
## Architecture
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
- `src/adapters/` contains runtime, provider, or environment-specific adapters.
|
|
28
|
+
- `src/index.ts` is the root public barrel and package entrypoint.
|
|
29
|
+
- `src/ports` is part of the package's public or composition surface.
|
|
30
|
+
- `src/types` is part of the package's public or composition surface.
|
|
31
|
+
- `src/web` is part of the package's public or composition surface.
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
console.log(result.rows);
|
|
32
|
-
```
|
|
33
|
+
## Public Entry Points
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
- Export `.` resolves through `./src/index.ts`.
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
import { web } from "@contractspec/lib.runtime-sandbox";
|
|
38
|
-
```
|
|
37
|
+
## Local Commands
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
- `bun run dev` — contractspec-bun-build dev
|
|
40
|
+
- `bun run build` — bun run prebuild && bun run build:bundle && bun run build:types
|
|
41
|
+
- `bun run lint` — bun run lint:fix
|
|
42
|
+
- `bun run lint:check` — biome check .
|
|
43
|
+
- `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
|
|
44
|
+
- `bun run typecheck` — tsc --noEmit
|
|
45
|
+
- `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
|
|
46
|
+
- `bun run publish:pkg:canary` — bun publish:pkg --tag canary
|
|
47
|
+
- `bun run clean` — rm -rf dist
|
|
48
|
+
- `bun run build:bundle` — contractspec-bun-build transpile
|
|
49
|
+
- `bun run build:types` — contractspec-bun-build types
|
|
50
|
+
- `bun run prebuild` — contractspec-bun-build prebuild
|
|
51
|
+
|
|
52
|
+
## Recent Updates
|
|
53
|
+
|
|
54
|
+
- Replace eslint+prettier by biomejs to optimize speed.
|
|
55
|
+
|
|
56
|
+
## Notes
|
|
57
|
+
|
|
58
|
+
- DatabasePort interface is the adapter boundary — consumers depend on the port, not the implementation.
|
|
59
|
+
- PGLite adapter must stay browser-compatible (no Node-only APIs).
|
|
60
|
+
- Migration schema must remain stable — breaking changes require a migration path.
|
package/dist/browser/index.js
CHANGED
|
@@ -228,16 +228,259 @@ async function seedWorkflowSystem(params) {
|
|
|
228
228
|
const existing = await db.query(`SELECT COUNT(*) as count FROM workflow_definition WHERE "projectId" = $1`, [projectId]);
|
|
229
229
|
if (existing.rows[0]?.count > 0)
|
|
230
230
|
return;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
231
|
+
const definitions = [
|
|
232
|
+
{
|
|
233
|
+
id: "wf_expense",
|
|
234
|
+
name: "Expense Approval",
|
|
235
|
+
description: "Approve non-trivial spend before finance releases budget.",
|
|
236
|
+
type: "APPROVAL",
|
|
237
|
+
status: "ACTIVE",
|
|
238
|
+
createdAt: "2026-03-10T09:00:00.000Z",
|
|
239
|
+
updatedAt: "2026-03-20T08:15:00.000Z",
|
|
240
|
+
steps: [
|
|
241
|
+
{
|
|
242
|
+
id: "wfstep_expense_manager",
|
|
243
|
+
name: "Manager Review",
|
|
244
|
+
description: "Validate business value and team budget.",
|
|
245
|
+
stepOrder: 1,
|
|
246
|
+
type: "APPROVAL",
|
|
247
|
+
requiredRoles: ["manager"],
|
|
248
|
+
createdAt: "2026-03-10T09:00:00.000Z"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: "wfstep_expense_finance",
|
|
252
|
+
name: "Finance Review",
|
|
253
|
+
description: "Confirm ledger coding and spending threshold.",
|
|
254
|
+
stepOrder: 2,
|
|
255
|
+
type: "APPROVAL",
|
|
256
|
+
requiredRoles: ["finance"],
|
|
257
|
+
createdAt: "2026-03-10T09:10:00.000Z"
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "wf_vendor",
|
|
263
|
+
name: "Vendor Onboarding",
|
|
264
|
+
description: "Sequence security, procurement, and legal before activation.",
|
|
265
|
+
type: "SEQUENTIAL",
|
|
266
|
+
status: "ACTIVE",
|
|
267
|
+
createdAt: "2026-03-08T11:00:00.000Z",
|
|
268
|
+
updatedAt: "2026-03-19T13:10:00.000Z",
|
|
269
|
+
steps: [
|
|
270
|
+
{
|
|
271
|
+
id: "wfstep_vendor_security",
|
|
272
|
+
name: "Security Check",
|
|
273
|
+
description: "Review data access and integration scope.",
|
|
274
|
+
stepOrder: 1,
|
|
275
|
+
type: "APPROVAL",
|
|
276
|
+
requiredRoles: ["security"],
|
|
277
|
+
createdAt: "2026-03-08T11:00:00.000Z"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: "wfstep_vendor_procurement",
|
|
281
|
+
name: "Procurement Check",
|
|
282
|
+
description: "Validate pricing, procurement policy, and owner.",
|
|
283
|
+
stepOrder: 2,
|
|
284
|
+
type: "APPROVAL",
|
|
285
|
+
requiredRoles: ["procurement"],
|
|
286
|
+
createdAt: "2026-03-08T11:05:00.000Z"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: "wfstep_vendor_legal",
|
|
290
|
+
name: "Legal Sign-off",
|
|
291
|
+
description: "Approve terms before the vendor goes live.",
|
|
292
|
+
stepOrder: 3,
|
|
293
|
+
type: "APPROVAL",
|
|
294
|
+
requiredRoles: ["legal"],
|
|
295
|
+
createdAt: "2026-03-08T11:10:00.000Z"
|
|
296
|
+
}
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "wf_policy_exception",
|
|
301
|
+
name: "Policy Exception",
|
|
302
|
+
description: "Escalate a temporary exception through team lead and compliance.",
|
|
303
|
+
type: "APPROVAL",
|
|
304
|
+
status: "DRAFT",
|
|
305
|
+
createdAt: "2026-03-15T07:30:00.000Z",
|
|
306
|
+
updatedAt: "2026-03-18T11:20:00.000Z",
|
|
307
|
+
steps: [
|
|
308
|
+
{
|
|
309
|
+
id: "wfstep_policy_lead",
|
|
310
|
+
name: "Team Lead Review",
|
|
311
|
+
description: "Check urgency and expected blast radius.",
|
|
312
|
+
stepOrder: 1,
|
|
313
|
+
type: "APPROVAL",
|
|
314
|
+
requiredRoles: ["team-lead"],
|
|
315
|
+
createdAt: "2026-03-15T07:30:00.000Z"
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "wfstep_policy_compliance",
|
|
319
|
+
name: "Compliance Review",
|
|
320
|
+
description: "Accept or reject the exception request.",
|
|
321
|
+
stepOrder: 2,
|
|
322
|
+
type: "APPROVAL",
|
|
323
|
+
requiredRoles: ["compliance"],
|
|
324
|
+
createdAt: "2026-03-15T07:40:00.000Z"
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
];
|
|
329
|
+
const instances = [
|
|
330
|
+
{
|
|
331
|
+
id: "wfinst_expense_open",
|
|
332
|
+
definitionId: "wf_expense",
|
|
333
|
+
status: "IN_PROGRESS",
|
|
334
|
+
currentStepId: "wfstep_expense_finance",
|
|
335
|
+
data: { amount: 4200, currency: "EUR", vendor: "Nimbus AI" },
|
|
336
|
+
requestedBy: "sarah@contractspec.io",
|
|
337
|
+
startedAt: "2026-03-20T08:00:00.000Z",
|
|
338
|
+
completedAt: null,
|
|
339
|
+
approvals: [
|
|
340
|
+
{
|
|
341
|
+
id: "wfappr_expense_manager",
|
|
342
|
+
stepId: "wfstep_expense_manager",
|
|
343
|
+
status: "APPROVED",
|
|
344
|
+
actorId: "manager.demo",
|
|
345
|
+
comment: "Approved for the Q2 automation budget.",
|
|
346
|
+
decidedAt: "2026-03-20T08:15:00.000Z",
|
|
347
|
+
createdAt: "2026-03-20T08:05:00.000Z"
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: "wfappr_expense_finance",
|
|
351
|
+
stepId: "wfstep_expense_finance",
|
|
352
|
+
status: "PENDING",
|
|
353
|
+
actorId: null,
|
|
354
|
+
comment: null,
|
|
355
|
+
decidedAt: null,
|
|
356
|
+
createdAt: "2026-03-20T08:15:00.000Z"
|
|
357
|
+
}
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
id: "wfinst_vendor_done",
|
|
362
|
+
definitionId: "wf_vendor",
|
|
363
|
+
status: "COMPLETED",
|
|
364
|
+
currentStepId: null,
|
|
365
|
+
data: { vendor: "Acme Cloud", riskTier: "medium" },
|
|
366
|
+
requestedBy: "leo@contractspec.io",
|
|
367
|
+
startedAt: "2026-03-19T09:30:00.000Z",
|
|
368
|
+
completedAt: "2026-03-19T13:10:00.000Z",
|
|
369
|
+
approvals: [
|
|
370
|
+
{
|
|
371
|
+
id: "wfappr_vendor_security",
|
|
372
|
+
stepId: "wfstep_vendor_security",
|
|
373
|
+
status: "APPROVED",
|
|
374
|
+
actorId: "security.demo",
|
|
375
|
+
comment: "SOC2 scope is acceptable.",
|
|
376
|
+
decidedAt: "2026-03-19T10:10:00.000Z",
|
|
377
|
+
createdAt: "2026-03-19T09:35:00.000Z"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
id: "wfappr_vendor_procurement",
|
|
381
|
+
stepId: "wfstep_vendor_procurement",
|
|
382
|
+
status: "APPROVED",
|
|
383
|
+
actorId: "procurement.demo",
|
|
384
|
+
comment: "Commercial terms match the preferred vendor tier.",
|
|
385
|
+
decidedAt: "2026-03-19T11:25:00.000Z",
|
|
386
|
+
createdAt: "2026-03-19T10:15:00.000Z"
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
id: "wfappr_vendor_legal",
|
|
390
|
+
stepId: "wfstep_vendor_legal",
|
|
391
|
+
status: "APPROVED",
|
|
392
|
+
actorId: "legal.demo",
|
|
393
|
+
comment: "MSA redlines are complete.",
|
|
394
|
+
decidedAt: "2026-03-19T13:05:00.000Z",
|
|
395
|
+
createdAt: "2026-03-19T11:30:00.000Z"
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
id: "wfinst_policy_rejected",
|
|
401
|
+
definitionId: "wf_policy_exception",
|
|
402
|
+
status: "REJECTED",
|
|
403
|
+
currentStepId: "wfstep_policy_compliance",
|
|
404
|
+
data: { policy: "Model rollout freeze", durationDays: 14 },
|
|
405
|
+
requestedBy: "maya@contractspec.io",
|
|
406
|
+
startedAt: "2026-03-18T10:00:00.000Z",
|
|
407
|
+
completedAt: "2026-03-18T11:20:00.000Z",
|
|
408
|
+
approvals: [
|
|
409
|
+
{
|
|
410
|
+
id: "wfappr_policy_lead",
|
|
411
|
+
stepId: "wfstep_policy_lead",
|
|
412
|
+
status: "APPROVED",
|
|
413
|
+
actorId: "lead.demo",
|
|
414
|
+
comment: "Escalation justified for the release train.",
|
|
415
|
+
decidedAt: "2026-03-18T10:30:00.000Z",
|
|
416
|
+
createdAt: "2026-03-18T10:05:00.000Z"
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
id: "wfappr_policy_compliance",
|
|
420
|
+
stepId: "wfstep_policy_compliance",
|
|
421
|
+
status: "REJECTED",
|
|
422
|
+
actorId: "compliance.demo",
|
|
423
|
+
comment: "Exception exceeds the allowed blast radius.",
|
|
424
|
+
decidedAt: "2026-03-18T11:15:00.000Z",
|
|
425
|
+
createdAt: "2026-03-18T10:35:00.000Z"
|
|
426
|
+
}
|
|
427
|
+
]
|
|
428
|
+
}
|
|
429
|
+
];
|
|
430
|
+
for (const definition of definitions) {
|
|
431
|
+
await db.execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status, "createdAt", "updatedAt")
|
|
432
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
433
|
+
definition.id,
|
|
434
|
+
projectId,
|
|
435
|
+
"org_demo",
|
|
436
|
+
definition.name,
|
|
437
|
+
definition.description,
|
|
438
|
+
definition.type,
|
|
439
|
+
definition.status,
|
|
440
|
+
definition.createdAt,
|
|
441
|
+
definition.updatedAt
|
|
442
|
+
]);
|
|
443
|
+
for (const step of definition.steps) {
|
|
444
|
+
await db.execute(`INSERT INTO workflow_step (id, "definitionId", name, description, "stepOrder", type, "requiredRoles", "createdAt")
|
|
445
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
|
|
446
|
+
step.id,
|
|
447
|
+
definition.id,
|
|
448
|
+
step.name,
|
|
449
|
+
step.description,
|
|
450
|
+
step.stepOrder,
|
|
451
|
+
step.type,
|
|
452
|
+
JSON.stringify(step.requiredRoles),
|
|
453
|
+
step.createdAt
|
|
454
|
+
]);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
for (const instance of instances) {
|
|
458
|
+
await db.execute(`INSERT INTO workflow_instance (id, "projectId", "definitionId", status, "currentStepId", data, "requestedBy", "startedAt", "completedAt")
|
|
459
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
460
|
+
instance.id,
|
|
461
|
+
projectId,
|
|
462
|
+
instance.definitionId,
|
|
463
|
+
instance.status,
|
|
464
|
+
instance.currentStepId,
|
|
465
|
+
JSON.stringify(instance.data),
|
|
466
|
+
instance.requestedBy,
|
|
467
|
+
instance.startedAt,
|
|
468
|
+
instance.completedAt
|
|
469
|
+
]);
|
|
470
|
+
for (const approval of instance.approvals) {
|
|
471
|
+
await db.execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "actorId", comment, "decidedAt", "createdAt")
|
|
472
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
|
|
473
|
+
approval.id,
|
|
474
|
+
instance.id,
|
|
475
|
+
approval.stepId,
|
|
476
|
+
approval.status,
|
|
477
|
+
approval.actorId,
|
|
478
|
+
approval.comment,
|
|
479
|
+
approval.decidedAt,
|
|
480
|
+
approval.createdAt
|
|
481
|
+
]);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
241
484
|
}
|
|
242
485
|
async function seedMarketplace(params) {
|
|
243
486
|
const { projectId, db } = params;
|
|
@@ -1702,116 +1945,6 @@ var psaReviewTask = pgTable("psa_review_task", {
|
|
|
1702
1945
|
decidedAt: text("decidedAt"),
|
|
1703
1946
|
decidedBy: text("decidedBy")
|
|
1704
1947
|
});
|
|
1705
|
-
// src/web/storage/indexeddb.ts
|
|
1706
|
-
var DEFAULT_DB_NAME = "contractspec-runtime";
|
|
1707
|
-
var DEFAULT_STORE = "kv";
|
|
1708
|
-
var FALLBACK_STORE = new Map;
|
|
1709
|
-
|
|
1710
|
-
class LocalStorageService {
|
|
1711
|
-
options;
|
|
1712
|
-
dbPromise;
|
|
1713
|
-
constructor(options = {}) {
|
|
1714
|
-
this.options = options;
|
|
1715
|
-
}
|
|
1716
|
-
async init() {
|
|
1717
|
-
await this.getDb();
|
|
1718
|
-
}
|
|
1719
|
-
async get(key, fallback) {
|
|
1720
|
-
const db = await this.getDb();
|
|
1721
|
-
if (!db) {
|
|
1722
|
-
return FALLBACK_STORE.get(key) ?? fallback;
|
|
1723
|
-
}
|
|
1724
|
-
return new Promise((resolve, reject) => {
|
|
1725
|
-
const tx = db.transaction(this.storeName, "readonly");
|
|
1726
|
-
const store = tx.objectStore(this.storeName);
|
|
1727
|
-
const request = store.get(key);
|
|
1728
|
-
request.onsuccess = () => {
|
|
1729
|
-
resolve(request.result ?? fallback);
|
|
1730
|
-
};
|
|
1731
|
-
request.onerror = () => reject(request.error);
|
|
1732
|
-
});
|
|
1733
|
-
}
|
|
1734
|
-
async set(key, value) {
|
|
1735
|
-
const db = await this.getDb();
|
|
1736
|
-
if (!db) {
|
|
1737
|
-
FALLBACK_STORE.set(key, value);
|
|
1738
|
-
return;
|
|
1739
|
-
}
|
|
1740
|
-
await new Promise((resolve, reject) => {
|
|
1741
|
-
const tx = db.transaction(this.storeName, "readwrite");
|
|
1742
|
-
const store = tx.objectStore(this.storeName);
|
|
1743
|
-
const request = store.put(value, key);
|
|
1744
|
-
request.onsuccess = () => resolve();
|
|
1745
|
-
request.onerror = () => reject(request.error);
|
|
1746
|
-
});
|
|
1747
|
-
}
|
|
1748
|
-
async delete(key) {
|
|
1749
|
-
const db = await this.getDb();
|
|
1750
|
-
if (!db) {
|
|
1751
|
-
FALLBACK_STORE.delete(key);
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
|
-
await new Promise((resolve, reject) => {
|
|
1755
|
-
const tx = db.transaction(this.storeName, "readwrite");
|
|
1756
|
-
const store = tx.objectStore(this.storeName);
|
|
1757
|
-
const request = store.delete(key);
|
|
1758
|
-
request.onsuccess = () => resolve();
|
|
1759
|
-
request.onerror = () => reject(request.error);
|
|
1760
|
-
});
|
|
1761
|
-
}
|
|
1762
|
-
async clear() {
|
|
1763
|
-
const db = await this.getDb();
|
|
1764
|
-
if (!db) {
|
|
1765
|
-
FALLBACK_STORE.clear();
|
|
1766
|
-
return;
|
|
1767
|
-
}
|
|
1768
|
-
await new Promise((resolve, reject) => {
|
|
1769
|
-
const tx = db.transaction(this.storeName, "readwrite");
|
|
1770
|
-
const store = tx.objectStore(this.storeName);
|
|
1771
|
-
const request = store.clear();
|
|
1772
|
-
request.onsuccess = () => resolve();
|
|
1773
|
-
request.onerror = () => reject(request.error);
|
|
1774
|
-
});
|
|
1775
|
-
}
|
|
1776
|
-
get storeName() {
|
|
1777
|
-
return this.options.storeName ?? DEFAULT_STORE;
|
|
1778
|
-
}
|
|
1779
|
-
async getDb() {
|
|
1780
|
-
if (typeof indexedDB === "undefined") {
|
|
1781
|
-
return null;
|
|
1782
|
-
}
|
|
1783
|
-
if (!this.dbPromise) {
|
|
1784
|
-
this.dbPromise = this.openDb();
|
|
1785
|
-
}
|
|
1786
|
-
return this.dbPromise;
|
|
1787
|
-
}
|
|
1788
|
-
openDb() {
|
|
1789
|
-
return new Promise((resolve, reject) => {
|
|
1790
|
-
const request = indexedDB.open(this.options.dbName ?? DEFAULT_DB_NAME, this.options.version ?? 1);
|
|
1791
|
-
request.onerror = () => reject(request.error);
|
|
1792
|
-
request.onsuccess = () => {
|
|
1793
|
-
resolve(request.result);
|
|
1794
|
-
};
|
|
1795
|
-
request.onupgradeneeded = () => {
|
|
1796
|
-
const db = request.result;
|
|
1797
|
-
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
1798
|
-
db.createObjectStore(this.storeName);
|
|
1799
|
-
}
|
|
1800
|
-
};
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
// src/web/utils/id.ts
|
|
1805
|
-
function generateId(prefix) {
|
|
1806
|
-
const base = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2, 10);
|
|
1807
|
-
return prefix ? `${prefix}_${base}` : base;
|
|
1808
|
-
}
|
|
1809
|
-
// src/web/graphql/local-client.ts
|
|
1810
|
-
import { ApolloClient, InMemoryCache } from "@apollo/client";
|
|
1811
|
-
import { SchemaLink } from "@apollo/client/link/schema";
|
|
1812
|
-
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
1813
|
-
import { GraphQLScalarType, Kind } from "graphql";
|
|
1814
|
-
|
|
1815
1948
|
// src/web/events/local-pubsub.ts
|
|
1816
1949
|
class LocalEventBus {
|
|
1817
1950
|
listeners = new Map;
|
|
@@ -1835,6 +1968,17 @@ class LocalEventBus {
|
|
|
1835
1968
|
};
|
|
1836
1969
|
}
|
|
1837
1970
|
}
|
|
1971
|
+
// src/web/graphql/local-client.ts
|
|
1972
|
+
import { ApolloClient, InMemoryCache } from "@apollo/client";
|
|
1973
|
+
import { SchemaLink } from "@apollo/client/link/schema";
|
|
1974
|
+
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
1975
|
+
import { GraphQLScalarType, Kind } from "graphql";
|
|
1976
|
+
|
|
1977
|
+
// src/web/utils/id.ts
|
|
1978
|
+
function generateId(prefix) {
|
|
1979
|
+
const base = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2, 10);
|
|
1980
|
+
return prefix ? `${prefix}_${base}` : base;
|
|
1981
|
+
}
|
|
1838
1982
|
|
|
1839
1983
|
// src/web/graphql/local-client.ts
|
|
1840
1984
|
var typeDefs = `
|
|
@@ -2381,6 +2525,106 @@ function mapRecipeInstruction(row, locale) {
|
|
|
2381
2525
|
ordering: row.ordering ?? 0
|
|
2382
2526
|
};
|
|
2383
2527
|
}
|
|
2528
|
+
// src/web/storage/indexeddb.ts
|
|
2529
|
+
var DEFAULT_DB_NAME = "contractspec-runtime";
|
|
2530
|
+
var DEFAULT_STORE = "kv";
|
|
2531
|
+
var FALLBACK_STORE = new Map;
|
|
2532
|
+
|
|
2533
|
+
class LocalStorageService {
|
|
2534
|
+
options;
|
|
2535
|
+
dbPromise;
|
|
2536
|
+
constructor(options = {}) {
|
|
2537
|
+
this.options = options;
|
|
2538
|
+
}
|
|
2539
|
+
async init() {
|
|
2540
|
+
await this.getDb();
|
|
2541
|
+
}
|
|
2542
|
+
async get(key, fallback) {
|
|
2543
|
+
const db = await this.getDb();
|
|
2544
|
+
if (!db) {
|
|
2545
|
+
return FALLBACK_STORE.get(key) ?? fallback;
|
|
2546
|
+
}
|
|
2547
|
+
return new Promise((resolve, reject) => {
|
|
2548
|
+
const tx = db.transaction(this.storeName, "readonly");
|
|
2549
|
+
const store = tx.objectStore(this.storeName);
|
|
2550
|
+
const request = store.get(key);
|
|
2551
|
+
request.onsuccess = () => {
|
|
2552
|
+
resolve(request.result ?? fallback);
|
|
2553
|
+
};
|
|
2554
|
+
request.onerror = () => reject(request.error);
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
async set(key, value) {
|
|
2558
|
+
const db = await this.getDb();
|
|
2559
|
+
if (!db) {
|
|
2560
|
+
FALLBACK_STORE.set(key, value);
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2563
|
+
await new Promise((resolve, reject) => {
|
|
2564
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
2565
|
+
const store = tx.objectStore(this.storeName);
|
|
2566
|
+
const request = store.put(value, key);
|
|
2567
|
+
request.onsuccess = () => resolve();
|
|
2568
|
+
request.onerror = () => reject(request.error);
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
async delete(key) {
|
|
2572
|
+
const db = await this.getDb();
|
|
2573
|
+
if (!db) {
|
|
2574
|
+
FALLBACK_STORE.delete(key);
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
await new Promise((resolve, reject) => {
|
|
2578
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
2579
|
+
const store = tx.objectStore(this.storeName);
|
|
2580
|
+
const request = store.delete(key);
|
|
2581
|
+
request.onsuccess = () => resolve();
|
|
2582
|
+
request.onerror = () => reject(request.error);
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
async clear() {
|
|
2586
|
+
const db = await this.getDb();
|
|
2587
|
+
if (!db) {
|
|
2588
|
+
FALLBACK_STORE.clear();
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
await new Promise((resolve, reject) => {
|
|
2592
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
2593
|
+
const store = tx.objectStore(this.storeName);
|
|
2594
|
+
const request = store.clear();
|
|
2595
|
+
request.onsuccess = () => resolve();
|
|
2596
|
+
request.onerror = () => reject(request.error);
|
|
2597
|
+
});
|
|
2598
|
+
}
|
|
2599
|
+
get storeName() {
|
|
2600
|
+
return this.options.storeName ?? DEFAULT_STORE;
|
|
2601
|
+
}
|
|
2602
|
+
async getDb() {
|
|
2603
|
+
if (typeof indexedDB === "undefined") {
|
|
2604
|
+
return null;
|
|
2605
|
+
}
|
|
2606
|
+
if (!this.dbPromise) {
|
|
2607
|
+
this.dbPromise = this.openDb();
|
|
2608
|
+
}
|
|
2609
|
+
return this.dbPromise;
|
|
2610
|
+
}
|
|
2611
|
+
openDb() {
|
|
2612
|
+
return new Promise((resolve, reject) => {
|
|
2613
|
+
const request = indexedDB.open(this.options.dbName ?? DEFAULT_DB_NAME, this.options.version ?? 1);
|
|
2614
|
+
request.onerror = () => reject(request.error);
|
|
2615
|
+
request.onsuccess = () => {
|
|
2616
|
+
resolve(request.result);
|
|
2617
|
+
};
|
|
2618
|
+
request.onupgradeneeded = () => {
|
|
2619
|
+
const db = request.result;
|
|
2620
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
2621
|
+
db.createObjectStore(this.storeName);
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2384
2628
|
// src/web/runtime/services.ts
|
|
2385
2629
|
var DEFAULT_PROJECT_ID = "local-project";
|
|
2386
2630
|
|