@contractspec/lib.runtime-sandbox 0.11.0

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.
Files changed (63) hide show
  1. package/dist/_virtual/rolldown_runtime.js +18 -0
  2. package/dist/adapters/pglite/adapter.js +97 -0
  3. package/dist/adapters/pglite/adapter.js.map +1 -0
  4. package/dist/adapters/pglite/index.js +3 -0
  5. package/dist/index.d.ts +23 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +24 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/ports/database.port.d.ts +70 -0
  10. package/dist/ports/database.port.d.ts.map +1 -0
  11. package/dist/types/database.types.d.ts +47 -0
  12. package/dist/types/database.types.d.ts.map +1 -0
  13. package/dist/web/database/migrations.d.ts +12 -0
  14. package/dist/web/database/migrations.d.ts.map +1 -0
  15. package/dist/web/database/migrations.js +746 -0
  16. package/dist/web/database/migrations.js.map +1 -0
  17. package/dist/web/database/schema.d.ts +7349 -0
  18. package/dist/web/database/schema.d.ts.map +1 -0
  19. package/dist/web/database/schema.js +528 -0
  20. package/dist/web/database/schema.js.map +1 -0
  21. package/dist/web/events/local-pubsub.d.ts +10 -0
  22. package/dist/web/events/local-pubsub.d.ts.map +1 -0
  23. package/dist/web/events/local-pubsub.js +24 -0
  24. package/dist/web/events/local-pubsub.js.map +1 -0
  25. package/dist/web/graphql/local-client.d.ts +20 -0
  26. package/dist/web/graphql/local-client.d.ts.map +1 -0
  27. package/dist/web/graphql/local-client.js +536 -0
  28. package/dist/web/graphql/local-client.js.map +1 -0
  29. package/dist/web/index.d.ts +15 -0
  30. package/dist/web/index.d.ts.map +1 -0
  31. package/dist/web/index.js +68 -0
  32. package/dist/web/index.js.map +1 -0
  33. package/dist/web/runtime/seeders/index.js +358 -0
  34. package/dist/web/runtime/seeders/index.js.map +1 -0
  35. package/dist/web/runtime/services.d.ts +60 -0
  36. package/dist/web/runtime/services.d.ts.map +1 -0
  37. package/dist/web/runtime/services.js +80 -0
  38. package/dist/web/runtime/services.js.map +1 -0
  39. package/dist/web/storage/indexeddb.d.ts +22 -0
  40. package/dist/web/storage/indexeddb.d.ts.map +1 -0
  41. package/dist/web/storage/indexeddb.js +85 -0
  42. package/dist/web/storage/indexeddb.js.map +1 -0
  43. package/dist/web/utils/id.d.ts +5 -0
  44. package/dist/web/utils/id.d.ts.map +1 -0
  45. package/dist/web/utils/id.js +9 -0
  46. package/dist/web/utils/id.js.map +1 -0
  47. package/package.json +70 -0
  48. package/src/adapters/pglite/adapter.ts +152 -0
  49. package/src/adapters/pglite/index.ts +1 -0
  50. package/src/index.ts +41 -0
  51. package/src/ports/database.port.ts +82 -0
  52. package/src/ports/index.ts +4 -0
  53. package/src/types/database.types.ts +55 -0
  54. package/src/types/index.ts +1 -0
  55. package/src/web/database/migrations.ts +760 -0
  56. package/src/web/database/schema.ts +596 -0
  57. package/src/web/events/local-pubsub.ts +28 -0
  58. package/src/web/graphql/local-client.ts +747 -0
  59. package/src/web/index.ts +21 -0
  60. package/src/web/runtime/seeders/index.ts +449 -0
  61. package/src/web/runtime/services.ts +132 -0
  62. package/src/web/storage/indexeddb.ts +116 -0
  63. package/src/web/utils/id.ts +7 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/web/index.ts"],"sourcesContent":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { __exportAll } from "../_virtual/rolldown_runtime.js";
