@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/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
- await db.execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status)
227
- VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
228
- "wf_1",
229
- projectId,
230
- "org_demo",
231
- "Approval Workflow",
232
- "Demo approval workflow",
233
- "APPROVAL",
234
- "ACTIVE"
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