@iota-uz/sdk 0.4.13 → 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,876 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+ import { z } from 'zod';
3
+
4
+ // ui/src/applet-runtime/context.ts
5
+ var requestContextStore = new AsyncLocalStorage();
6
+ function withRequestContext(request, fn) {
7
+ return requestContextStore.run({ request }, fn);
8
+ }
9
+ function currentRequest() {
10
+ const ctx = requestContextStore.getStore();
11
+ if (!ctx?.request) {
12
+ throw new Error("No active applet request context. Use defineApplet({ fetch }) to establish request context.");
13
+ }
14
+ return ctx.request;
15
+ }
16
+ function currentRequestOptional() {
17
+ return requestContextStore.getStore()?.request;
18
+ }
19
+
20
+ // ui/src/applet-runtime/engine.ts
21
+ var defaultEnginePath = "/rpc";
22
+ function readEnv(name) {
23
+ const value = process.env[name];
24
+ if (!value || value.trim() === "") {
25
+ throw new Error(`${name} is required`);
26
+ }
27
+ return value;
28
+ }
29
+ function buildEngineURL(pathname) {
30
+ const socketPath = readEnv("IOTA_ENGINE_SOCKET");
31
+ const path = pathname.startsWith("/") ? pathname : `/${pathname}`;
32
+ return `http://localhost${path}?socket=${encodeURIComponent(socketPath)}`;
33
+ }
34
+ function randomID() {
35
+ const g = globalThis;
36
+ if (g.crypto?.randomUUID) {
37
+ return g.crypto.randomUUID();
38
+ }
39
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
40
+ }
41
+ async function postJSON(pathname, body, timeoutMs) {
42
+ const socketPath = readEnv("IOTA_ENGINE_SOCKET");
43
+ const request = currentRequestOptional();
44
+ const forwardedHeaders2 = {
45
+ "content-type": "application/json"
46
+ };
47
+ if (request) {
48
+ const maybeForwardHeader = (headerName) => {
49
+ const value = request.headers.get(headerName);
50
+ if (value && value.trim() !== "") {
51
+ forwardedHeaders2[headerName] = value;
52
+ }
53
+ };
54
+ maybeForwardHeader("x-iota-tenant-id");
55
+ maybeForwardHeader("x-iota-user-id");
56
+ maybeForwardHeader("x-iota-permissions");
57
+ maybeForwardHeader("x-iota-request-id");
58
+ maybeForwardHeader("cookie");
59
+ maybeForwardHeader("authorization");
60
+ }
61
+ const abortController = typeof timeoutMs === "number" && timeoutMs > 0 ? new AbortController() : void 0;
62
+ let timer;
63
+ try {
64
+ if (abortController && timeoutMs) {
65
+ timer = setTimeout(() => abortController.abort(), timeoutMs);
66
+ }
67
+ const init = {
68
+ method: "POST",
69
+ headers: forwardedHeaders2,
70
+ body: JSON.stringify(body),
71
+ signal: abortController?.signal,
72
+ unix: socketPath
73
+ };
74
+ const response = await fetch(buildEngineURL(pathname), init);
75
+ if (!response.ok) {
76
+ throw new Error(`engine RPC HTTP ${response.status}`);
77
+ }
78
+ return await response.json();
79
+ } finally {
80
+ if (timer) {
81
+ clearTimeout(timer);
82
+ }
83
+ }
84
+ }
85
+ var engine = {
86
+ async call(method, params, options) {
87
+ const request = {
88
+ id: randomID(),
89
+ method,
90
+ params
91
+ };
92
+ const response = await postJSON(defaultEnginePath, request, options?.timeoutMs);
93
+ if (response.error) {
94
+ throw new Error(`${response.error.code}: ${response.error.message}`);
95
+ }
96
+ return response.result;
97
+ }
98
+ };
99
+
100
+ // ui/src/applet-runtime/ws.ts
101
+ var connectionHandlers = /* @__PURE__ */ new Set();
102
+ var messageHandlers = /* @__PURE__ */ new Set();
103
+ var closeHandlers = /* @__PURE__ */ new Set();
104
+ function appletMethod(op) {
105
+ const appletID = process.env.IOTA_APPLET_ID;
106
+ if (!appletID || appletID.trim() === "") {
107
+ throw new Error("IOTA_APPLET_ID is required");
108
+ }
109
+ return `${appletID}.ws.${op}`;
110
+ }
111
+ async function runHandlers(handlers, value) {
112
+ for (const handler of handlers) {
113
+ await handler(value);
114
+ }
115
+ }
116
+ async function runMessageHandlers(connectionId, data) {
117
+ for (const handler of messageHandlers) {
118
+ await handler(connectionId, data);
119
+ }
120
+ }
121
+ async function dispatchBridgeEvent(payload) {
122
+ if (payload.event === "open") {
123
+ await runHandlers(connectionHandlers, payload.connectionId);
124
+ return;
125
+ }
126
+ if (payload.event === "close") {
127
+ await runHandlers(closeHandlers, payload.connectionId);
128
+ return;
129
+ }
130
+ if (payload.event === "message") {
131
+ const decoded = Buffer.from(payload.dataBase64 ?? "", "base64");
132
+ await runMessageHandlers(payload.connectionId, new Uint8Array(decoded));
133
+ }
134
+ }
135
+ var ws = {
136
+ async send(connectionId, data) {
137
+ return engine.call(appletMethod("send"), { connectionId, data });
138
+ },
139
+ onConnection(handler) {
140
+ connectionHandlers.add(handler);
141
+ return () => connectionHandlers.delete(handler);
142
+ },
143
+ onMessage(handler) {
144
+ messageHandlers.add(handler);
145
+ return () => messageHandlers.delete(handler);
146
+ },
147
+ onClose(handler) {
148
+ closeHandlers.add(handler);
149
+ return () => closeHandlers.delete(handler);
150
+ }
151
+ };
152
+
153
+ // ui/src/applet-runtime/defineApplet.ts
154
+ function requireUnixSocketPath() {
155
+ const path = process.env.IOTA_APPLET_SOCKET;
156
+ if (!path || path.trim() === "") {
157
+ throw new Error("IOTA_APPLET_SOCKET is required");
158
+ }
159
+ return path;
160
+ }
161
+ function defineApplet(definition) {
162
+ const socketPath = requireUnixSocketPath();
163
+ const bun = globalThis.Bun;
164
+ if (!bun || typeof bun.serve !== "function") {
165
+ throw new Error("defineApplet requires Bun runtime");
166
+ }
167
+ return bun.serve({
168
+ unix: socketPath,
169
+ fetch: (request) => withRequestContext(request, async () => {
170
+ const url = new URL(request.url);
171
+ if (request.method === "POST" && url.pathname === "/__ws") {
172
+ let payload;
173
+ try {
174
+ payload = await request.json();
175
+ } catch {
176
+ return new Response(JSON.stringify({ error: "invalid_json" }), {
177
+ status: 400,
178
+ headers: { "content-type": "application/json; charset=utf-8" }
179
+ });
180
+ }
181
+ if (!payload?.connectionId || !payload?.event) {
182
+ return new Response(JSON.stringify({ error: "invalid_payload" }), {
183
+ status: 400,
184
+ headers: { "content-type": "application/json; charset=utf-8" }
185
+ });
186
+ }
187
+ if (!["open", "message", "close"].includes(payload.event)) {
188
+ return new Response(JSON.stringify({ error: "invalid_payload" }), {
189
+ status: 400,
190
+ headers: { "content-type": "application/json; charset=utf-8" }
191
+ });
192
+ }
193
+ await dispatchBridgeEvent(payload);
194
+ return new Response(JSON.stringify({ ok: true }), {
195
+ status: 202,
196
+ headers: { "content-type": "application/json; charset=utf-8" }
197
+ });
198
+ }
199
+ return definition.fetch(request);
200
+ })
201
+ });
202
+ }
203
+
204
+ // ui/src/applet-runtime/auth.ts
205
+ function requiredHeader(headers, name) {
206
+ const value = headers.get(name);
207
+ if (!value || value.trim() === "") {
208
+ throw new Error(`${name} header is required`);
209
+ }
210
+ return value;
211
+ }
212
+ var auth = {
213
+ async currentUser() {
214
+ const request = currentRequest();
215
+ const headers = request.headers;
216
+ const id2 = requiredHeader(headers, "x-iota-user-id");
217
+ const tenantId = requiredHeader(headers, "x-iota-tenant-id");
218
+ const permissionsHeader = headers.get("x-iota-permissions") ?? "";
219
+ const requestID = headers.get("x-iota-request-id") ?? void 0;
220
+ const permissions = permissionsHeader.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
221
+ return {
222
+ id: id2,
223
+ tenantId,
224
+ permissions,
225
+ requestId: requestID
226
+ };
227
+ }
228
+ };
229
+
230
+ // ui/src/applet-runtime/kv.ts
231
+ function appletMethod2(op) {
232
+ const appletID = process.env.IOTA_APPLET_ID;
233
+ if (!appletID || appletID.trim() === "") {
234
+ throw new Error("IOTA_APPLET_ID is required");
235
+ }
236
+ return `${appletID}.kv.${op}`;
237
+ }
238
+ var kv = {
239
+ get(key) {
240
+ return engine.call(appletMethod2("get"), { key });
241
+ },
242
+ set(key, value, ttlSeconds) {
243
+ return engine.call(appletMethod2("set"), { key, value, ttlSeconds });
244
+ },
245
+ del(key) {
246
+ return engine.call(appletMethod2("del"), { key });
247
+ },
248
+ mget(keys) {
249
+ return engine.call(appletMethod2("mget"), { keys });
250
+ }
251
+ };
252
+
253
+ // ui/src/applet-runtime/db.ts
254
+ function createDB(caller) {
255
+ class QueryIndexBuilder {
256
+ eq(field, value) {
257
+ this.constraint = { field, op: "eq", value };
258
+ return this;
259
+ }
260
+ build(name) {
261
+ if (!this.constraint) {
262
+ return void 0;
263
+ }
264
+ return { name, ...this.constraint };
265
+ }
266
+ }
267
+ class QueryFilterBuilder {
268
+ constructor() {
269
+ this.constraints = [];
270
+ }
271
+ eq(field, value) {
272
+ this.constraints.push({ field, op: "eq", value });
273
+ return this;
274
+ }
275
+ build() {
276
+ return this.constraints;
277
+ }
278
+ }
279
+ class RuntimeQueryBuilder {
280
+ constructor(table, options = {}) {
281
+ this.table = table;
282
+ this.options = options;
283
+ }
284
+ withIndex(name, build) {
285
+ const index = new QueryIndexBuilder();
286
+ build(index);
287
+ return new RuntimeQueryBuilder(this.table, {
288
+ ...this.options,
289
+ index: index.build(name)
290
+ });
291
+ }
292
+ filter(build) {
293
+ const filter = new QueryFilterBuilder();
294
+ build(filter);
295
+ return new RuntimeQueryBuilder(this.table, {
296
+ ...this.options,
297
+ filters: [...this.options.filters ?? [], ...filter.build()]
298
+ });
299
+ }
300
+ order(direction) {
301
+ return new RuntimeQueryBuilder(this.table, {
302
+ ...this.options,
303
+ order: direction
304
+ });
305
+ }
306
+ take(limit) {
307
+ return new RuntimeQueryBuilder(this.table, {
308
+ ...this.options,
309
+ take: limit
310
+ });
311
+ }
312
+ collect() {
313
+ return caller("query", { table: this.table, query: this.options });
314
+ }
315
+ async first() {
316
+ const rows = await this.take(1).collect();
317
+ return rows[0] ?? null;
318
+ }
319
+ }
320
+ const client = {
321
+ get(id2) {
322
+ return caller("get", { id: id2 });
323
+ },
324
+ query(table) {
325
+ return new RuntimeQueryBuilder(table);
326
+ },
327
+ queryRaw(table, query) {
328
+ return caller("query", { table, query: query ?? {} });
329
+ },
330
+ insert(table, value) {
331
+ return caller("insert", { table, value });
332
+ },
333
+ patch(id2, value) {
334
+ return caller("patch", { id: id2, value });
335
+ },
336
+ replace(id2, value) {
337
+ return caller("replace", { id: id2, value });
338
+ },
339
+ delete(id2) {
340
+ return caller("delete", { id: id2 });
341
+ }
342
+ };
343
+ return client;
344
+ }
345
+ var db = createDB((op, params) => engine.call(appletMethod3(op), params));
346
+ function appletMethod3(op) {
347
+ const appletID = process.env.IOTA_APPLET_ID;
348
+ if (!appletID || appletID.trim() === "") {
349
+ throw new Error("IOTA_APPLET_ID is required");
350
+ }
351
+ return `${appletID}.db.${op}`;
352
+ }
353
+
354
+ // ui/src/applet-runtime/jobs.ts
355
+ function appletMethod4(op) {
356
+ const appletID = process.env.IOTA_APPLET_ID;
357
+ if (!appletID || appletID.trim() === "") {
358
+ throw new Error("IOTA_APPLET_ID is required");
359
+ }
360
+ return `${appletID}.jobs.${op}`;
361
+ }
362
+ var jobs = {
363
+ enqueue(method, params) {
364
+ return engine.call(appletMethod4("enqueue"), { method, params: params ?? {} });
365
+ },
366
+ schedule(cron, method, params) {
367
+ return engine.call(appletMethod4("schedule"), { cron, method, params: params ?? {} });
368
+ },
369
+ list() {
370
+ return engine.call(appletMethod4("list"), {});
371
+ },
372
+ cancel(id2) {
373
+ return engine.call(appletMethod4("cancel"), { id: id2 });
374
+ }
375
+ };
376
+
377
+ // ui/src/applet-runtime/secrets.ts
378
+ function appletMethod5(op) {
379
+ const appletID = process.env.IOTA_APPLET_ID;
380
+ if (!appletID || appletID.trim() === "") {
381
+ throw new Error("IOTA_APPLET_ID is required");
382
+ }
383
+ return `${appletID}.secrets.${op}`;
384
+ }
385
+ var secrets = {
386
+ async get(name) {
387
+ const response = await engine.call(appletMethod5("get"), { name });
388
+ return response.value;
389
+ }
390
+ };
391
+
392
+ // ui/src/applet-runtime/files.ts
393
+ function readEnv2(name) {
394
+ const value = process.env[name];
395
+ if (!value || value.trim() === "") {
396
+ throw new Error(`${name} is required`);
397
+ }
398
+ return value;
399
+ }
400
+ function appletMethod6(op) {
401
+ const appletID = process.env.IOTA_APPLET_ID;
402
+ if (!appletID || appletID.trim() === "") {
403
+ throw new Error("IOTA_APPLET_ID is required");
404
+ }
405
+ return `${appletID}.files.${op}`;
406
+ }
407
+ function buildEngineURL2(pathname, socketPath) {
408
+ const path = pathname.startsWith("/") ? pathname : `/${pathname}`;
409
+ return `http://localhost${path}?socket=${encodeURIComponent(socketPath)}`;
410
+ }
411
+ function forwardedHeaders() {
412
+ const request = currentRequestOptional();
413
+ const headers = {};
414
+ if (!request) {
415
+ return headers;
416
+ }
417
+ const maybeForwardHeader = (headerName) => {
418
+ const value = request.headers.get(headerName);
419
+ if (value && value.trim() !== "") {
420
+ headers[headerName] = value;
421
+ }
422
+ };
423
+ maybeForwardHeader("x-iota-tenant-id");
424
+ maybeForwardHeader("x-iota-user-id");
425
+ maybeForwardHeader("x-iota-request-id");
426
+ return headers;
427
+ }
428
+ async function toBytes(data) {
429
+ if (data instanceof Blob) {
430
+ const buffer = await data.arrayBuffer();
431
+ return new Uint8Array(buffer);
432
+ }
433
+ if (data instanceof ArrayBuffer) {
434
+ return new Uint8Array(data);
435
+ }
436
+ return data;
437
+ }
438
+ async function toBase64(data) {
439
+ const bytes = await toBytes(data);
440
+ return Buffer.from(bytes).toString("base64");
441
+ }
442
+ async function requestFilesEndpoint(path, options) {
443
+ const socketPath = readEnv2("IOTA_ENGINE_SOCKET");
444
+ const headers = new Headers(options.headers);
445
+ headers.set("X-Iota-Applet-Id", options.appletId);
446
+ for (const [name, value] of Object.entries(forwardedHeaders())) {
447
+ headers.set(name, value);
448
+ }
449
+ const response = await fetch(buildEngineURL2(path, socketPath), {
450
+ ...options,
451
+ headers,
452
+ unix: socketPath
453
+ });
454
+ if (!response.ok) {
455
+ throw new Error(`files endpoint HTTP ${response.status}`);
456
+ }
457
+ return await response.json();
458
+ }
459
+ var files = {
460
+ async store(input) {
461
+ const appletID = readEnv2("IOTA_APPLET_ID");
462
+ try {
463
+ const bytes = await toBytes(input.data);
464
+ const exactBuffer = bytes.buffer instanceof ArrayBuffer ? bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) : Uint8Array.from(bytes).buffer;
465
+ return await requestFilesEndpoint("/files/store", {
466
+ appletId: appletID,
467
+ method: "POST",
468
+ headers: {
469
+ "X-Iota-File-Name": input.name,
470
+ "X-Iota-Content-Type": input.contentType ?? "",
471
+ "Content-Type": input.contentType ?? "application/octet-stream"
472
+ },
473
+ body: new Blob([exactBuffer], { type: input.contentType ?? "application/octet-stream" })
474
+ });
475
+ } catch (error) {
476
+ if (error instanceof TypeError || error instanceof ReferenceError) {
477
+ throw error;
478
+ }
479
+ const dataBase64 = await toBase64(input.data);
480
+ return engine.call(appletMethod6("store"), {
481
+ name: input.name,
482
+ contentType: input.contentType ?? "",
483
+ dataBase64
484
+ });
485
+ }
486
+ },
487
+ async get(id2) {
488
+ const appletID = readEnv2("IOTA_APPLET_ID");
489
+ try {
490
+ return await requestFilesEndpoint(`/files/get?id=${encodeURIComponent(id2)}&applet=${encodeURIComponent(appletID)}`, {
491
+ appletId: appletID,
492
+ method: "GET"
493
+ });
494
+ } catch (error) {
495
+ if (error instanceof TypeError || error instanceof ReferenceError) {
496
+ throw error;
497
+ }
498
+ return engine.call(appletMethod6("get"), { id: id2 });
499
+ }
500
+ },
501
+ async delete(id2) {
502
+ const appletID = readEnv2("IOTA_APPLET_ID");
503
+ try {
504
+ const result = await requestFilesEndpoint(`/files/delete?id=${encodeURIComponent(id2)}&applet=${encodeURIComponent(appletID)}`, {
505
+ appletId: appletID,
506
+ method: "DELETE"
507
+ });
508
+ return result.ok;
509
+ } catch (error) {
510
+ if (error instanceof TypeError || error instanceof ReferenceError) {
511
+ throw error;
512
+ }
513
+ return engine.call(appletMethod6("delete"), { id: id2 });
514
+ }
515
+ }
516
+ };
517
+ var RuntimeTableDefinition = class _RuntimeTableDefinition {
518
+ constructor(schema, indexes = []) {
519
+ this.schema = schema;
520
+ this.indexes = indexes;
521
+ }
522
+ index(name, fields) {
523
+ return new _RuntimeTableDefinition(this.schema, [
524
+ ...this.indexes,
525
+ {
526
+ name,
527
+ fields: [...fields]
528
+ }
529
+ ]);
530
+ }
531
+ };
532
+ function defineTable(shape) {
533
+ return new RuntimeTableDefinition(z.object(shape));
534
+ }
535
+ function defineSchema(tables) {
536
+ return { tables };
537
+ }
538
+ function id(_tableName) {
539
+ return z.string().min(1);
540
+ }
541
+
542
+ // ui/src/applet-runtime/test-context.ts
543
+ var defaultUser = {
544
+ id: "test-user",
545
+ tenantId: "test-tenant",
546
+ permissions: []
547
+ };
548
+ function normalizeUser(user) {
549
+ return {
550
+ ...defaultUser,
551
+ ...user ?? {},
552
+ permissions: user?.permissions ? [...user.permissions] : []
553
+ };
554
+ }
555
+ function randomID2(prefix) {
556
+ return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
557
+ }
558
+ function toNumber(value) {
559
+ if (typeof value === "number") {
560
+ return value;
561
+ }
562
+ return Number.NaN;
563
+ }
564
+ function nestedValue(input, field) {
565
+ let current = input;
566
+ for (const segment of field.split(".")) {
567
+ if (!current || typeof current !== "object") {
568
+ return void 0;
569
+ }
570
+ current = current[segment];
571
+ }
572
+ return current;
573
+ }
574
+ function matchesConstraint(value, constraint) {
575
+ if (constraint.op !== "eq") {
576
+ return false;
577
+ }
578
+ return String(nestedValue(value, constraint.field)) === String(constraint.value);
579
+ }
580
+ function filterDocuments(documents, options) {
581
+ let result = [...documents];
582
+ if (options.index) {
583
+ result = result.filter((doc) => matchesConstraint(doc.value, options.index));
584
+ }
585
+ for (const filter of options.filters ?? []) {
586
+ result = result.filter((doc) => matchesConstraint(doc.value, filter));
587
+ }
588
+ result.sort((a, b) => options.order === "asc" ? a.updatedAt - b.updatedAt : b.updatedAt - a.updatedAt);
589
+ if (typeof options.take === "number" && options.take > 0) {
590
+ result = result.slice(0, options.take);
591
+ }
592
+ return result;
593
+ }
594
+ function parseBase64Payload(data) {
595
+ if (typeof data !== "string" || data.trim() === "") {
596
+ throw new Error("dataBase64 is required");
597
+ }
598
+ return data;
599
+ }
600
+ async function toBase642(data) {
601
+ if (data instanceof Blob) {
602
+ const buffer = await data.arrayBuffer();
603
+ return Buffer.from(buffer).toString("base64");
604
+ }
605
+ if (data instanceof ArrayBuffer) {
606
+ return Buffer.from(data).toString("base64");
607
+ }
608
+ return Buffer.from(data).toString("base64");
609
+ }
610
+ function createTestContext(options = {}) {
611
+ const appletId = options.appletId?.trim() || "test";
612
+ let currentUser = normalizeUser(options.user);
613
+ const customHandlers = options.engineHandlers ?? {};
614
+ const secretStore = new Map(Object.entries(options.secrets ?? {}));
615
+ const kvStore = /* @__PURE__ */ new Map();
616
+ const dbStore = /* @__PURE__ */ new Map();
617
+ const jobsStore = /* @__PURE__ */ new Map();
618
+ const filesStore = /* @__PURE__ */ new Map();
619
+ function scopedKey(key) {
620
+ return `${currentUser.tenantId}::${appletId}::${key}`;
621
+ }
622
+ async function dbCall(op, params) {
623
+ switch (op) {
624
+ case "get": {
625
+ const id2 = String(params.id ?? "");
626
+ const record = dbStore.get(id2);
627
+ return record ? { _id: record.id, table: record.table, value: record.value } : null;
628
+ }
629
+ case "query": {
630
+ const table = String(params.table ?? "");
631
+ const query = params.query ?? {};
632
+ const rows = filterDocuments(
633
+ [...dbStore.values()].filter((doc) => doc.table === table),
634
+ query
635
+ );
636
+ return rows.map((doc) => ({ _id: doc.id, table: doc.table, value: doc.value }));
637
+ }
638
+ case "insert": {
639
+ const id2 = randomID2("doc");
640
+ const now = Date.now();
641
+ const record = {
642
+ id: id2,
643
+ table: String(params.table ?? ""),
644
+ value: params.value,
645
+ createdAt: now,
646
+ updatedAt: now
647
+ };
648
+ dbStore.set(id2, record);
649
+ return { _id: id2, table: record.table, value: record.value };
650
+ }
651
+ case "patch": {
652
+ const id2 = String(params.id ?? "");
653
+ const current = dbStore.get(id2);
654
+ if (!current) {
655
+ return null;
656
+ }
657
+ const currentValue = current.value;
658
+ const nextValue = params.value;
659
+ const next = {
660
+ ...current,
661
+ value: currentValue && typeof currentValue === "object" && !Array.isArray(currentValue) && nextValue && typeof nextValue === "object" && !Array.isArray(nextValue) ? { ...currentValue, ...nextValue } : nextValue,
662
+ updatedAt: Date.now()
663
+ };
664
+ dbStore.set(id2, next);
665
+ return { _id: id2, table: next.table, value: next.value };
666
+ }
667
+ case "replace": {
668
+ const id2 = String(params.id ?? "");
669
+ const current = dbStore.get(id2);
670
+ if (!current) {
671
+ return null;
672
+ }
673
+ const next = {
674
+ ...current,
675
+ value: params.value,
676
+ updatedAt: Date.now()
677
+ };
678
+ dbStore.set(id2, next);
679
+ return { _id: id2, table: next.table, value: next.value };
680
+ }
681
+ case "delete": {
682
+ const id2 = String(params.id ?? "");
683
+ return dbStore.delete(id2);
684
+ }
685
+ default:
686
+ throw new Error(`unknown db op: ${op}`);
687
+ }
688
+ }
689
+ const db2 = createDB((op, params) => dbCall(op, params));
690
+ const engine2 = {
691
+ async call(method, params) {
692
+ if (customHandlers[method]) {
693
+ return await customHandlers[method](params);
694
+ }
695
+ const chunks = method.split(".");
696
+ if (chunks.length < 3 || chunks[0] !== appletId) {
697
+ throw new Error(`unsupported method: ${method}`);
698
+ }
699
+ const namespace = chunks[1];
700
+ const op = chunks.slice(2).join(".");
701
+ const payload = params ?? {};
702
+ if (namespace === "kv") {
703
+ const key = String(payload.key ?? "");
704
+ const storageKey = scopedKey(key);
705
+ if (op === "get") {
706
+ return kvStore.get(storageKey) ?? null;
707
+ }
708
+ if (op === "set") {
709
+ kvStore.set(storageKey, payload.value);
710
+ return void 0;
711
+ }
712
+ if (op === "del") {
713
+ return kvStore.delete(storageKey);
714
+ }
715
+ if (op === "mget") {
716
+ const keys = Array.isArray(payload.keys) ? payload.keys.map((v) => String(v)) : [];
717
+ return keys.map((k) => kvStore.get(scopedKey(k)) ?? null);
718
+ }
719
+ }
720
+ if (namespace === "db") {
721
+ return dbCall(op, payload);
722
+ }
723
+ if (namespace === "jobs") {
724
+ if (op === "enqueue") {
725
+ const id2 = randomID2("job");
726
+ const record = {
727
+ id: id2,
728
+ type: "one_off",
729
+ cron: "",
730
+ method: String(payload.method ?? ""),
731
+ params: payload.params ?? {},
732
+ status: "queued",
733
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
734
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
735
+ };
736
+ jobsStore.set(id2, record);
737
+ return record;
738
+ }
739
+ if (op === "schedule") {
740
+ const id2 = randomID2("job");
741
+ const record = {
742
+ id: id2,
743
+ type: "scheduled",
744
+ cron: String(payload.cron ?? ""),
745
+ method: String(payload.method ?? ""),
746
+ params: payload.params ?? {},
747
+ status: "scheduled",
748
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
749
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
750
+ };
751
+ jobsStore.set(id2, record);
752
+ return record;
753
+ }
754
+ if (op === "list") {
755
+ return [...jobsStore.values()];
756
+ }
757
+ if (op === "cancel") {
758
+ const id2 = String(payload.id ?? "");
759
+ return { ok: jobsStore.delete(id2) };
760
+ }
761
+ }
762
+ if (namespace === "secrets" && op === "get") {
763
+ const name = String(payload.name ?? "");
764
+ const value = secretStore.get(name);
765
+ if (value === void 0) {
766
+ throw new Error(`secret not found: ${name}`);
767
+ }
768
+ return { value };
769
+ }
770
+ if (namespace === "files") {
771
+ if (op === "store") {
772
+ const id2 = randomID2("file");
773
+ const name = String(payload.name ?? "file.bin");
774
+ const dataBase64 = parseBase64Payload(payload.dataBase64);
775
+ const contentType = String(payload.contentType ?? "");
776
+ const size = Buffer.from(dataBase64, "base64").byteLength;
777
+ const record = {
778
+ id: id2,
779
+ name,
780
+ contentType,
781
+ size,
782
+ path: `/test/${currentUser.tenantId}/${appletId}/${id2}-${name}`,
783
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
784
+ dataBase64
785
+ };
786
+ filesStore.set(id2, record);
787
+ return record;
788
+ }
789
+ if (op === "get") {
790
+ const id2 = String(payload.id ?? "");
791
+ const record = filesStore.get(id2);
792
+ if (!record) {
793
+ return null;
794
+ }
795
+ const { dataBase64: _, ...rest } = record;
796
+ return rest;
797
+ }
798
+ if (op === "delete") {
799
+ const id2 = String(payload.id ?? "");
800
+ return filesStore.delete(id2);
801
+ }
802
+ }
803
+ throw new Error(`unsupported method: ${method}`);
804
+ }
805
+ };
806
+ return {
807
+ auth: {
808
+ async currentUser() {
809
+ return currentUser;
810
+ }
811
+ },
812
+ engine: engine2,
813
+ kv: {
814
+ async get(key) {
815
+ return kvStore.get(scopedKey(key)) ?? null;
816
+ },
817
+ async set(key, value, ttlSeconds) {
818
+ if (!Number.isNaN(toNumber(ttlSeconds)) && ttlSeconds <= 0) {
819
+ kvStore.delete(scopedKey(key));
820
+ return;
821
+ }
822
+ kvStore.set(scopedKey(key), value);
823
+ },
824
+ async del(key) {
825
+ return kvStore.delete(scopedKey(key));
826
+ },
827
+ async mget(keys) {
828
+ return keys.map((key) => kvStore.get(scopedKey(key)) ?? null);
829
+ }
830
+ },
831
+ db: db2,
832
+ jobs: {
833
+ enqueue(method, params) {
834
+ return engine2.call(`${appletId}.jobs.enqueue`, { method, params: params ?? {} });
835
+ },
836
+ schedule(cron, method, params) {
837
+ return engine2.call(`${appletId}.jobs.schedule`, { cron, method, params: params ?? {} });
838
+ },
839
+ list() {
840
+ return engine2.call(`${appletId}.jobs.list`, {});
841
+ },
842
+ cancel(id2) {
843
+ return engine2.call(`${appletId}.jobs.cancel`, { id: id2 });
844
+ }
845
+ },
846
+ secrets: {
847
+ async get(name) {
848
+ const result = await engine2.call(`${appletId}.secrets.get`, { name });
849
+ return result.value;
850
+ }
851
+ },
852
+ files: {
853
+ async store(input) {
854
+ const dataBase64 = await toBase642(input.data);
855
+ return engine2.call(`${appletId}.files.store`, {
856
+ name: input.name,
857
+ contentType: input.contentType ?? "",
858
+ dataBase64
859
+ });
860
+ },
861
+ get(id2) {
862
+ return engine2.call(`${appletId}.files.get`, { id: id2 });
863
+ },
864
+ delete(id2) {
865
+ return engine2.call(`${appletId}.files.delete`, { id: id2 });
866
+ }
867
+ },
868
+ setCurrentUser(nextUser) {
869
+ currentUser = normalizeUser(nextUser);
870
+ }
871
+ };
872
+ }
873
+
874
+ export { auth, createTestContext, db, defineApplet, defineSchema, defineTable, engine, files, id, jobs, kv, secrets, ws };
875
+ //# sourceMappingURL=index.mjs.map
876
+ //# sourceMappingURL=index.mjs.map