2
+ import { SANDBOX_MIGRATIONS } from "./database/migrations.js";
3
+ import { agentDefinition, agentRun, agentRunLog, agentRunStep, agentTool, agentToolAssignment, analyticsDashboard, analyticsQuery, analyticsWidget, crmCompany, crmContact, crmDeal, crmPipeline, crmStage, integration, integrationConnection, integrationFieldMapping, integrationSyncConfig, marketplaceOrder, marketplaceOrderItem, marketplacePayout, marketplaceProduct, marketplaceReview, marketplaceStore, psaChangeCandidate, psaReviewTask, psaRule, psaRuleVersion, psaSnapshot, psaUserContext, saasProject, saasSubscription, saasUsage, templateConversation, templateConversationParticipant, templateMessage, templateRecipe, templateRecipeCategory, templateRecipeIngredient, templateRecipeInstruction, templateTask, templateTaskCategory, workflowApproval, workflowDefinition, workflowInstance, workflowStep } from "./database/schema.js";
4
+ import { LocalStorageService } from "./storage/indexeddb.js";
5
+ import { generateId } from "./utils/id.js";
6
+ import { LocalEventBus } from "./events/local-pubsub.js";
7
+ import { LocalGraphQLClient } from "./graphql/local-client.js";
8
+ import { LocalRuntimeServices } from "./runtime/services.js";
9
+
10
+ //#region src/web/index.ts
11
+ var web_exports = /* @__PURE__ */ __exportAll({
12
+ LocalEventBus: () => LocalEventBus,
13
+ LocalGraphQLClient: () => LocalGraphQLClient,
14
+ LocalRuntimeServices: () => LocalRuntimeServices,
15
+ LocalStorageService: () => LocalStorageService,
16
+ SANDBOX_MIGRATIONS: () => SANDBOX_MIGRATIONS,
17
+ agentDefinition: () => agentDefinition,
18
+ agentRun: () => agentRun,
19
+ agentRunLog: () => agentRunLog,
20
+ agentRunStep: () => agentRunStep,
21
+ agentTool: () => agentTool,
22
+ agentToolAssignment: () => agentToolAssignment,
23
+ analyticsDashboard: () => analyticsDashboard,
24
+ analyticsQuery: () => analyticsQuery,
25
+ analyticsWidget: () => analyticsWidget,
26
+ crmCompany: () => crmCompany,
27
+ crmContact: () => crmContact,
28
+ crmDeal: () => crmDeal,
29
+ crmPipeline: () => crmPipeline,
30
+ crmStage: () => crmStage,
31
+ generateId: () => generateId,
32
+ integration: () => integration,
33
+ integrationConnection: () => integrationConnection,
34
+ integrationFieldMapping: () => integrationFieldMapping,
35
+ integrationSyncConfig: () => integrationSyncConfig,
36
+ marketplaceOrder: () => marketplaceOrder,
37
+ marketplaceOrderItem: () => marketplaceOrderItem,
38
+ marketplacePayout: () => marketplacePayout,
39
+ marketplaceProduct: () => marketplaceProduct,
40
+ marketplaceReview: () => marketplaceReview,
41
+ marketplaceStore: () => marketplaceStore,
42
+ psaChangeCandidate: () => psaChangeCandidate,
43
+ psaReviewTask: () => psaReviewTask,
44
+ psaRule: () => psaRule,
45
+ psaRuleVersion: () => psaRuleVersion,
46
+ psaSnapshot: () => psaSnapshot,
47
+ psaUserContext: () => psaUserContext,
48
+ saasProject: () => saasProject,
49
+ saasSubscription: () => saasSubscription,
50
+ saasUsage: () => saasUsage,
51
+ templateConversation: () => templateConversation,
52
+ templateConversationParticipant: () => templateConversationParticipant,
53
+ templateMessage: () => templateMessage,
54
+ templateRecipe: () => templateRecipe,
55
+ templateRecipeCategory: () => templateRecipeCategory,
56
+ templateRecipeIngredient: () => templateRecipeIngredient,
57
+ templateRecipeInstruction: () => templateRecipeInstruction,
58
+ templateTask: () => templateTask,
59
+ templateTaskCategory: () => templateTaskCategory,
60
+ workflowApproval: () => workflowApproval,
61
+ workflowDefinition: () => workflowDefinition,
62
+ workflowInstance: () => workflowInstance,
63
+ workflowStep: () => workflowStep
64
+ });
65
+
66
+ //#endregion
67
+ export { web_exports };
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/web/index.ts"],"sourcesContent":["// Re-export database schema and types\nexport * from './database/migrations';\nexport * from './database/schema';\n\n// Storage\nexport * from './storage/indexeddb';\n\n// Utils\nexport * from './utils/id';\n\n// GraphQL client\nexport * from './graphql/local-client';\n\n// Event bus\nexport * from './events/local-pubsub';\n\n// Runtime services\nexport * from './runtime/services';\n\n// Handlers (Deprecated/Removed)\n// export * from './handlers';\n"],"mappings":""}
@@ -0,0 +1,358 @@
1
+ //#region src/web/runtime/seeders/index.ts
2
+ /**
3
+ * Seed the database with template-specific data.
4
+ *
5
+ * Unknown templates are a no-op (safe default).
6
+ */
7
+ async function seedTemplate(params) {
8
+ const { templateId, projectId, db } = params;
9
+ switch (templateId) {
10
+ case "todos-app": return seedTodos({
11
+ projectId,
12
+ db
13
+ });
14
+ case "messaging-app": return seedMessaging({
15
+ projectId,
16
+ db
17
+ });
18
+ case "recipe-app-i18n": return seedRecipes({
19
+ projectId,
20
+ db
21
+ });
22
+ case "crm-pipeline": return seedCrmPipeline({
23
+ projectId,
24
+ db
25
+ });
26
+ case "saas-boilerplate": return seedSaasBoilerplate({
27
+ projectId,
28
+ db
29
+ });
30
+ case "agent-console": return seedAgentConsole({
31
+ projectId,
32
+ db
33
+ });
34
+ case "workflow-system": return seedWorkflowSystem({
35
+ projectId,
36
+ db
37
+ });
38
+ case "marketplace": return seedMarketplace({
39
+ projectId,
40
+ db
41
+ });
42
+ case "integration-hub": return seedIntegrationHub({
43
+ projectId,
44
+ db
45
+ });
46
+ case "analytics-dashboard": return seedAnalyticsDashboard({
47
+ projectId,
48
+ db
49
+ });
50
+ case "policy-safe-knowledge-assistant": return seedPolicyKnowledgeAssistant({
51
+ projectId,
52
+ db
53
+ });
54
+ default: return;
55
+ }
56
+ }
57
+ async function seedTodos(params) {
58
+ const { projectId, db } = params;
59
+ if ((await db.query(`SELECT COUNT(*) as count FROM template_task WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
60
+ const workCategoryId = "todo_category_ops";
61
+ const homeCategoryId = "todo_category_home";
62
+ await db.execute(`INSERT INTO template_task_category (id, "projectId", name, color) VALUES ($1, $2, $3, $4)`, [
63
+ workCategoryId,
64
+ projectId,
65
+ "Operations",
66
+ "#8b5cf6"
67
+ ]);
68
+ await db.execute(`INSERT INTO template_task_category (id, "projectId", name, color) VALUES ($1, $2, $3, $4)`, [
69
+ homeCategoryId,
70
+ projectId,
71
+ "Home",
72
+ "#f472b6"
73
+ ]);
74
+ const tasks = [
75
+ {
76
+ id: "todo_task_1",
77
+ title: "Review intent signals",
78
+ description: "Scan yesterday's signals and flag the ones to promote.",
79
+ categoryId: workCategoryId,
80
+ priority: "HIGH"
81
+ },
82
+ {
83
+ id: "todo_task_2",
84
+ title: "Schedule studio walkthrough",
85
+ description: "Prep the sandbox before tomorrow's ceremony.",
86
+ categoryId: workCategoryId,
87
+ priority: "MEDIUM"
88
+ },
89
+ {
90
+ id: "todo_task_3",
91
+ title: "Collect testimonials",
92
+ description: "Ask last week's pilot crew for quotes.",
93
+ categoryId: homeCategoryId,
94
+ priority: "LOW"
95
+ }
96
+ ];
97
+ for (const task of tasks) await db.execute(`INSERT INTO template_task (id, "projectId", "categoryId", title, description, completed, priority, tags)
98
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
99
+ task.id,
100
+ projectId,
101
+ task.categoryId,
102
+ task.title,
103
+ task.description,
104
+ 0,
105
+ task.priority,
106
+ JSON.stringify(["demo"])
107
+ ]);
108
+ }
109
+ async function seedMessaging(params) {
110
+ const { projectId, db } = params;
111
+ if ((await db.query(`SELECT COUNT(*) as count FROM template_conversation WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
112
+ const conversationId = "conv_demo_1";
113
+ const now = (/* @__PURE__ */ new Date()).toISOString();
114
+ await db.execute(`INSERT INTO template_conversation (id, "projectId", name, "isGroup", "avatarUrl", "updatedAt")
115
+ VALUES ($1, $2, $3, $4, $5, $6)`, [
116
+ conversationId,
117
+ projectId,
118
+ "Team Chat",
119
+ 1,
120
+ null,
121
+ now
122
+ ]);
123
+ await db.execute(`INSERT INTO template_conversation_participant (id, "conversationId", "projectId", "userId", "displayName", role, "joinedAt")
124
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
125
+ "part_1",
126
+ conversationId,
127
+ projectId,
128
+ "user_alice",
129
+ "Alice",
130
+ "member",
131
+ now
132
+ ]);
133
+ await db.execute(`INSERT INTO template_conversation_participant (id, "conversationId", "projectId", "userId", "displayName", role, "joinedAt")
134
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
135
+ "part_2",
136
+ conversationId,
137
+ projectId,
138
+ "user_bob",
139
+ "Bob",
140
+ "member",
141
+ now
142
+ ]);
143
+ await db.execute(`INSERT INTO template_message (id, "conversationId", "projectId", "senderId", "senderName", content, attachments, status, "createdAt", "updatedAt")
144
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
145
+ "msg_1",
146
+ conversationId,
147
+ projectId,
148
+ "user_alice",
149
+ "Alice",
150
+ "Hey team! Ready for the demo?",
151
+ JSON.stringify([]),
152
+ "DELIVERED",
153
+ now,
154
+ now
155
+ ]);
156
+ }
157
+ async function seedRecipes(params) {
158
+ const { projectId, db } = params;
159
+ if ((await db.query(`SELECT COUNT(*) as count FROM template_recipe WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
160
+ await db.execute(`INSERT INTO template_recipe_category (id, "nameEn", "nameFr", icon) VALUES ($1, $2, $3, $4)`, [
161
+ "cat_main",
162
+ "Main Courses",
163
+ "Plats Principaux",
164
+ "🍽️"
165
+ ]);
166
+ const recipeId = "recipe_1";
167
+ await db.execute(`INSERT INTO template_recipe (id, "projectId", "categoryId", "slugEn", "slugFr", "nameEn", "nameFr", "descriptionEn", "descriptionFr", "prepTimeMinutes", "cookTimeMinutes", servings, "isFavorite")
168
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`, [
169
+ recipeId,
170
+ projectId,
171
+ "cat_main",
172
+ "grilled-salmon",
173
+ "saumon-grille",
174
+ "Grilled Salmon",
175
+ "Saumon Grillé",
176
+ "Delicious grilled salmon with lemon",
177
+ "Délicieux saumon grillé au citron",
178
+ 10,
179
+ 15,
180
+ 4,
181
+ 0
182
+ ]);
183
+ await db.execute(`INSERT INTO template_recipe_ingredient (id, "recipeId", "nameEn", "nameFr", quantity, ordering)
184
+ VALUES ($1, $2, $3, $4, $5, $6)`, [
185
+ "ing_1",
186
+ recipeId,
187
+ "Salmon fillet",
188
+ "Filet de saumon",
189
+ "4 pieces",
190
+ 1
191
+ ]);
192
+ await db.execute(`INSERT INTO template_recipe_ingredient (id, "recipeId", "nameEn", "nameFr", quantity, ordering)
193
+ VALUES ($1, $2, $3, $4, $5, $6)`, [
194
+ "ing_2",
195
+ recipeId,
196
+ "Lemon",
197
+ "Citron",
198
+ "1 whole",
199
+ 2
200
+ ]);
201
+ await db.execute(`INSERT INTO template_recipe_instruction (id, "recipeId", "contentEn", "contentFr", ordering)
202
+ VALUES ($1, $2, $3, $4, $5)`, [
203
+ "inst_1",
204
+ recipeId,
205
+ "Preheat grill to medium-high heat.",
206
+ "Préchauffer le grill à feu moyen-vif.",
207
+ 1
208
+ ]);
209
+ await db.execute(`INSERT INTO template_recipe_instruction (id, "recipeId", "contentEn", "contentFr", ordering)
210
+ VALUES ($1, $2, $3, $4, $5)`, [
211
+ "inst_2",
212
+ recipeId,
213
+ "Season salmon and grill for 4-5 minutes per side.",
214
+ "Assaisonner le saumon et griller 4-5 minutes de chaque côté.",
215
+ 2
216
+ ]);
217
+ }
218
+ async function seedCrmPipeline(params) {
219
+ const { projectId, db } = params;
220
+ if ((await db.query(`SELECT COUNT(*) as count FROM crm_pipeline WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
221
+ const pipelineId = "pipeline_sales";
222
+ await db.execute(`INSERT INTO crm_pipeline (id, "projectId", name) VALUES ($1, $2, $3)`, [
223
+ pipelineId,
224
+ projectId,
225
+ "Sales Pipeline"
226
+ ]);
227
+ for (const stage of [
228
+ {
229
+ id: "stage_lead",
230
+ name: "Lead",
231
+ position: 1
232
+ },
233
+ {
234
+ id: "stage_contact",
235
+ name: "Contact Made",
236
+ position: 2
237
+ },
238
+ {
239
+ id: "stage_proposal",
240
+ name: "Proposal",
241
+ position: 3
242
+ },
243
+ {
244
+ id: "stage_negotiation",
245
+ name: "Negotiation",
246
+ position: 4
247
+ },
248
+ {
249
+ id: "stage_closed",
250
+ name: "Closed",
251
+ position: 5
252
+ }
253
+ ]) await db.execute(`INSERT INTO crm_stage (id, "pipelineId", name, position) VALUES ($1, $2, $3, $4)`, [
254
+ stage.id,
255
+ pipelineId,
256
+ stage.name,
257
+ stage.position
258
+ ]);
259
+ }
260
+ async function seedSaasBoilerplate(params) {
261
+ const { projectId, db } = params;
262
+ if ((await db.query(`SELECT COUNT(*) as count FROM saas_project WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
263
+ await db.execute(`INSERT INTO saas_project (id, "projectId", "organizationId", name, description, status, tier)
264
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
265
+ "saas_proj_1",
266
+ projectId,
267
+ "org_demo",
268
+ "Demo Project",
269
+ "A demo SaaS project",
270
+ "ACTIVE",
271
+ "PRO"
272
+ ]);
273
+ }
274
+ async function seedAgentConsole(params) {
275
+ const { projectId, db } = params;
276
+ if ((await db.query(`SELECT COUNT(*) as count FROM agent_definition WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
277
+ await db.execute(`INSERT INTO agent_definition (id, "projectId", "organizationId", name, description, "modelProvider", "modelName", status)
278
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
279
+ "agent_1",
280
+ projectId,
281
+ "org_demo",
282
+ "Demo Agent",
283
+ "A demo AI agent",
284
+ "openai",
285
+ "gpt-4",
286
+ "ACTIVE"
287
+ ]);
288
+ }
289
+ async function seedWorkflowSystem(params) {
290
+ const { projectId, db } = params;
291
+ if ((await db.query(`SELECT COUNT(*) as count FROM workflow_definition WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
292
+ await db.execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status)
293
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
294
+ "wf_1",
295
+ projectId,
296
+ "org_demo",
297
+ "Approval Workflow",
298
+ "Demo approval workflow",
299
+ "APPROVAL",
300
+ "ACTIVE"
301
+ ]);
302
+ }
303
+ async function seedMarketplace(params) {
304
+ const { projectId, db } = params;
305
+ if ((await db.query(`SELECT COUNT(*) as count FROM marketplace_store WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
306
+ await db.execute(`INSERT INTO marketplace_store (id, "projectId", "organizationId", name, description, status)
307
+ VALUES ($1, $2, $3, $4, $5, $6)`, [
308
+ "store_1",
309
+ projectId,
310
+ "org_demo",
311
+ "Demo Store",
312
+ "A demo store",
313
+ "ACTIVE"
314
+ ]);
315
+ }
316
+ async function seedIntegrationHub(params) {
317
+ const { projectId, db } = params;
318
+ if ((await db.query(`SELECT COUNT(*) as count FROM integration WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
319
+ await db.execute(`INSERT INTO integration (id, "projectId", "organizationId", name, description, type, status)
320
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
321
+ "int_1",
322
+ projectId,
323
+ "org_demo",
324
+ "Salesforce",
325
+ "Salesforce CRM integration",
326
+ "CRM",
327
+ "ACTIVE"
328
+ ]);
329
+ }
330
+ async function seedAnalyticsDashboard(params) {
331
+ const { projectId, db } = params;
332
+ if ((await db.query(`SELECT COUNT(*) as count FROM analytics_dashboard WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
333
+ await db.execute(`INSERT INTO analytics_dashboard (id, "projectId", "organizationId", name, slug, description, status)
334
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
335
+ "dash_1",
336
+ projectId,
337
+ "org_demo",
338
+ "Sales Overview",
339
+ "sales-overview",
340
+ "Sales performance dashboard",
341
+ "PUBLISHED"
342
+ ]);
343
+ }
344
+ async function seedPolicyKnowledgeAssistant(params) {
345
+ const { projectId, db } = params;
346
+ if ((await db.query(`SELECT COUNT(*) as count FROM psa_user_context WHERE "projectId" = $1`, [projectId])).rows[0]?.count > 0) return;
347
+ await db.execute(`INSERT INTO psa_user_context ("projectId", locale, jurisdiction, "allowedScope")
348
+ VALUES ($1, $2, $3, $4)`, [
349
+ projectId,
350
+ "en-GB",
351
+ "EU",
352
+ "education_only"
353
+ ]);
354
+ }
355
+
356
+ //#endregion
357
+ export { seedTemplate };
358
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/web/runtime/seeders/index.ts"],"sourcesContent":["/**\n * Seed template data into the sandbox database.\n *\n * Lazy-loads individual seeders to avoid bundle bloat.\n */\nimport type { DatabasePort } from '@contractspec/lib.runtime-sandbox';\nimport type { TemplateId } from '../services';\n\nexport interface SeedTemplateParams {\n templateId: TemplateId;\n projectId: string;\n db: DatabasePort;\n}\n\n/**\n * Seed the database with template-specific data.\n *\n * Unknown templates are a no-op (safe default).\n */\nexport async function seedTemplate(params: SeedTemplateParams): Promise<void> {\n const { templateId, projectId, db } = params;\n\n switch (templateId) {\n case 'todos-app':\n return seedTodos({ projectId, db });\n case 'messaging-app':\n return seedMessaging({ projectId, db });\n case 'recipe-app-i18n':\n return seedRecipes({ projectId, db });\n case 'crm-pipeline':\n return seedCrmPipeline({ projectId, db });\n case 'saas-boilerplate':\n return seedSaasBoilerplate({ projectId, db });\n case 'agent-console':\n return seedAgentConsole({ projectId, db });\n case 'workflow-system':\n return seedWorkflowSystem({ projectId, db });\n case 'marketplace':\n return seedMarketplace({ projectId, db });\n case 'integration-hub':\n return seedIntegrationHub({ projectId, db });\n case 'analytics-dashboard':\n return seedAnalyticsDashboard({ projectId, db });\n case 'policy-safe-knowledge-assistant':\n return seedPolicyKnowledgeAssistant({ projectId, db });\n default:\n // Unknown template - no-op\n return;\n }\n}\n\n// --------------------------------------------------------------------------\n// Individual seeder functions with PostgreSQL syntax\n// --------------------------------------------------------------------------\n\nasync function seedTodos(params: { projectId: string; db: DatabasePort }) {\n const { projectId, db } = params;\n\n // Check if already seeded\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM template_task WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n const workCategoryId = 'todo_category_ops';\n const homeCategoryId = 'todo_category_home';\n\n await db.execute(\n `INSERT INTO template_task_category (id, \"projectId\", name, color) VALUES ($1, $2, $3, $4)`,\n [workCategoryId, projectId, 'Operations', '#8b5cf6']\n );\n await db.execute(\n `INSERT INTO template_task_category (id, \"projectId\", name, color) VALUES ($1, $2, $3, $4)`,\n [homeCategoryId, projectId, 'Home', '#f472b6']\n );\n\n const tasks = [\n {\n id: 'todo_task_1',\n title: 'Review intent signals',\n description: \"Scan yesterday's signals and flag the ones to promote.\",\n categoryId: workCategoryId,\n priority: 'HIGH',\n },\n {\n id: 'todo_task_2',\n title: 'Schedule studio walkthrough',\n description: \"Prep the sandbox before tomorrow's ceremony.\",\n categoryId: workCategoryId,\n priority: 'MEDIUM',\n },\n {\n id: 'todo_task_3',\n title: 'Collect testimonials',\n description: \"Ask last week's pilot crew for quotes.\",\n categoryId: homeCategoryId,\n priority: 'LOW',\n },\n ];\n\n for (const task of tasks) {\n await db.execute(\n `INSERT INTO template_task (id, \"projectId\", \"categoryId\", title, description, completed, priority, tags)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,\n [\n task.id,\n projectId,\n task.categoryId,\n task.title,\n task.description,\n 0,\n task.priority,\n JSON.stringify(['demo']),\n ]\n );\n }\n}\n\nasync function seedMessaging(params: { projectId: string; db: DatabasePort }) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM template_conversation WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n const conversationId = 'conv_demo_1';\n const now = new Date().toISOString();\n\n await db.execute(\n `INSERT INTO template_conversation (id, \"projectId\", name, \"isGroup\", \"avatarUrl\", \"updatedAt\")\n VALUES ($1, $2, $3, $4, $5, $6)`,\n [conversationId, projectId, 'Team Chat', 1, null, now]\n );\n\n // Add participants\n await db.execute(\n `INSERT INTO template_conversation_participant (id, \"conversationId\", \"projectId\", \"userId\", \"displayName\", role, \"joinedAt\")\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n ['part_1', conversationId, projectId, 'user_alice', 'Alice', 'member', now]\n );\n await db.execute(\n `INSERT INTO template_conversation_participant (id, \"conversationId\", \"projectId\", \"userId\", \"displayName\", role, \"joinedAt\")\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n ['part_2', conversationId, projectId, 'user_bob', 'Bob', 'member', now]\n );\n\n // Add messages\n await db.execute(\n `INSERT INTO template_message (id, \"conversationId\", \"projectId\", \"senderId\", \"senderName\", content, attachments, status, \"createdAt\", \"updatedAt\")\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,\n [\n 'msg_1',\n conversationId,\n projectId,\n 'user_alice',\n 'Alice',\n 'Hey team! Ready for the demo?',\n JSON.stringify([]),\n 'DELIVERED',\n now,\n now,\n ]\n );\n}\n\nasync function seedRecipes(params: { projectId: string; db: DatabasePort }) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM template_recipe WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n // Add category\n await db.execute(\n `INSERT INTO template_recipe_category (id, \"nameEn\", \"nameFr\", icon) VALUES ($1, $2, $3, $4)`,\n ['cat_main', 'Main Courses', 'Plats Principaux', '🍽️']\n );\n\n // Add recipe\n const recipeId = 'recipe_1';\n await db.execute(\n `INSERT INTO template_recipe (id, \"projectId\", \"categoryId\", \"slugEn\", \"slugFr\", \"nameEn\", \"nameFr\", \"descriptionEn\", \"descriptionFr\", \"prepTimeMinutes\", \"cookTimeMinutes\", servings, \"isFavorite\")\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`,\n [\n recipeId,\n projectId,\n 'cat_main',\n 'grilled-salmon',\n 'saumon-grille',\n 'Grilled Salmon',\n 'Saumon Grillé',\n 'Delicious grilled salmon with lemon',\n 'Délicieux saumon grillé au citron',\n 10,\n 15,\n 4,\n 0,\n ]\n );\n\n // Add ingredients\n await db.execute(\n `INSERT INTO template_recipe_ingredient (id, \"recipeId\", \"nameEn\", \"nameFr\", quantity, ordering)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n ['ing_1', recipeId, 'Salmon fillet', 'Filet de saumon', '4 pieces', 1]\n );\n await db.execute(\n `INSERT INTO template_recipe_ingredient (id, \"recipeId\", \"nameEn\", \"nameFr\", quantity, ordering)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n ['ing_2', recipeId, 'Lemon', 'Citron', '1 whole', 2]\n );\n\n // Add instructions\n await db.execute(\n `INSERT INTO template_recipe_instruction (id, \"recipeId\", \"contentEn\", \"contentFr\", ordering)\n VALUES ($1, $2, $3, $4, $5)`,\n [\n 'inst_1',\n recipeId,\n 'Preheat grill to medium-high heat.',\n 'Préchauffer le grill à feu moyen-vif.',\n 1,\n ]\n );\n await db.execute(\n `INSERT INTO template_recipe_instruction (id, \"recipeId\", \"contentEn\", \"contentFr\", ordering)\n VALUES ($1, $2, $3, $4, $5)`,\n [\n 'inst_2',\n recipeId,\n 'Season salmon and grill for 4-5 minutes per side.',\n 'Assaisonner le saumon et griller 4-5 minutes de chaque côté.',\n 2,\n ]\n );\n}\n\nasync function seedCrmPipeline(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM crm_pipeline WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n const pipelineId = 'pipeline_sales';\n await db.execute(\n `INSERT INTO crm_pipeline (id, \"projectId\", name) VALUES ($1, $2, $3)`,\n [pipelineId, projectId, 'Sales Pipeline']\n );\n\n const stages = [\n { id: 'stage_lead', name: 'Lead', position: 1 },\n { id: 'stage_contact', name: 'Contact Made', position: 2 },\n { id: 'stage_proposal', name: 'Proposal', position: 3 },\n { id: 'stage_negotiation', name: 'Negotiation', position: 4 },\n { id: 'stage_closed', name: 'Closed', position: 5 },\n ];\n\n for (const stage of stages) {\n await db.execute(\n `INSERT INTO crm_stage (id, \"pipelineId\", name, position) VALUES ($1, $2, $3, $4)`,\n [stage.id, pipelineId, stage.name, stage.position]\n );\n }\n}\n\nasync function seedSaasBoilerplate(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM saas_project WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n await db.execute(\n `INSERT INTO saas_project (id, \"projectId\", \"organizationId\", name, description, status, tier)\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n [\n 'saas_proj_1',\n projectId,\n 'org_demo',\n 'Demo Project',\n 'A demo SaaS project',\n 'ACTIVE',\n 'PRO',\n ]\n );\n}\n\nasync function seedAgentConsole(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM agent_definition WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n await db.execute(\n `INSERT INTO agent_definition (id, \"projectId\", \"organizationId\", name, description, \"modelProvider\", \"modelName\", status)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,\n [\n 'agent_1',\n projectId,\n 'org_demo',\n 'Demo Agent',\n 'A demo AI agent',\n 'openai',\n 'gpt-4',\n 'ACTIVE',\n ]\n );\n}\n\nasync function seedWorkflowSystem(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM workflow_definition WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n await db.execute(\n `INSERT INTO workflow_definition (id, \"projectId\", \"organizationId\", name, description, type, status)\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n [\n 'wf_1',\n projectId,\n 'org_demo',\n 'Approval Workflow',\n 'Demo approval workflow',\n 'APPROVAL',\n 'ACTIVE',\n ]\n );\n}\n\nasync function seedMarketplace(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM marketplace_store WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n await db.execute(\n `INSERT INTO marketplace_store (id, \"projectId\", \"organizationId\", name, description, status)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n ['store_1', projectId, 'org_demo', 'Demo Store', 'A demo store', 'ACTIVE']\n );\n}\n\nasync function seedIntegrationHub(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM integration WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n await db.execute(\n `INSERT INTO integration (id, \"projectId\", \"organizationId\", name, description, type, status)\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n [\n 'int_1',\n projectId,\n 'org_demo',\n 'Salesforce',\n 'Salesforce CRM integration',\n 'CRM',\n 'ACTIVE',\n ]\n );\n}\n\nasync function seedAnalyticsDashboard(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM analytics_dashboard WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n await db.execute(\n `INSERT INTO analytics_dashboard (id, \"projectId\", \"organizationId\", name, slug, description, status)\n VALUES ($1, $2, $3, $4, $5, $6, $7)`,\n [\n 'dash_1',\n projectId,\n 'org_demo',\n 'Sales Overview',\n 'sales-overview',\n 'Sales performance dashboard',\n 'PUBLISHED',\n ]\n );\n}\n\nasync function seedPolicyKnowledgeAssistant(params: {\n projectId: string;\n db: DatabasePort;\n}) {\n const { projectId, db } = params;\n\n const existing = await db.query(\n `SELECT COUNT(*) as count FROM psa_user_context WHERE \"projectId\" = $1`,\n [projectId]\n );\n if ((existing.rows[0]?.count as number) > 0) return;\n\n await db.execute(\n `INSERT INTO psa_user_context (\"projectId\", locale, jurisdiction, \"allowedScope\")\n VALUES ($1, $2, $3, $4)`,\n [projectId, 'en-GB', 'EU', 'education_only']\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import { LocalStorageService } from "../storage/indexeddb.js";
2
+ import { LocalEventBus } from "../events/local-pubsub.js";
3
+ import { LocalGraphQLClient } from "../graphql/local-client.js";
4
+ import { DatabasePort } from "@contractspec/lib.runtime-sandbox";
5
+
6
+ //#region src/web/runtime/services.d.ts
7
+
8
+ type TemplateId = string;
9
+ interface LocalRuntimeInitOptions {
10
+ /**
11
+ * Data directory for IndexedDB persistence (optional).
12
+ * If omitted, uses in-memory database.
13
+ */
14
+ dataDir?: string;
15
+ }
16
+ interface TemplateSeedOptions {
17
+ templateId: TemplateId;
18
+ projectId?: string;
19
+ }
20
+ /**
21
+ * Local runtime services for sandbox environment.
22
+ *
23
+ * Provides lazy-loaded database access via DatabasePort interface.
24
+ */
25
+ declare class LocalRuntimeServices {
26
+ #private;
27
+ readonly storage: LocalStorageService;
28
+ readonly pubsub: LocalEventBus;
29
+ private _db?;
30
+ /**
31
+ * Get the database port (must be initialized first).
32
+ */
33
+ get db(): DatabasePort;
34
+ private _graphql?;
35
+ /**
36
+ * Get the GraphQL client (must be initialized first).
37
+ */
38
+ get graphql(): LocalGraphQLClient;
39
+ /**
40
+ * Initialize the runtime services.
41
+ *
42
+ * Lazy-loads PGLite adapter to avoid bundle bloat.
43
+ */
44
+ init(options?: LocalRuntimeInitOptions): Promise<void>;
45
+ /**
46
+ * Check if runtime is initialized.
47
+ */
48
+ isInitialized(): boolean;
49
+ /**
50
+ * Seed the database with deterministic defaults for a template.
51
+ *
52
+ * - No randomness
53
+ * - No wall-clock timestamps
54
+ * - Unknown templates are a no-op (safe default)
55
+ */
56
+ seedTemplate(options: TemplateSeedOptions): Promise<void>;
57
+ }
58
+ //#endregion
59
+ export { LocalRuntimeInitOptions, LocalRuntimeServices, TemplateId, TemplateSeedOptions };
60
+ //# sourceMappingURL=services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.d.ts","names":[],"sources":["../../../src/web/runtime/services.ts"],"sourcesContent":[],"mappings":";;;;;;;AAmCa,KAtBD,UAAA,GAsBC,MAAoB;AACf,UArBD,uBAAA,CAqBC;EACD;;;;EAoCoC,OAAA,CAAA,EAAA,MAAA;;AAuCD,UAzFnC,mBAAA,CAyFmC;EAAO,UAAA,EAxF7C,UAwF6C;;;;;;;;cA7E9C,oBAAA;;oBACK;mBACD;;;;;YAQL;;;;;iBAcK;;;;;;iBAcK,0BAA+B;;;;;;;;;;;;wBAuCvB,sBAAsB"}
@@ -0,0 +1,80 @@
1
+ import { SANDBOX_MIGRATIONS } from "../database/migrations.js";
2
+ import { LocalStorageService } from "../storage/indexeddb.js";
3
+ import { LocalEventBus } from "../events/local-pubsub.js";
4
+ import { LocalGraphQLClient } from "../graphql/local-client.js";
5
+
6
+ //#region src/web/runtime/services.ts
7
+ const DEFAULT_PROJECT_ID = "local-project";
8
+ /**
9
+ * Local runtime services for sandbox environment.
10
+ *
11
+ * Provides lazy-loaded database access via DatabasePort interface.
12
+ */
13
+ var LocalRuntimeServices = class {
14
+ storage = new LocalStorageService();
15
+ pubsub = new LocalEventBus();
16
+ #initialized = false;
17
+ _db;
18
+ /**
19
+ * Get the database port (must be initialized first).
20
+ */
21
+ get db() {
22
+ if (!this._db) throw new Error("LocalRuntimeServices not initialized. Call init() first.");
23
+ return this._db;
24
+ }
25
+ _graphql;
26
+ /**
27
+ * Get the GraphQL client (must be initialized first).
28
+ */
29
+ get graphql() {
30
+ if (!this._graphql) throw new Error("LocalRuntimeServices not initialized. Call init() first.");
31
+ return this._graphql;
32
+ }
33
+ /**
34
+ * Initialize the runtime services.
35
+ *
36
+ * Lazy-loads PGLite adapter to avoid bundle bloat.
37
+ */
38
+ async init(options = {}) {
39
+ if (this.#initialized) return;
40
+ const { createPGLiteAdapter } = await import("@contractspec/lib.runtime-sandbox");
41
+ this._db = await createPGLiteAdapter();
42
+ await this._db.init({ dataDir: options.dataDir });
43
+ await this._db.migrate(SANDBOX_MIGRATIONS);
44
+ await this.storage.init();
45
+ this._graphql = new LocalGraphQLClient({
46
+ db: this._db,
47
+ storage: this.storage,
48
+ pubsub: this.pubsub
49
+ });
50
+ this.#initialized = true;
51
+ }
52
+ /**
53
+ * Check if runtime is initialized.
54
+ */
55
+ isInitialized() {
56
+ return this.#initialized;
57
+ }
58
+ /**
59
+ * Seed the database with deterministic defaults for a template.
60
+ *
61
+ * - No randomness
62
+ * - No wall-clock timestamps
63
+ * - Unknown templates are a no-op (safe default)
64
+ */
65
+ async seedTemplate(options) {
66
+ if (!this.#initialized) throw new Error("Call init() before seeding templates.");
67
+ const projectId = options.projectId ?? DEFAULT_PROJECT_ID;
68
+ if (!this._db) throw new Error("Database not initialized");
69
+ const { seedTemplate } = await import("./seeders/index.js");
70
+ await seedTemplate({
71
+ templateId: options.templateId,
72
+ projectId,
73
+ db: this._db
74
+ });
75
+ }
76
+ };
77
+
78
+ //#endregion
79
+ export { LocalRuntimeServices };
80
+ //# sourceMappingURL=services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.js","names":["#initialized"],"sources":["../../../src/web/runtime/services.ts"],"sourcesContent":["/**\n * Runtime services for local (browser) sandbox environment.\n *\n * Uses lazy-loading for PGLite to avoid bundle bloat.\n */\nimport type { DatabasePort } from '@contractspec/lib.runtime-sandbox';\n\nimport { LocalEventBus } from '../events/local-pubsub';\nimport { LocalGraphQLClient } from '../graphql/local-client';\nimport { LocalStorageService } from '../storage/indexeddb';\n\nimport { SANDBOX_MIGRATIONS } from '../database/migrations';\n\nexport type TemplateId = string;\n\nexport interface LocalRuntimeInitOptions {\n /**\n * Data directory for IndexedDB persistence (optional).\n * If omitted, uses in-memory database.\n */\n dataDir?: string;\n}\n\nexport interface TemplateSeedOptions {\n templateId: TemplateId;\n projectId?: string;\n}\n\nconst DEFAULT_PROJECT_ID = 'local-project' as const;\n\n/**\n * Local runtime services for sandbox environment.\n *\n * Provides lazy-loaded database access via DatabasePort interface.\n */\nexport class LocalRuntimeServices {\n readonly storage = new LocalStorageService();\n readonly pubsub = new LocalEventBus();\n #initialized = false;\n\n private _db?: DatabasePort;\n\n /**\n * Get the database port (must be initialized first).\n */\n get db(): DatabasePort {\n if (!this._db) {\n throw new Error(\n 'LocalRuntimeServices not initialized. Call init() first.'\n );\n }\n return this._db;\n }\n\n private _graphql?: LocalGraphQLClient;\n\n /**\n * Get the GraphQL client (must be initialized first).\n */\n get graphql(): LocalGraphQLClient {\n if (!this._graphql) {\n throw new Error(\n 'LocalRuntimeServices not initialized. Call init() first.'\n );\n }\n return this._graphql;\n }\n\n /**\n * Initialize the runtime services.\n *\n * Lazy-loads PGLite adapter to avoid bundle bloat.\n */\n async init(options: LocalRuntimeInitOptions = {}): Promise<void> {\n if (this.#initialized) return;\n\n // Lazy-load PGLite adapter\n const { createPGLiteAdapter } =\n await import('@contractspec/lib.runtime-sandbox');\n this._db = await createPGLiteAdapter();\n await this._db.init({ dataDir: options.dataDir });\n\n // Run migrations\n await this._db.migrate(SANDBOX_MIGRATIONS);\n\n // Initialize storage\n await this.storage.init();\n\n // Initialize GraphQL client with the new database port\n this._graphql = new LocalGraphQLClient({\n db: this._db,\n storage: this.storage,\n pubsub: this.pubsub,\n });\n\n this.#initialized = true;\n }\n\n /**\n * Check if runtime is initialized.\n */\n isInitialized(): boolean {\n return this.#initialized;\n }\n\n /**\n * Seed the database with deterministic defaults for a template.\n *\n * - No randomness\n * - No wall-clock timestamps\n * - Unknown templates are a no-op (safe default)\n */\n async seedTemplate(options: TemplateSeedOptions): Promise<void> {\n if (!this.#initialized) {\n throw new Error('Call init() before seeding templates.');\n }\n\n const projectId = options.projectId ?? DEFAULT_PROJECT_ID;\n\n if (!this._db) {\n throw new Error('Database not initialized');\n }\n\n // Lazy-load seeders to avoid bundle bloat\n const { seedTemplate } = await import('./seeders');\n await seedTemplate({\n templateId: options.templateId,\n projectId,\n db: this._db,\n });\n }\n}\n"],"mappings":";;;;;;AA4BA,MAAM,qBAAqB;;;;;;AAO3B,IAAa,uBAAb,MAAkC;CAChC,AAAS,UAAU,IAAI,qBAAqB;CAC5C,AAAS,SAAS,IAAI,eAAe;CACrC,eAAe;CAEf,AAAQ;;;;CAKR,IAAI,KAAmB;AACrB,MAAI,CAAC,KAAK,IACR,OAAM,IAAI,MACR,2DACD;AAEH,SAAO,KAAK;;CAGd,AAAQ;;;;CAKR,IAAI,UAA8B;AAChC,MAAI,CAAC,KAAK,SACR,OAAM,IAAI,MACR,2DACD;AAEH,SAAO,KAAK;;;;;;;CAQd,MAAM,KAAK,UAAmC,EAAE,EAAiB;AAC/D,MAAI,MAAKA,YAAc;EAGvB,MAAM,EAAE,wBACN,MAAM,OAAO;AACf,OAAK,MAAM,MAAM,qBAAqB;AACtC,QAAM,KAAK,IAAI,KAAK,EAAE,SAAS,QAAQ,SAAS,CAAC;AAGjD,QAAM,KAAK,IAAI,QAAQ,mBAAmB;AAG1C,QAAM,KAAK,QAAQ,MAAM;AAGzB,OAAK,WAAW,IAAI,mBAAmB;GACrC,IAAI,KAAK;GACT,SAAS,KAAK;GACd,QAAQ,KAAK;GACd,CAAC;AAEF,QAAKA,cAAe;;;;;CAMtB,gBAAyB;AACvB,SAAO,MAAKA;;;;;;;;;CAUd,MAAM,aAAa,SAA6C;AAC9D,MAAI,CAAC,MAAKA,YACR,OAAM,IAAI,MAAM,wCAAwC;EAG1D,MAAM,YAAY,QAAQ,aAAa;AAEvC,MAAI,CAAC,KAAK,IACR,OAAM,IAAI,MAAM,2BAA2B;EAI7C,MAAM,EAAE,iBAAiB,MAAM,OAAO;AACtC,QAAM,aAAa;GACjB,YAAY,QAAQ;GACpB;GACA,IAAI,KAAK;GACV,CAAC"}
@@ -0,0 +1,22 @@
1
+ //#region src/web/storage/indexeddb.d.ts
2
+ interface LocalStorageOptions {
3
+ dbName?: string;
4
+ storeName?: string;
5
+ version?: number;
6
+ }
7
+ declare class LocalStorageService {
8
+ private readonly options;
9
+ private dbPromise?;
10
+ constructor(options?: LocalStorageOptions);
11
+ init(): Promise<void>;
12
+ get<TValue = unknown>(key: string, fallback?: TValue): Promise<TValue | undefined>;
13
+ set<TValue = unknown>(key: string, value: TValue): Promise<void>;
14
+ delete(key: string): Promise<void>;
15
+ clear(): Promise<void>;
16
+ private get storeName();
17
+ private getDb;
18
+ private openDb;
19
+ }
20
+ //#endregion
21
+ export { LocalStorageOptions, LocalStorageService };
22
+ //# sourceMappingURL=indexeddb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexeddb.d.ts","names":[],"sources":["../../../src/web/storage/indexeddb.ts"],"sourcesContent":[],"mappings":";UAAiB,mBAAA;EAAA,MAAA,CAAA,EAAA,MAAA;EAUJ,SAAA,CAAA,EAAA,MAAA;EAG2B,OAAA,CAAA,EAAA,MAAA;;AAQzB,cAXF,mBAAA,CAWE;EACF,iBAAA,OAAA;EAAR,QAAA,SAAA;EAgB6C,WAAA,CAAA,OAAA,CAAA,EAzBV,mBAyBU;EAAS,IAAA,CAAA,CAAA,EAvB3C,OAuB2C,CAAA,IAAA,CAAA;EAe9B,GAAA,CAAA,SAAA,OAAA,CAAA,CAAA,GAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAhCd,MAgCc,CAAA,EA/BxB,OA+BwB,CA/BhB,MA+BgB,GAAA,SAAA,CAAA;EAeZ,GAAA,CAAA,SAAA,OAAA,CAAA,CAAA,GAAA,EAAA,MAAA,EAAA,KAAA,EA9BiC,MA8BjC,CAAA,EA9B0C,OA8B1C,CAAA,IAAA,CAAA;EAAO,MAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAfK,OAeL,CAAA,IAAA,CAAA;WAAP"}