@contractspec/lib.runtime-sandbox 2.7.5 → 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/dist/index.js
CHANGED
|
@@ -223,16 +223,259 @@ async function seedWorkflowSystem(params) {
|
|
|
223
223
|
const existing = await db.query(`SELECT COUNT(*) as count FROM workflow_definition WHERE "projectId" = $1`, [projectId]);
|
|
224
224
|
if (existing.rows[0]?.count > 0)
|
|
225
225
|
return;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
226
|
+
const definitions = [
|
|
227
|
+
{
|
|
228
|
+
id: "wf_expense",
|
|
229
|
+
name: "Expense Approval",
|
|
230
|
+
description: "Approve non-trivial spend before finance releases budget.",
|
|
231
|
+
type: "APPROVAL",
|
|
232
|
+
status: "ACTIVE",
|
|
233
|
+
createdAt: "2026-03-10T09:00:00.000Z",
|
|
234
|
+
updatedAt: "2026-03-20T08:15:00.000Z",
|
|
235
|
+
steps: [
|
|
236
|
+
{
|
|
237
|
+
id: "wfstep_expense_manager",
|
|
238
|
+
name: "Manager Review",
|
|
239
|
+
description: "Validate business value and team budget.",
|
|
240
|
+
stepOrder: 1,
|
|
241
|
+
type: "APPROVAL",
|
|
242
|
+
requiredRoles: ["manager"],
|
|
243
|
+
createdAt: "2026-03-10T09:00:00.000Z"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: "wfstep_expense_finance",
|
|
247
|
+
name: "Finance Review",
|
|
248
|
+
description: "Confirm ledger coding and spending threshold.",
|
|
249
|
+
stepOrder: 2,
|
|
250
|
+
type: "APPROVAL",
|
|
251
|
+
requiredRoles: ["finance"],
|
|
252
|
+
createdAt: "2026-03-10T09:10:00.000Z"
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: "wf_vendor",
|
|
258
|
+
name: "Vendor Onboarding",
|
|
259
|
+
description: "Sequence security, procurement, and legal before activation.",
|
|
260
|
+
type: "SEQUENTIAL",
|
|
261
|
+
status: "ACTIVE",
|
|
262
|
+
createdAt: "2026-03-08T11:00:00.000Z",
|
|
263
|
+
updatedAt: "2026-03-19T13:10:00.000Z",
|
|
264
|
+
steps: [
|
|
265
|
+
{
|
|
266
|
+
id: "wfstep_vendor_security",
|
|
267
|
+
name: "Security Check",
|
|
268
|
+
description: "Review data access and integration scope.",
|
|
269
|
+
stepOrder: 1,
|
|
270
|
+
type: "APPROVAL",
|
|
271
|
+
requiredRoles: ["security"],
|
|
272
|
+
createdAt: "2026-03-08T11:00:00.000Z"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: "wfstep_vendor_procurement",
|
|
276
|
+
name: "Procurement Check",
|
|
277
|
+
description: "Validate pricing, procurement policy, and owner.",
|
|
278
|
+
stepOrder: 2,
|
|
279
|
+
type: "APPROVAL",
|
|
280
|
+
requiredRoles: ["procurement"],
|
|
281
|
+
createdAt: "2026-03-08T11:05:00.000Z"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: "wfstep_vendor_legal",
|
|
285
|
+
name: "Legal Sign-off",
|
|
286
|
+
description: "Approve terms before the vendor goes live.",
|
|
287
|
+
stepOrder: 3,
|
|
288
|
+
type: "APPROVAL",
|
|
289
|
+
requiredRoles: ["legal"],
|
|
290
|
+
createdAt: "2026-03-08T11:10:00.000Z"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: "wf_policy_exception",
|
|
296
|
+
name: "Policy Exception",
|
|
297
|
+
description: "Escalate a temporary exception through team lead and compliance.",
|
|
298
|
+
type: "APPROVAL",
|
|
299
|
+
status: "DRAFT",
|
|
300
|
+
createdAt: "2026-03-15T07:30:00.000Z",
|
|
301
|
+
updatedAt: "2026-03-18T11:20:00.000Z",
|
|
302
|
+
steps: [
|
|
303
|
+
{
|
|
304
|
+
id: "wfstep_policy_lead",
|
|
305
|
+
name: "Team Lead Review",
|
|
306
|
+
description: "Check urgency and expected blast radius.",
|
|
307
|
+
stepOrder: 1,
|
|
308
|
+
type: "APPROVAL",
|
|
309
|
+
requiredRoles: ["team-lead"],
|
|
310
|
+
createdAt: "2026-03-15T07:30:00.000Z"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: "wfstep_policy_compliance",
|
|
314
|
+
name: "Compliance Review",
|
|
315
|
+
description: "Accept or reject the exception request.",
|
|
316
|
+
stepOrder: 2,
|
|
317
|
+
type: "APPROVAL",
|
|
318
|
+
requiredRoles: ["compliance"],
|
|
319
|
+
createdAt: "2026-03-15T07:40:00.000Z"
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
];
|
|
324
|
+
const instances = [
|
|
325
|
+
{
|
|
326
|
+
id: "wfinst_expense_open",
|
|
327
|
+
definitionId: "wf_expense",
|
|
328
|
+
status: "IN_PROGRESS",
|
|
329
|
+
currentStepId: "wfstep_expense_finance",
|
|
330
|
+
data: { amount: 4200, currency: "EUR", vendor: "Nimbus AI" },
|
|
331
|
+
requestedBy: "sarah@contractspec.io",
|
|
332
|
+
startedAt: "2026-03-20T08:00:00.000Z",
|
|
333
|
+
completedAt: null,
|
|
334
|
+
approvals: [
|
|
335
|
+
{
|
|
336
|
+
id: "wfappr_expense_manager",
|
|
337
|
+
stepId: "wfstep_expense_manager",
|
|
338
|
+
status: "APPROVED",
|
|
339
|
+
actorId: "manager.demo",
|
|
340
|
+
comment: "Approved for the Q2 automation budget.",
|
|
341
|
+
decidedAt: "2026-03-20T08:15:00.000Z",
|
|
342
|
+
createdAt: "2026-03-20T08:05:00.000Z"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: "wfappr_expense_finance",
|
|
346
|
+
stepId: "wfstep_expense_finance",
|
|
347
|
+
status: "PENDING",
|
|
348
|
+
actorId: null,
|
|
349
|
+
comment: null,
|
|
350
|
+
decidedAt: null,
|
|
351
|
+
createdAt: "2026-03-20T08:15:00.000Z"
|
|
352
|
+
}
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
id: "wfinst_vendor_done",
|
|
357
|
+
definitionId: "wf_vendor",
|
|
358
|
+
status: "COMPLETED",
|
|
359
|
+
currentStepId: null,
|
|
360
|
+
data: { vendor: "Acme Cloud", riskTier: "medium" },
|
|
361
|
+
requestedBy: "leo@contractspec.io",
|
|
362
|
+
startedAt: "2026-03-19T09:30:00.000Z",
|
|
363
|
+
completedAt: "2026-03-19T13:10:00.000Z",
|
|
364
|
+
approvals: [
|
|
365
|
+
{
|
|
366
|
+
id: "wfappr_vendor_security",
|
|
367
|
+
stepId: "wfstep_vendor_security",
|
|
368
|
+
status: "APPROVED",
|
|
369
|
+
actorId: "security.demo",
|
|
370
|
+
comment: "SOC2 scope is acceptable.",
|
|
371
|
+
decidedAt: "2026-03-19T10:10:00.000Z",
|
|
372
|
+
createdAt: "2026-03-19T09:35:00.000Z"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
id: "wfappr_vendor_procurement",
|
|
376
|
+
stepId: "wfstep_vendor_procurement",
|
|
377
|
+
status: "APPROVED",
|
|
378
|
+
actorId: "procurement.demo",
|
|
379
|
+
comment: "Commercial terms match the preferred vendor tier.",
|
|
380
|
+
decidedAt: "2026-03-19T11:25:00.000Z",
|
|
381
|
+
createdAt: "2026-03-19T10:15:00.000Z"
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
id: "wfappr_vendor_legal",
|
|
385
|
+
stepId: "wfstep_vendor_legal",
|
|
386
|
+
status: "APPROVED",
|
|
387
|
+
actorId: "legal.demo",
|
|
388
|
+
comment: "MSA redlines are complete.",
|
|
389
|
+
decidedAt: "2026-03-19T13:05:00.000Z",
|
|
390
|
+
createdAt: "2026-03-19T11:30:00.000Z"
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: "wfinst_policy_rejected",
|
|
396
|
+
definitionId: "wf_policy_exception",
|
|
397
|
+
status: "REJECTED",
|
|
398
|
+
currentStepId: "wfstep_policy_compliance",
|
|
399
|
+
data: { policy: "Model rollout freeze", durationDays: 14 },
|
|
400
|
+
requestedBy: "maya@contractspec.io",
|
|
401
|
+
startedAt: "2026-03-18T10:00:00.000Z",
|
|
402
|
+
completedAt: "2026-03-18T11:20:00.000Z",
|
|
403
|
+
approvals: [
|
|
404
|
+
{
|
|
405
|
+
id: "wfappr_policy_lead",
|
|
406
|
+
stepId: "wfstep_policy_lead",
|
|
407
|
+
status: "APPROVED",
|
|
408
|
+
actorId: "lead.demo",
|
|
409
|
+
comment: "Escalation justified for the release train.",
|
|
410
|
+
decidedAt: "2026-03-18T10:30:00.000Z",
|
|
411
|
+
createdAt: "2026-03-18T10:05:00.000Z"
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
id: "wfappr_policy_compliance",
|
|
415
|
+
stepId: "wfstep_policy_compliance",
|
|
416
|
+
status: "REJECTED",
|
|
417
|
+
actorId: "compliance.demo",
|
|
418
|
+
comment: "Exception exceeds the allowed blast radius.",
|
|
419
|
+
decidedAt: "2026-03-18T11:15:00.000Z",
|
|
420
|
+
createdAt: "2026-03-18T10:35:00.000Z"
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
}
|
|
424
|
+
];
|
|
425
|
+
for (const definition of definitions) {
|
|
426
|
+
await db.execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status, "createdAt", "updatedAt")
|
|
427
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
428
|
+
definition.id,
|
|
429
|
+
projectId,
|
|
430
|
+
"org_demo",
|
|
431
|
+
definition.name,
|
|
432
|
+
definition.description,
|
|
433
|
+
definition.type,
|
|
434
|
+
definition.status,
|
|
435
|
+
definition.createdAt,
|
|
436
|
+
definition.updatedAt
|
|
437
|
+
]);
|
|
438
|
+
for (const step of definition.steps) {
|
|
439
|
+
await db.execute(`INSERT INTO workflow_step (id, "definitionId", name, description, "stepOrder", type, "requiredRoles", "createdAt")
|
|
440
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
|
|
441
|
+
step.id,
|
|
442
|
+
definition.id,
|
|
443
|
+
step.name,
|
|
444
|
+
step.description,
|
|
445
|
+
step.stepOrder,
|
|
446
|
+
step.type,
|
|
447
|
+
JSON.stringify(step.requiredRoles),
|
|
448
|
+
step.createdAt
|
|
449
|
+
]);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
for (const instance of instances) {
|
|
453
|
+
await db.execute(`INSERT INTO workflow_instance (id, "projectId", "definitionId", status, "currentStepId", data, "requestedBy", "startedAt", "completedAt")
|
|
454
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
455
|
+
instance.id,
|
|
456
|
+
projectId,
|
|
457
|
+
instance.definitionId,
|
|
458
|
+
instance.status,
|
|
459
|
+
instance.currentStepId,
|
|
460
|
+
JSON.stringify(instance.data),
|
|
461
|
+
instance.requestedBy,
|
|
462
|
+
instance.startedAt,
|
|
463
|
+
instance.completedAt
|
|
464
|
+
]);
|
|
465
|
+
for (const approval of instance.approvals) {
|
|
466
|
+
await db.execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "actorId", comment, "decidedAt", "createdAt")
|
|
467
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
|
|
468
|
+
approval.id,
|
|
469
|
+
instance.id,
|
|
470
|
+
approval.stepId,
|
|
471
|
+
approval.status,
|
|
472
|
+
approval.actorId,
|
|
473
|
+
approval.comment,
|
|
474
|
+
approval.decidedAt,
|
|
475
|
+
approval.createdAt
|
|
476
|
+
]);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
236
479
|
}
|
|
237
480
|
async function seedMarketplace(params) {
|
|
238
481
|
const { projectId, db } = params;
|
|
@@ -1697,116 +1940,6 @@ var psaReviewTask = pgTable("psa_review_task", {
|
|
|
1697
1940
|
decidedAt: text("decidedAt"),
|
|
1698
1941
|
decidedBy: text("decidedBy")
|
|
1699
1942
|
});
|
|
1700
|
-
// src/web/storage/indexeddb.ts
|
|
1701
|
-
var DEFAULT_DB_NAME = "contractspec-runtime";
|
|
1702
|
-
var DEFAULT_STORE = "kv";
|
|
1703
|
-
var FALLBACK_STORE = new Map;
|
|
1704
|
-
|
|
1705
|
-
class LocalStorageService {
|
|
1706
|
-
options;
|
|
1707
|
-
dbPromise;
|
|
1708
|
-
constructor(options = {}) {
|
|
1709
|
-
this.options = options;
|
|
1710
|
-
}
|
|
1711
|
-
async init() {
|
|
1712
|
-
await this.getDb();
|
|
1713
|
-
}
|
|
1714
|
-
async get(key, fallback) {
|
|
1715
|
-
const db = await this.getDb();
|
|
1716
|
-
if (!db) {
|
|
1717
|
-
return FALLBACK_STORE.get(key) ?? fallback;
|
|
1718
|
-
}
|
|
1719
|
-
return new Promise((resolve, reject) => {
|
|
1720
|
-
const tx = db.transaction(this.storeName, "readonly");
|
|
1721
|
-
const store = tx.objectStore(this.storeName);
|
|
1722
|
-
const request = store.get(key);
|
|
1723
|
-
request.onsuccess = () => {
|
|
1724
|
-
resolve(request.result ?? fallback);
|
|
1725
|
-
};
|
|
1726
|
-
request.onerror = () => reject(request.error);
|
|
1727
|
-
});
|
|
1728
|
-
}
|
|
1729
|
-
async set(key, value) {
|
|
1730
|
-
const db = await this.getDb();
|
|
1731
|
-
if (!db) {
|
|
1732
|
-
FALLBACK_STORE.set(key, value);
|
|
1733
|
-
return;
|
|
1734
|
-
}
|
|
1735
|
-
await new Promise((resolve, reject) => {
|
|
1736
|
-
const tx = db.transaction(this.storeName, "readwrite");
|
|
1737
|
-
const store = tx.objectStore(this.storeName);
|
|
1738
|
-
const request = store.put(value, key);
|
|
1739
|
-
request.onsuccess = () => resolve();
|
|
1740
|
-
request.onerror = () => reject(request.error);
|
|
1741
|
-
});
|
|
1742
|
-
}
|
|
1743
|
-
async delete(key) {
|
|
1744
|
-
const db = await this.getDb();
|
|
1745
|
-
if (!db) {
|
|
1746
|
-
FALLBACK_STORE.delete(key);
|
|
1747
|
-
return;
|
|
1748
|
-
}
|
|
1749
|
-
await new Promise((resolve, reject) => {
|
|
1750
|
-
const tx = db.transaction(this.storeName, "readwrite");
|
|
1751
|
-
const store = tx.objectStore(this.storeName);
|
|
1752
|
-
const request = store.delete(key);
|
|
1753
|
-
request.onsuccess = () => resolve();
|
|
1754
|
-
request.onerror = () => reject(request.error);
|
|
1755
|
-
});
|
|
1756
|
-
}
|
|
1757
|
-
async clear() {
|
|
1758
|
-
const db = await this.getDb();
|
|
1759
|
-
if (!db) {
|
|
1760
|
-
FALLBACK_STORE.clear();
|
|
1761
|
-
return;
|
|
1762
|
-
}
|
|
1763
|
-
await new Promise((resolve, reject) => {
|
|
1764
|
-
const tx = db.transaction(this.storeName, "readwrite");
|
|
1765
|
-
const store = tx.objectStore(this.storeName);
|
|
1766
|
-
const request = store.clear();
|
|
1767
|
-
request.onsuccess = () => resolve();
|
|
1768
|
-
request.onerror = () => reject(request.error);
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
get storeName() {
|
|
1772
|
-
return this.options.storeName ?? DEFAULT_STORE;
|
|
1773
|
-
}
|
|
1774
|
-
async getDb() {
|
|
1775
|
-
if (typeof indexedDB === "undefined") {
|
|
1776
|
-
return null;
|
|
1777
|
-
}
|
|
1778
|
-
if (!this.dbPromise) {
|
|
1779
|
-
this.dbPromise = this.openDb();
|
|
1780
|
-
}
|
|
1781
|
-
return this.dbPromise;
|
|
1782
|
-
}
|
|
1783
|
-
openDb() {
|
|
1784
|
-
return new Promise((resolve, reject) => {
|
|
1785
|
-
const request = indexedDB.open(this.options.dbName ?? DEFAULT_DB_NAME, this.options.version ?? 1);
|
|
1786
|
-
request.onerror = () => reject(request.error);
|
|
1787
|
-
request.onsuccess = () => {
|
|
1788
|
-
resolve(request.result);
|
|
1789
|
-
};
|
|
1790
|
-
request.onupgradeneeded = () => {
|
|
1791
|
-
const db = request.result;
|
|
1792
|
-
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
1793
|
-
db.createObjectStore(this.storeName);
|
|
1794
|
-
}
|
|
1795
|
-
};
|
|
1796
|
-
});
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
// src/web/utils/id.ts
|
|
1800
|
-
function generateId(prefix) {
|
|
1801
|
-
const base = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2, 10);
|
|
1802
|
-
return prefix ? `${prefix}_${base}` : base;
|
|
1803
|
-
}
|
|
1804
|
-
// src/web/graphql/local-client.ts
|
|
1805
|
-
import { ApolloClient, InMemoryCache } from "@apollo/client";
|
|
1806
|
-
import { SchemaLink } from "@apollo/client/link/schema";
|
|
1807
|
-
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
1808
|
-
import { GraphQLScalarType, Kind } from "graphql";
|
|
1809
|
-
|
|
1810
1943
|
// src/web/events/local-pubsub.ts
|
|
1811
1944
|
class LocalEventBus {
|
|
1812
1945
|
listeners = new Map;
|
|
@@ -1830,6 +1963,17 @@ class LocalEventBus {
|
|
|
1830
1963
|
};
|
|
1831
1964
|
}
|
|
1832
1965
|
}
|
|
1966
|
+
// src/web/graphql/local-client.ts
|
|
1967
|
+
import { ApolloClient, InMemoryCache } from "@apollo/client";
|
|
1968
|
+
import { SchemaLink } from "@apollo/client/link/schema";
|
|
1969
|
+
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
1970
|
+
import { GraphQLScalarType, Kind } from "graphql";
|
|
1971
|
+
|
|
1972
|
+
// src/web/utils/id.ts
|
|
1973
|
+
function generateId(prefix) {
|
|
1974
|
+
const base = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2, 10);
|
|
1975
|
+
return prefix ? `${prefix}_${base}` : base;
|
|
1976
|
+
}
|
|
1833
1977
|
|
|
1834
1978
|
// src/web/graphql/local-client.ts
|
|
1835
1979
|
var typeDefs = `
|
|
@@ -2376,6 +2520,106 @@ function mapRecipeInstruction(row, locale) {
|
|
|
2376
2520
|
ordering: row.ordering ?? 0
|
|
2377
2521
|
};
|
|
2378
2522
|
}
|
|
2523
|
+
// src/web/storage/indexeddb.ts
|
|
2524
|
+
var DEFAULT_DB_NAME = "contractspec-runtime";
|
|
2525
|
+
var DEFAULT_STORE = "kv";
|
|
2526
|
+
var FALLBACK_STORE = new Map;
|
|
2527
|
+
|
|
2528
|
+
class LocalStorageService {
|
|
2529
|
+
options;
|
|
2530
|
+
dbPromise;
|
|
2531
|
+
constructor(options = {}) {
|
|
2532
|
+
this.options = options;
|
|
2533
|
+
}
|
|
2534
|
+
async init() {
|
|
2535
|
+
await this.getDb();
|
|
2536
|
+
}
|
|
2537
|
+
async get(key, fallback) {
|
|
2538
|
+
const db = await this.getDb();
|
|
2539
|
+
if (!db) {
|
|
2540
|
+
return FALLBACK_STORE.get(key) ?? fallback;
|
|
2541
|
+
}
|
|
2542
|
+
return new Promise((resolve, reject) => {
|
|
2543
|
+
const tx = db.transaction(this.storeName, "readonly");
|
|
2544
|
+
const store = tx.objectStore(this.storeName);
|
|
2545
|
+
const request = store.get(key);
|
|
2546
|
+
request.onsuccess = () => {
|
|
2547
|
+
resolve(request.result ?? fallback);
|
|
2548
|
+
};
|
|
2549
|
+
request.onerror = () => reject(request.error);
|
|
2550
|
+
});
|
|
2551
|
+
}
|
|
2552
|
+
async set(key, value) {
|
|
2553
|
+
const db = await this.getDb();
|
|
2554
|
+
if (!db) {
|
|
2555
|
+
FALLBACK_STORE.set(key, value);
|
|
2556
|
+
return;
|
|
2557
|
+
}
|
|
2558
|
+
await new Promise((resolve, reject) => {
|
|
2559
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
2560
|
+
const store = tx.objectStore(this.storeName);
|
|
2561
|
+
const request = store.put(value, key);
|
|
2562
|
+
request.onsuccess = () => resolve();
|
|
2563
|
+
request.onerror = () => reject(request.error);
|
|
2564
|
+
});
|
|
2565
|
+
}
|
|
2566
|
+
async delete(key) {
|
|
2567
|
+
const db = await this.getDb();
|
|
2568
|
+
if (!db) {
|
|
2569
|
+
FALLBACK_STORE.delete(key);
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
await new Promise((resolve, reject) => {
|
|
2573
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
2574
|
+
const store = tx.objectStore(this.storeName);
|
|
2575
|
+
const request = store.delete(key);
|
|
2576
|
+
request.onsuccess = () => resolve();
|
|
2577
|
+
request.onerror = () => reject(request.error);
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
async clear() {
|
|
2581
|
+
const db = await this.getDb();
|
|
2582
|
+
if (!db) {
|
|
2583
|
+
FALLBACK_STORE.clear();
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
await new Promise((resolve, reject) => {
|
|
2587
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
2588
|
+
const store = tx.objectStore(this.storeName);
|
|
2589
|
+
const request = store.clear();
|
|
2590
|
+
request.onsuccess = () => resolve();
|
|
2591
|
+
request.onerror = () => reject(request.error);
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
get storeName() {
|
|
2595
|
+
return this.options.storeName ?? DEFAULT_STORE;
|
|
2596
|
+
}
|
|
2597
|
+
async getDb() {
|
|
2598
|
+
if (typeof indexedDB === "undefined") {
|
|
2599
|
+
return null;
|
|
2600
|
+
}
|
|
2601
|
+
if (!this.dbPromise) {
|
|
2602
|
+
this.dbPromise = this.openDb();
|
|
2603
|
+
}
|
|
2604
|
+
return this.dbPromise;
|
|
2605
|
+
}
|
|
2606
|
+
openDb() {
|
|
2607
|
+
return new Promise((resolve, reject) => {
|
|
2608
|
+
const request = indexedDB.open(this.options.dbName ?? DEFAULT_DB_NAME, this.options.version ?? 1);
|
|
2609
|
+
request.onerror = () => reject(request.error);
|
|
2610
|
+
request.onsuccess = () => {
|
|
2611
|
+
resolve(request.result);
|
|
2612
|
+
};
|
|
2613
|
+
request.onupgradeneeded = () => {
|
|
2614
|
+
const db = request.result;
|
|
2615
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
2616
|
+
db.createObjectStore(this.storeName);
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2379
2623
|
// src/web/runtime/services.ts
|
|
2380
2624
|
var DEFAULT_PROJECT_ID = "local-project";
|
|
2381
2625
|
|