ai-evaluate 2.1.6 → 2.1.8

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 (100) hide show
  1. package/README.md +90 -3
  2. package/dist/capnweb-bundle.d.ts +10 -0
  3. package/dist/capnweb-bundle.d.ts.map +1 -0
  4. package/dist/capnweb-bundle.js +2596 -0
  5. package/dist/capnweb-bundle.js.map +1 -0
  6. package/dist/evaluate.d.ts +1 -1
  7. package/dist/evaluate.d.ts.map +1 -1
  8. package/dist/evaluate.js +186 -7
  9. package/dist/evaluate.js.map +1 -1
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/miniflare-pool.d.ts +109 -0
  15. package/dist/miniflare-pool.d.ts.map +1 -0
  16. package/dist/miniflare-pool.js +308 -0
  17. package/dist/miniflare-pool.js.map +1 -0
  18. package/dist/node.d.ts.map +1 -1
  19. package/dist/node.js +42 -10
  20. package/dist/node.js.map +1 -1
  21. package/dist/shared.d.ts +66 -0
  22. package/dist/shared.d.ts.map +1 -0
  23. package/dist/shared.js +169 -0
  24. package/dist/shared.js.map +1 -0
  25. package/dist/type-guards.d.ts +21 -0
  26. package/dist/type-guards.d.ts.map +1 -0
  27. package/dist/type-guards.js +216 -0
  28. package/dist/type-guards.js.map +1 -0
  29. package/dist/types.d.ts +17 -2
  30. package/dist/types.d.ts.map +1 -1
  31. package/dist/validation.d.ts +26 -0
  32. package/dist/validation.d.ts.map +1 -0
  33. package/dist/validation.js +104 -0
  34. package/dist/validation.js.map +1 -0
  35. package/dist/worker-template/code-transforms.d.ts +9 -0
  36. package/dist/worker-template/code-transforms.d.ts.map +1 -0
  37. package/dist/worker-template/code-transforms.js +28 -0
  38. package/dist/worker-template/code-transforms.js.map +1 -0
  39. package/{src/worker-template.d.ts → dist/worker-template/core.d.ts} +7 -15
  40. package/dist/worker-template/core.d.ts.map +1 -0
  41. package/dist/worker-template/core.js +502 -0
  42. package/dist/worker-template/core.js.map +1 -0
  43. package/dist/worker-template/helpers.d.ts +14 -0
  44. package/dist/worker-template/helpers.d.ts.map +1 -0
  45. package/dist/worker-template/helpers.js +79 -0
  46. package/dist/worker-template/helpers.js.map +1 -0
  47. package/dist/worker-template/index.d.ts +14 -0
  48. package/dist/worker-template/index.d.ts.map +1 -0
  49. package/dist/worker-template/index.js +19 -0
  50. package/dist/worker-template/index.js.map +1 -0
  51. package/dist/worker-template/sdk-generator.d.ts +17 -0
  52. package/dist/worker-template/sdk-generator.d.ts.map +1 -0
  53. package/{src/worker-template.js → dist/worker-template/sdk-generator.js} +377 -1506
  54. package/dist/worker-template/sdk-generator.js.map +1 -0
  55. package/dist/worker-template/test-generator.d.ts +16 -0
  56. package/dist/worker-template/test-generator.d.ts.map +1 -0
  57. package/dist/worker-template/test-generator.js +357 -0
  58. package/dist/worker-template/test-generator.js.map +1 -0
  59. package/dist/worker-template.d.ts +2 -2
  60. package/dist/worker-template.d.ts.map +1 -1
  61. package/dist/worker-template.js +64 -31
  62. package/dist/worker-template.js.map +1 -1
  63. package/example/package.json +7 -3
  64. package/example/src/index.ts +194 -40
  65. package/example/wrangler.jsonc +18 -2
  66. package/package.json +1 -3
  67. package/src/capnweb-bundle.ts +2596 -0
  68. package/src/evaluate.ts +216 -7
  69. package/src/index.ts +3 -1
  70. package/src/miniflare-pool.ts +395 -0
  71. package/src/node.ts +56 -11
  72. package/src/shared.ts +186 -0
  73. package/src/type-guards.ts +323 -0
  74. package/src/types.ts +18 -2
  75. package/src/validation.ts +120 -0
  76. package/src/worker-template/code-transforms.ts +32 -0
  77. package/src/worker-template/core.ts +557 -0
  78. package/src/worker-template/helpers.ts +90 -0
  79. package/src/worker-template/index.ts +23 -0
  80. package/src/{worker-template.ts → worker-template/sdk-generator.ts} +322 -1566
  81. package/src/worker-template/test-generator.ts +358 -0
  82. package/test/miniflare-pool.test.ts +246 -0
  83. package/test/node.test.ts +467 -0
  84. package/test/security.test.ts +1009 -0
  85. package/test/shared.test.ts +105 -0
  86. package/test/type-guards.test.ts +303 -0
  87. package/test/validation.test.ts +240 -0
  88. package/test/worker-template.test.ts +21 -19
  89. package/src/evaluate.js +0 -187
  90. package/src/index.js +0 -10
  91. package/src/node.d.ts +0 -17
  92. package/src/node.d.ts.map +0 -1
  93. package/src/node.js +0 -168
  94. package/src/node.js.map +0 -1
  95. package/src/types.d.ts +0 -172
  96. package/src/types.d.ts.map +0 -1
  97. package/src/types.js +0 -4
  98. package/src/types.js.map +0 -1
  99. package/src/worker-template.d.ts.map +0 -1
  100. package/src/worker-template.js.map +0 -1
@@ -1,46 +1,201 @@
1
1
  /**
2
- * Worker template for sandbox execution
3
- *
4
- * This code is stringified and sent to the worker loader.
5
- * It uses the TEST service binding (ai-tests) for assertions and test running.
6
- *
7
- * The user's code (module, tests, script) is embedded directly into
8
- * the worker source - no eval() or new Function() needed. The security
9
- * comes from running in an isolated V8 context via worker_loaders.
10
- *
11
- * Routes:
12
- * - POST /execute - Run tests and scripts, return results
13
- * - POST /rpc or WebSocket upgrade - capnweb RPC to module exports
14
- * - GET / - Return info about available exports
15
- */
16
- /**
17
- * Generate SDK code for injection into sandbox
2
+ * SDK code generation for worker templates
18
3
  *
19
4
  * Supports two modes:
20
5
  * - local: In-memory implementations (for testing without network)
21
6
  * - remote: RPC-based implementations (for production/integration tests)
22
7
  */
23
- function generateSDKCode(config = {}) {
24
- // Use local mode by default for sandboxed execution
8
+ /**
9
+ * Generate SDK code for injection into sandbox
10
+ */
11
+ export function generateSDKCode(config = {}) {
25
12
  if (config.context === 'remote') {
26
13
  return generateRemoteSDKCode(config);
27
14
  }
28
15
  return generateLocalSDKCode(config);
29
16
  }
17
+ /**
18
+ * Generate .should chainable assertions code
19
+ */
20
+ export function generateShouldCode() {
21
+ return `
22
+ // ============================================================
23
+ // Global .should Chainable Assertions
24
+ // ============================================================
25
+
26
+ const __createShouldChain__ = (actual, negated = false) => {
27
+ const check = (condition, message) => {
28
+ const passes = negated ? !condition : condition;
29
+ if (!passes) throw new Error(negated ? 'Expected NOT: ' + message : message);
30
+ };
31
+
32
+ const stringify = (val) => {
33
+ try {
34
+ return JSON.stringify(val);
35
+ } catch {
36
+ return String(val);
37
+ }
38
+ };
39
+
40
+ // Create a lazy chain getter - returns 'this' assertion for chaining
41
+ const assertion = {};
42
+
43
+ // Core assertion methods
44
+ assertion.equal = (expected) => {
45
+ check(actual === expected, 'Expected ' + stringify(actual) + ' to equal ' + stringify(expected));
46
+ return assertion;
47
+ };
48
+ assertion.deep = {
49
+ equal: (expected) => {
50
+ check(stringify(actual) === stringify(expected), 'Expected deep equal to ' + stringify(expected));
51
+ return assertion;
52
+ },
53
+ include: (expected) => {
54
+ const actualStr = stringify(actual);
55
+ const expectedStr = stringify(expected);
56
+ // Check if expected properties exist with same values
57
+ const matches = Object.entries(expected || {}).every(([k, v]) =>
58
+ actual && stringify(actual[k]) === stringify(v)
59
+ );
60
+ check(matches, 'Expected ' + actualStr + ' to deeply include ' + expectedStr);
61
+ return assertion;
62
+ }
63
+ };
64
+ assertion.include = (value) => {
65
+ if (typeof actual === 'string') check(actual.includes(String(value)), 'Expected "' + actual + '" to include "' + value + '"');
66
+ else if (Array.isArray(actual)) check(actual.includes(value), 'Expected array to include ' + stringify(value));
67
+ return assertion;
68
+ };
69
+ assertion.contain = assertion.include;
70
+ assertion.lengthOf = (n) => {
71
+ check(actual?.length === n, 'Expected length ' + n + ', got ' + actual?.length);
72
+ return assertion;
73
+ };
74
+ assertion.match = (regex) => {
75
+ const str = String(actual);
76
+ check(regex.test(str), 'Expected "' + str + '" to match ' + regex);
77
+ return assertion;
78
+ };
79
+ assertion.matches = assertion.match;
80
+
81
+ // .be accessor with type checks
82
+ Object.defineProperty(assertion, 'be', {
83
+ get: () => {
84
+ const beObj = {
85
+ a: (type) => {
86
+ const actualType = actual === null ? 'null' : Array.isArray(actual) ? 'array' : actual instanceof Date ? 'date' : typeof actual;
87
+ check(actualType === type.toLowerCase(), 'Expected ' + stringify(actual) + ' to be a ' + type);
88
+ return assertion;
89
+ },
90
+ above: (n) => { check(actual > n, 'Expected ' + actual + ' to be above ' + n); return assertion; },
91
+ below: (n) => { check(actual < n, 'Expected ' + actual + ' to be below ' + n); return assertion; },
92
+ within: (min, max) => { check(actual >= min && actual <= max, 'Expected ' + actual + ' to be within ' + min + '..' + max); return assertion; },
93
+ oneOf: (arr) => { check(Array.isArray(arr) && arr.includes(actual), 'Expected ' + stringify(actual) + ' to be one of ' + stringify(arr)); return assertion; },
94
+ instanceOf: (cls) => { check(actual instanceof cls, 'Expected to be instance of ' + cls.name); return assertion; }
95
+ };
96
+ beObj.an = beObj.a;
97
+ Object.defineProperty(beObj, 'true', { get: () => { check(actual === true, 'Expected ' + stringify(actual) + ' to be true'); return assertion; } });
98
+ Object.defineProperty(beObj, 'false', { get: () => { check(actual === false, 'Expected ' + stringify(actual) + ' to be false'); return assertion; } });
99
+ Object.defineProperty(beObj, 'ok', { get: () => { check(!!actual, 'Expected ' + stringify(actual) + ' to be truthy'); return assertion; } });
100
+ Object.defineProperty(beObj, 'null', { get: () => { check(actual === null, 'Expected ' + stringify(actual) + ' to be null'); return assertion; } });
101
+ Object.defineProperty(beObj, 'undefined', { get: () => { check(actual === undefined, 'Expected ' + stringify(actual) + ' to be undefined'); return assertion; } });
102
+ Object.defineProperty(beObj, 'empty', { get: () => {
103
+ const isEmpty = actual === '' || (Array.isArray(actual) && actual.length === 0) || (actual && typeof actual === 'object' && Object.keys(actual).length === 0);
104
+ check(isEmpty, 'Expected ' + stringify(actual) + ' to be empty');
105
+ return assertion;
106
+ }});
107
+ return beObj;
108
+ }
109
+ });
110
+
111
+ // .have accessor with property/keys/lengthOf/at checks
112
+ Object.defineProperty(assertion, 'have', {
113
+ get: () => ({
114
+ property: (name, value) => {
115
+ const hasIt = actual != null && Object.prototype.hasOwnProperty.call(actual, name);
116
+ if (value !== undefined) {
117
+ check(hasIt && actual[name] === value, "Expected property '" + name + "' = " + stringify(value) + ", got " + stringify(actual?.[name]));
118
+ } else {
119
+ check(hasIt, "Expected to have property '" + name + "'");
120
+ }
121
+ if (hasIt) return __createShouldChain__(actual[name], negated);
122
+ return assertion;
123
+ },
124
+ keys: (...keys) => {
125
+ const actualKeys = Object.keys(actual || {});
126
+ check(keys.every(k => actualKeys.includes(k)), 'Expected to have keys ' + stringify(keys));
127
+ return assertion;
128
+ },
129
+ lengthOf: (n) => {
130
+ check(actual?.length === n, 'Expected length ' + n + ', got ' + actual?.length);
131
+ return assertion;
132
+ },
133
+ at: {
134
+ least: (n) => {
135
+ check(actual?.length >= n, 'Expected length at least ' + n + ', got ' + actual?.length);
136
+ return assertion;
137
+ },
138
+ most: (n) => {
139
+ check(actual?.length <= n, 'Expected length at most ' + n + ', got ' + actual?.length);
140
+ return assertion;
141
+ }
142
+ }
143
+ })
144
+ });
145
+
146
+ // .not negation
147
+ Object.defineProperty(assertion, 'not', {
148
+ get: () => __createShouldChain__(actual, !negated)
149
+ });
150
+
151
+ // .with passthrough for readability
152
+ Object.defineProperty(assertion, 'with', {
153
+ get: () => assertion
154
+ });
155
+
156
+ // .that passthrough for chaining (e.g. .have.property('x').that.matches(/.../) )
157
+ Object.defineProperty(assertion, 'that', {
158
+ get: () => assertion
159
+ });
160
+
161
+ // .and passthrough for chaining
162
+ Object.defineProperty(assertion, 'and', {
163
+ get: () => assertion
164
+ });
165
+
166
+ return assertion;
167
+ };
168
+
169
+ // Add .should to Object.prototype
170
+ Object.defineProperty(Object.prototype, 'should', {
171
+ get: function() { return __createShouldChain__(this); },
172
+ configurable: true,
173
+ enumerable: false
174
+ });
175
+ `;
176
+ }
30
177
  /**
31
178
  * Generate local SDK code with in-memory implementations
32
- *
33
- * Implements APIs that align with ai-database (MemoryDB) and ai-workflows:
34
- * - MongoDB-style query operators ($gt, $gte, $lt, $in, $regex, etc.)
35
- * - URL resolution and identifier parsing
36
- * - upsert, generate, forEach methods
37
- * - Typed collection accessors (db.Users.find(), etc.)
38
- * - Workflow event/schedule patterns
39
179
  */
40
180
  function generateLocalSDKCode(config = {}) {
41
181
  const ns = config.ns || 'default';
42
182
  const aiGatewayUrl = config.aiGatewayUrl || '';
43
183
  const aiGatewayToken = config.aiGatewayToken || '';
184
+ // This is a very large string - the full local SDK implementation
185
+ // For brevity, I'm using a dynamic import approach to load the template
186
+ return getLocalSDKTemplate(ns, aiGatewayUrl, aiGatewayToken);
187
+ }
188
+ /**
189
+ * Generate remote SDK code (RPC-based)
190
+ */
191
+ function generateRemoteSDKCode(config = {}) {
192
+ const rpcUrl = config.rpcUrl || 'https://rpc.do';
193
+ const token = config.token || '';
194
+ const ns = config.ns || 'default';
195
+ return getRemoteSDKTemplate(rpcUrl, token, ns);
196
+ }
197
+ // Helper to get the local SDK template
198
+ function getLocalSDKTemplate(ns, aiGatewayUrl, aiGatewayToken) {
44
199
  return `
45
200
  // ============================================================
46
201
  // Local SDK - In-memory implementation (aligned with ai-database/ai-workflows)
@@ -202,7 +357,6 @@ const __cosineSimilarity__ = (a, b) => {
202
357
  const __embeddings__ = new Map();
203
358
 
204
359
  // AI embed helper for semantic search - defined early so __db_core__.search can use it
205
- // Uses Gemini embedding model (768 dimensions) through AI Gateway
206
360
  const __aiEmbed__ = async (text) => {
207
361
  if (!__SDK_CONFIG__.aiGatewayUrl) return [];
208
362
  try {
@@ -228,6 +382,26 @@ const __aiEmbed__ = async (text) => {
228
382
  }
229
383
  };
230
384
 
385
+ ${getDbCoreTemplate()}
386
+
387
+ ${getTypedCollectionTemplate()}
388
+
389
+ ${getAiGatewayTemplate()}
390
+
391
+ ${getAiMethodsTemplate()}
392
+
393
+ ${getHonoAppTemplate()}
394
+
395
+ ${getMdxRenderingTemplate()}
396
+
397
+ ${getWorkflowSystemTemplate()}
398
+
399
+ ${getContextObjectTemplate()}
400
+ `;
401
+ }
402
+ // Helper functions that return template strings for different parts of the SDK
403
+ function getDbCoreTemplate() {
404
+ return `
231
405
  // Local DB implementation (aligned with ai-database DBClient interface)
232
406
  const __db_core__ = {
233
407
  ns: __SDK_CONFIG__.ns,
@@ -254,19 +428,15 @@ const __db_core__ = {
254
428
  if (options.semantic && typeof __aiEmbed__ === 'function') {
255
429
  const queryEmbedding = await __aiEmbed__(options.query || '');
256
430
  if (!queryEmbedding || queryEmbedding.length === 0) {
257
- // Embedding failed - return text-based results sorted by relevance
258
- // This handles cases where AI Gateway auth isn't configured
259
431
  console.warn('Semantic search: embeddings unavailable, using fuzzy text matching');
260
432
  const queryTerms = (options.query || '').toLowerCase().split(/\\s+/);
261
433
  const textResults = [];
262
434
  for (const thing of __db_things__.values()) {
263
435
  if (!__matchesQuery__(thing, options)) continue;
264
436
  const content = JSON.stringify(thing.data).toLowerCase();
265
- // Score based on how many query terms appear in the content
266
437
  let score = 0;
267
438
  for (const term of queryTerms) {
268
439
  if (content.includes(term)) score += 1;
269
- // Bonus for partial matches
270
440
  for (const word of content.split(/[\\s\\W]+/)) {
271
441
  if (word.includes(term) || term.includes(word)) score += 0.1;
272
442
  }
@@ -279,11 +449,8 @@ const __db_core__ = {
279
449
 
280
450
  for (const thing of __db_things__.values()) {
281
451
  if (!__matchesQuery__(thing, options)) continue;
282
-
283
- // Get or compute embedding for this thing
284
452
  const thingUrl = thing.url || thing.id;
285
453
  let thingEmbedding = __embeddings__.get(thingUrl);
286
-
287
454
  if (!thingEmbedding) {
288
455
  const textContent = JSON.stringify(thing.data);
289
456
  thingEmbedding = await __aiEmbed__(textContent);
@@ -291,13 +458,11 @@ const __db_core__ = {
291
458
  __embeddings__.set(thingUrl, thingEmbedding);
292
459
  }
293
460
  }
294
-
295
461
  if (thingEmbedding && thingEmbedding.length > 0) {
296
462
  const score = __cosineSimilarity__(queryEmbedding, thingEmbedding);
297
463
  if (score >= minScore) results.push({ thing, score });
298
464
  }
299
465
  }
300
-
301
466
  results.sort((a, b) => b.score - a.score);
302
467
  return __applyQueryOptions__(results.map(r => r.thing), options);
303
468
  }
@@ -334,16 +499,13 @@ const __db_core__ = {
334
499
  const thing = __db_byUrl__.get(url);
335
500
  if (thing) return thing;
336
501
  }
337
- // Try by ID across all things
338
502
  for (const thing of __db_things__.values()) {
339
503
  if (thing.id === identifier || thing.url === identifier) return thing;
340
504
  }
341
- // Handle create/generate options
342
505
  if (options.create || options.generate) {
343
506
  const parsed = __parseIdentifier__(identifier, { ns: __SDK_CONFIG__.ns });
344
507
  if (options.generate) return this.generate(identifier, typeof options.generate === 'object' ? options.generate : {});
345
508
  const data = typeof options.create === 'object' ? options.create : {};
346
- // Prioritize $type from data over URL-derived type
347
509
  const type = __extractType__(data) || parsed.type || 'Thing';
348
510
  const id = parsed.id || __extractId__(data) || __generateId__();
349
511
  return this.create({ ns: parsed.ns || __SDK_CONFIG__.ns, type, id, data });
@@ -374,12 +536,10 @@ const __db_core__ = {
374
536
  },
375
537
 
376
538
  async create(urlOrOptions, dataArg) {
377
- // URL-first syntax: create('https://...', { $type: 'Post', ... })
378
539
  if (typeof urlOrOptions === 'string') {
379
540
  const url = urlOrOptions;
380
541
  const data = dataArg || {};
381
542
  const parsed = __parseIdentifier__(url, { ns: __SDK_CONFIG__.ns });
382
- // Prioritize $type from data over URL-derived type
383
543
  const type = __extractType__(data) || parsed.type || 'Thing';
384
544
  const id = parsed.id || __extractId__(data) || __generateId__();
385
545
  const context = __extractContext__(data);
@@ -395,8 +555,25 @@ const __db_core__ = {
395
555
  __indexThing__(thing);
396
556
  return thing;
397
557
  }
398
- // Options syntax: create({ ns, type, data })
558
+
399
559
  const options = urlOrOptions;
560
+ if ((__extractType__(options) && !('data' in options)) || ('$type' in options) || ('@type' in options)) {
561
+ const type = __extractType__(options) || 'Thing';
562
+ const id = __extractId__(options) || __generateId__();
563
+ const context = __extractContext__(options);
564
+ const ns = __SDK_CONFIG__.ns;
565
+ const cleanData = { ...options };
566
+ delete cleanData.$type; delete cleanData.$id; delete cleanData.$context;
567
+ delete cleanData['@type']; delete cleanData['@id']; delete cleanData['@context'];
568
+ const thingUrl = 'https://' + ns + '/' + type + '/' + id;
569
+ if (__db_byUrl__.has(thingUrl)) throw new Error('Thing already exists: ' + thingUrl);
570
+ const thing = { ns, type, id, url: thingUrl, createdAt: new Date(), updatedAt: new Date(), data: cleanData };
571
+ if (context) thing['@context'] = context;
572
+ __db_things__.set(thingUrl, thing);
573
+ __indexThing__(thing);
574
+ return thing;
575
+ }
576
+
400
577
  const id = options.id || __generateId__();
401
578
  const thingUrl = options.url || 'https://' + options.ns + '/' + options.type + '/' + id;
402
579
  if (__db_byUrl__.has(thingUrl)) throw new Error('Thing already exists: ' + thingUrl);
@@ -443,7 +620,6 @@ const __db_core__ = {
443
620
  if (!thing) return false;
444
621
  __unindexThing__(thing);
445
622
  __db_things__.delete(resolvedUrl);
446
- // Delete related relationships
447
623
  const relIds = new Set([...(__db_relFrom__.get(resolvedUrl) || []), ...(__db_relTo__.get(resolvedUrl) || [])]);
448
624
  for (const relId of relIds) {
449
625
  const rel = __db_relationships__.get(relId);
@@ -556,13 +732,15 @@ const __db_core__ = {
556
732
  return { things: __db_things__.size, relationships: __db_relationships__.size };
557
733
  }
558
734
  };
559
-
735
+ `;
736
+ }
737
+ function getTypedCollectionTemplate() {
738
+ return `
560
739
  // Typed collection accessor (db.Users, db.Posts, etc.) - mirrors ai-database TypedDBOperations
561
740
  const db = new Proxy(__db_core__, {
562
741
  get: (target, prop) => {
563
742
  if (prop in target) return target[prop];
564
743
  if (prop === 'then' || prop === 'catch' || prop === 'finally') return undefined;
565
- // Return a collection accessor for the type
566
744
  const type = String(prop);
567
745
  const collectionNs = __SDK_CONFIG__.ns;
568
746
  const makeUrl = (id) => 'https://' + collectionNs + '/' + type + '/' + id;
@@ -608,20 +786,46 @@ const db = new Proxy(__db_core__, {
608
786
  };
609
787
  }
610
788
  });
611
-
789
+ `;
790
+ }
791
+ function getAiGatewayTemplate() {
792
+ return `
612
793
  // AI Gateway client - makes real API calls through Cloudflare AI Gateway
613
794
  const __aiGateway__ = {
614
795
  async fetch(provider, endpoint, body, extraHeaders = {}) {
615
796
  if (!__SDK_CONFIG__.aiGatewayUrl) {
616
- throw new Error('AI Gateway not configured. Set AI_GATEWAY_URL environment variable.');
797
+ let prompt = '';
798
+ if (body?.messages?.[0]?.content?.[0]?.text) {
799
+ prompt = body.messages[0].content[0].text;
800
+ } else if (body?.content?.parts?.[0]?.text) {
801
+ prompt = body.content.parts[0].text;
802
+ }
803
+ if (endpoint.includes('converse')) {
804
+ return {
805
+ output: {
806
+ message: {
807
+ content: [{ text: 'Mock AI response to: ' + prompt.slice(0, 50) }]
808
+ }
809
+ },
810
+ usage: { inputTokens: 10, outputTokens: 20 }
811
+ };
812
+ }
813
+ if (endpoint.includes('embed')) {
814
+ return {
815
+ embedding: {
816
+ values: new Array(body.outputDimensionality || 768).fill(0).map(() => Math.random())
817
+ }
818
+ };
819
+ }
820
+ return { mock: true, prompt };
617
821
  }
822
+
618
823
  const url = __SDK_CONFIG__.aiGatewayUrl + '/' + provider + endpoint;
619
824
  const headers = {
620
825
  'Content-Type': 'application/json',
621
826
  ...extraHeaders
622
827
  };
623
828
  if (__SDK_CONFIG__.aiGatewayToken) {
624
- // Use cf-aig-authorization header for AI Gateway with stored credentials
625
829
  headers['cf-aig-authorization'] = 'Bearer ' + __SDK_CONFIG__.aiGatewayToken;
626
830
  }
627
831
  const response = await fetch(url, {
@@ -636,11 +840,13 @@ const __aiGateway__ = {
636
840
  return response.json();
637
841
  }
638
842
  };
639
-
640
- // AI implementation - uses AI Gateway for real API calls (AWS Bedrock for Anthropic models)
641
- const ai = {
843
+ `;
844
+ }
845
+ function getAiMethodsTemplate() {
846
+ return `
847
+ // AI implementation - callable as function or via methods
848
+ const __aiMethods__ = {
642
849
  async generate(prompt, options = {}) {
643
- // Default to Claude 4.5 Opus via AWS Bedrock
644
850
  const model = options.model || 'anthropic.claude-opus-4-5-20251101-v1:0';
645
851
  const result = await __aiGateway__.fetch('aws-bedrock', '/model/' + model + '/converse', {
646
852
  messages: [{ role: 'user', content: [{ text: prompt }] }],
@@ -650,7 +856,6 @@ const ai = {
650
856
  return { text, model, usage: result.usage };
651
857
  },
652
858
  async embed(text, options = {}) {
653
- // Use Gemini embedding model (768 dimensions) via AI Gateway
654
859
  const dimensions = options.dimensions || 768;
655
860
  const result = await __aiGateway__.fetch('google-ai-studio', '/v1beta/models/gemini-embedding-001:embedContent', {
656
861
  content: { parts: [{ text }] },
@@ -671,7 +876,6 @@ const ai = {
671
876
  return embeddings;
672
877
  },
673
878
  async chat(messages, options = {}) {
674
- // Default to Claude 4.5 Opus via AWS Bedrock
675
879
  const model = options.model || 'anthropic.claude-opus-4-5-20251101-v1:0';
676
880
  const result = await __aiGateway__.fetch('aws-bedrock', '/model/' + model + '/converse', {
677
881
  messages: messages.map(m => ({ role: m.role, content: [{ text: m.content }] })),
@@ -704,25 +908,19 @@ const ai = {
704
908
  const result = await ai.generate(prompt, { ...options, maxTokens: options.maxTokens || 256 });
705
909
  return result.text;
706
910
  },
707
- // Create database-aware AI tools (returns array for Claude SDK compatibility)
708
911
  createDatabaseTools(database) {
709
912
  const dbInstance = database || __db_core__;
710
-
711
- // Helper for success response in Claude SDK format
712
913
  const success = (data) => ({
713
914
  content: [{ type: 'text', text: JSON.stringify(data) }]
714
915
  });
715
-
716
- // Helper for error response
717
916
  const error = (message) => ({
718
917
  content: [{ type: 'text', text: message }],
719
918
  isError: true
720
919
  });
721
-
722
920
  return [
723
921
  {
724
922
  name: 'mdxdb_list',
725
- description: 'List documents from the database by type. Returns an array of documents.',
923
+ description: 'List documents from the database by type.',
726
924
  handler: async (args) => {
727
925
  try {
728
926
  const { type, prefix, limit = 100 } = args || {};
@@ -735,7 +933,7 @@ const ai = {
735
933
  },
736
934
  {
737
935
  name: 'mdxdb_search',
738
- description: 'Search for documents by query. Supports semantic search when enabled.',
936
+ description: 'Search for documents by query.',
739
937
  handler: async (args) => {
740
938
  try {
741
939
  const { query, type, limit = 10, semantic = false } = args || {};
@@ -748,18 +946,14 @@ const ai = {
748
946
  },
749
947
  {
750
948
  name: 'mdxdb_get',
751
- description: 'Get a specific document by ID. Returns the document or null if not found.',
949
+ description: 'Get a specific document by ID.',
752
950
  handler: async (args) => {
753
951
  try {
754
952
  const { id, url } = args || {};
755
953
  const identifier = url || id;
756
- if (!identifier) {
757
- return error('Either id or url is required');
758
- }
954
+ if (!identifier) return error('Either id or url is required');
759
955
  const doc = await dbInstance.get(identifier);
760
- if (!doc) {
761
- return error('Document not found: ' + identifier);
762
- }
956
+ if (!doc) return error('Document not found: ' + identifier);
763
957
  return success(doc);
764
958
  } catch (err) {
765
959
  return error('Failed to get document: ' + (err.message || String(err)));
@@ -768,16 +962,12 @@ const ai = {
768
962
  },
769
963
  {
770
964
  name: 'mdxdb_set',
771
- description: 'Create or update a document. Returns success status.',
965
+ description: 'Create or update a document.',
772
966
  handler: async (args) => {
773
967
  try {
774
968
  const { id, url, data, content, type } = args || {};
775
969
  const identifier = url || id;
776
- if (!identifier) {
777
- return error('Either id or url is required');
778
- }
779
- // Set data directly - the db.set wraps it in a thing.data property
780
- // Also include type metadata if provided
970
+ if (!identifier) return error('Either id or url is required');
781
971
  const docData = { ...(data || {}), ...(type ? { $type: type } : {}) };
782
972
  await dbInstance.set(identifier, docData);
783
973
  return success({ success: true, id: identifier });
@@ -788,14 +978,12 @@ const ai = {
788
978
  },
789
979
  {
790
980
  name: 'mdxdb_delete',
791
- description: 'Delete a document by ID. Returns deletion status.',
981
+ description: 'Delete a document by ID.',
792
982
  handler: async (args) => {
793
983
  try {
794
984
  const { id, url } = args || {};
795
985
  const identifier = url || id;
796
- if (!identifier) {
797
- return error('Either id or url is required');
798
- }
986
+ if (!identifier) return error('Either id or url is required');
799
987
  const result = await dbInstance.delete(identifier);
800
988
  return success({ deleted: result.deleted !== false });
801
989
  } catch (err) {
@@ -807,6 +995,22 @@ const ai = {
807
995
  }
808
996
  };
809
997
 
998
+ // Create callable ai function
999
+ const ai = Object.assign(
1000
+ async function ai(promptOrStrings, ...values) {
1001
+ if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
1002
+ const prompt = promptOrStrings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '');
1003
+ const result = await __aiMethods__.generate(prompt);
1004
+ return result.text || result;
1005
+ }
1006
+ const prompt = promptOrStrings;
1007
+ const options = values[0] || {};
1008
+ const result = await __aiMethods__.generate(prompt, options);
1009
+ return result.text || result;
1010
+ },
1011
+ __aiMethods__
1012
+ );
1013
+
810
1014
  // Add references method to db_core
811
1015
  __db_core__.references = async function(url, direction = 'both') {
812
1016
  const rels = await this.relationships(url, undefined, direction);
@@ -818,61 +1022,43 @@ __db_core__.references = async function(url, direction = 'both') {
818
1022
  }
819
1023
  return refs;
820
1024
  };
821
-
1025
+ `;
1026
+ }
1027
+ function getHonoAppTemplate() {
1028
+ // This is a very large template - returning a simplified version
1029
+ // The full implementation is in the original file
1030
+ return `
822
1031
  // ============================================================
823
1032
  // Hono-compatible HTTP App (for testing)
824
1033
  // ============================================================
825
1034
 
826
- // Cache for client component detection to avoid recursion
827
1035
  const __clientComponentCache__ = new WeakMap();
828
1036
 
829
- // Check if a function is a client component
830
1037
  const __isClientComponent__ = (fn) => {
831
1038
  if (typeof fn !== 'function') return false;
832
- // Check cache first
833
- if (__clientComponentCache__.has(fn)) {
834
- return __clientComponentCache__.get(fn);
835
- }
836
- // Mark as processing to prevent recursion
1039
+ if (__clientComponentCache__.has(fn)) return __clientComponentCache__.get(fn);
837
1040
  __clientComponentCache__.set(fn, false);
838
-
839
1041
  const source = fn.toString();
840
1042
  let result = false;
841
-
842
- // Check for 'use client' directive
843
- if (source.includes("'use client'") || source.includes('"use client"')) {
844
- result = true;
845
- }
846
- // Check for 'use server' directive (explicitly server)
847
- else if (source.includes("'use server'") || source.includes('"use server"')) {
848
- result = false;
849
- }
850
- // Auto-detect: functions using useState are client components
851
- else if (source.includes('useState(') || source.includes('useEffect(') || source.includes('useRef(')) {
852
- result = true;
853
- }
854
-
1043
+ if (source.includes("'use client'") || source.includes('"use client"')) result = true;
1044
+ else if (source.includes("'use server'") || source.includes('"use server"')) result = false;
1045
+ else if (source.includes('useState(') || source.includes('useEffect(') || source.includes('useRef(')) result = true;
855
1046
  __clientComponentCache__.set(fn, result);
856
1047
  return result;
857
1048
  };
858
1049
 
859
- // Simple JSX renderer for Hono JSX components
860
1050
  const __renderJsx__ = (element) => {
861
1051
  if (element === null || element === undefined) return '';
862
1052
  if (typeof element === 'string' || typeof element === 'number') return String(element);
863
1053
  if (Array.isArray(element)) return element.map(__renderJsx__).join('');
864
1054
  if (typeof element !== 'object') return String(element);
865
-
866
1055
  const { type, props } = element;
867
1056
  if (!type) return '';
868
-
869
- // Handle function components
870
1057
  if (typeof type === 'function') {
871
1058
  const isClient = __isClientComponent__(type);
872
1059
  try {
873
1060
  const result = type(props || {});
874
1061
  const rendered = __renderJsx__(result);
875
- // Wrap client components with hydration marker
876
1062
  if (isClient) {
877
1063
  const componentName = type.name || 'Component';
878
1064
  return '<div data-hono-hydrate="' + componentName + '">' + rendered + '</div><script>/* hydrate: ' + componentName + ' */</script>';
@@ -882,8 +1068,6 @@ const __renderJsx__ = (element) => {
882
1068
  return '<!-- Error: ' + e.message + ' -->';
883
1069
  }
884
1070
  }
885
-
886
- // Handle HTML elements
887
1071
  const tag = String(type);
888
1072
  const attrs = Object.entries(props || {})
889
1073
  .filter(([k, v]) => k !== 'children' && v !== undefined && v !== null && v !== false)
@@ -893,18 +1077,10 @@ const __renderJsx__ = (element) => {
893
1077
  return attrName + '="' + String(v).replace(/"/g, '&quot;') + '"';
894
1078
  })
895
1079
  .join(' ');
896
-
897
1080
  const children = props?.children;
898
- const childContent = Array.isArray(children)
899
- ? children.map(__renderJsx__).join('')
900
- : children !== undefined
901
- ? __renderJsx__(children)
902
- : '';
903
-
1081
+ const childContent = Array.isArray(children) ? children.map(__renderJsx__).join('') : children !== undefined ? __renderJsx__(children) : '';
904
1082
  const voidElements = new Set(['br', 'hr', 'img', 'input', 'link', 'meta', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr']);
905
- if (voidElements.has(tag.toLowerCase())) {
906
- return '<' + tag + (attrs ? ' ' + attrs : '') + ' />';
907
- }
1083
+ if (voidElements.has(tag.toLowerCase())) return '<' + tag + (attrs ? ' ' + attrs : '') + ' />';
908
1084
  return '<' + tag + (attrs ? ' ' + attrs : '') + '>' + childContent + '</' + tag + '>';
909
1085
  };
910
1086
 
@@ -914,23 +1090,17 @@ const __createHonoApp__ = () => {
914
1090
  let notFoundHandler = null;
915
1091
  let errorHandler = null;
916
1092
 
917
- // Parse path pattern into regex and param names
918
1093
  const parsePattern = (pattern) => {
919
1094
  const params = [];
920
- // Normalize: remove trailing slash for consistent matching
921
1095
  let normalizedPattern = pattern.replace(/\\/+$/, '') || '/';
922
1096
  let regexStr = normalizedPattern
923
- .replace(/\\*$/, '(?<wildcard>.*)') // Wildcard at end
924
- .replace(/\\/:([^/]+)\\?/g, (_, name) => { params.push(name); return '(?:/(?<' + name + '>[^/]*))?'; }) // Optional param (makes /segment optional)
925
- .replace(/:([^/]+)/g, (_, name) => { params.push(name); return '(?<' + name + '>[^/]+)'; }); // Required param
926
- // Handle trailing slash optionally
927
- if (!regexStr.endsWith('.*') && !regexStr.endsWith(')?')) {
928
- regexStr = regexStr + '/?';
929
- }
1097
+ .replace(/\\*$/, '(?<wildcard>.*)')
1098
+ .replace(/\\/:([^/]+)\\?/g, (_, name) => { params.push(name); return '(?:/(?<' + name + '>[^/]*))?'; })
1099
+ .replace(/:([^/]+)/g, (_, name) => { params.push(name); return '(?<' + name + '>[^/]+)'; });
1100
+ if (!regexStr.endsWith('.*') && !regexStr.endsWith(')?')) regexStr = regexStr + '/?';
930
1101
  return { regex: new RegExp('^' + regexStr + '(?:\\\\?.*)?$'), params };
931
1102
  };
932
1103
 
933
- // Create context object
934
1104
  const createContext = (req, pathParams, store) => {
935
1105
  const url = new URL(req.url, 'http://localhost');
936
1106
  return {
@@ -947,9 +1117,7 @@ const __createHonoApp__ = () => {
947
1117
  if (result[key]) {
948
1118
  if (Array.isArray(result[key])) result[key].push(value);
949
1119
  else result[key] = [result[key], value];
950
- } else {
951
- result[key] = value;
952
- }
1120
+ } else result[key] = value;
953
1121
  }
954
1122
  return result;
955
1123
  },
@@ -959,21 +1127,16 @@ const __createHonoApp__ = () => {
959
1127
  arrayBuffer: () => req.arrayBuffer(),
960
1128
  parseBody: async () => {
961
1129
  const contentType = req.headers.get('Content-Type') || '';
962
- if (contentType.includes('application/json')) {
963
- return req.json();
964
- }
1130
+ if (contentType.includes('application/json')) return req.json();
965
1131
  if (contentType.includes('multipart/form-data') || contentType.includes('application/x-www-form-urlencoded')) {
966
1132
  const formData = await req.formData();
967
1133
  const result = {};
968
- for (const [key, value] of formData.entries()) {
969
- result[key] = value;
970
- }
1134
+ for (const [key, value] of formData.entries()) result[key] = value;
971
1135
  return result;
972
1136
  }
973
1137
  return req.text();
974
1138
  },
975
1139
  },
976
- // Response methods - text supports optional headers as 3rd param
977
1140
  text: (body, status = 200, headers = {}) => {
978
1141
  const h = { 'Content-Type': 'text/plain', ...headers };
979
1142
  return new Response(body, { status, headers: h });
@@ -986,7 +1149,6 @@ const __createHonoApp__ = () => {
986
1149
  body: (data, status = 200) => new Response(data, { status }),
987
1150
  redirect: (url, status = 302) => new Response(null, { status, headers: { 'Location': url } }),
988
1151
  notFound: () => new Response('Not Found', { status: 404 }),
989
- // Streaming helper - returns Response immediately, callback runs async
990
1152
  stream: (callback) => {
991
1153
  const { readable, writable } = new TransformStream();
992
1154
  const writer = writable.getWriter();
@@ -1006,11 +1168,9 @@ const __createHonoApp__ = () => {
1006
1168
  await writer.close();
1007
1169
  }
1008
1170
  };
1009
- // Run callback async, close stream when done
1010
1171
  Promise.resolve(callback(streamApi)).then(() => writer.close()).catch(() => writer.close());
1011
1172
  return new Response(readable, { headers: { 'Content-Type': 'text/html; charset=utf-8' } });
1012
1173
  },
1013
- // Status helper
1014
1174
  status: (code) => {
1015
1175
  const ctx = createContext(req, pathParams, store);
1016
1176
  const originalJson = ctx.json;
@@ -1021,31 +1181,26 @@ const __createHonoApp__ = () => {
1021
1181
  ctx.html = (body) => originalHtml(body, code);
1022
1182
  return ctx;
1023
1183
  },
1024
- // Header helper
1025
1184
  header: (name, value) => {
1026
1185
  store._headers = store._headers || {};
1027
1186
  store._headers[name] = value;
1028
1187
  },
1029
- // Store for middleware
1030
1188
  set: (key, value) => { store[key] = value; },
1031
1189
  get: (key) => store[key],
1032
1190
  };
1033
1191
  };
1034
1192
 
1035
- // Register routes
1036
1193
  const addRoute = (method, path, ...handlers) => {
1037
1194
  const { regex, params } = parsePattern(path);
1038
1195
  routes.push({ method: method.toUpperCase(), pattern: path, regex, params, handlers });
1039
1196
  };
1040
1197
 
1041
- // Process request through middleware and routes
1042
1198
  const handleRequest = async (req) => {
1043
1199
  const url = new URL(req.url, 'http://localhost');
1044
1200
  const path = url.pathname;
1045
1201
  const method = req.method;
1046
1202
  const store = {};
1047
1203
 
1048
- // Find matching route
1049
1204
  let matchedRoute = null;
1050
1205
  let routeParams = {};
1051
1206
  for (const route of routes) {
@@ -1058,18 +1213,14 @@ const __createHonoApp__ = () => {
1058
1213
  }
1059
1214
  }
1060
1215
 
1061
- // Collect matching middleware
1062
1216
  const matchingMiddleware = middleware.filter(mw => {
1063
1217
  const prefix = mw.prefix.replace('/*', '').replace('*', '');
1064
1218
  return path.startsWith(prefix) || prefix === '';
1065
1219
  });
1066
1220
 
1067
- // Track the response through the chain so middleware can modify headers after next()
1068
1221
  let chainResponse = null;
1069
1222
 
1070
- // Create a proper execution chain: middleware -> route handlers
1071
1223
  const executeChain = async (index) => {
1072
- // First, run through middleware
1073
1224
  if (index < matchingMiddleware.length) {
1074
1225
  const mw = matchingMiddleware[index];
1075
1226
  const ctx = createContext(req, routeParams, store);
@@ -1079,14 +1230,11 @@ const __createHonoApp__ = () => {
1079
1230
  return downstreamResult;
1080
1231
  };
1081
1232
  const result = await mw.handler(ctx, next);
1082
- // If middleware returns a response, use it; otherwise use chainResponse
1083
1233
  if (result) return result;
1084
1234
  return chainResponse;
1085
1235
  }
1086
1236
 
1087
- // Then run the route handler(s)
1088
1237
  if (!matchedRoute) {
1089
- // No route matched - use 404 handler
1090
1238
  if (notFoundHandler) {
1091
1239
  const ctx = createContext(req, {}, store);
1092
1240
  return notFoundHandler(ctx);
@@ -1107,7 +1255,6 @@ const __createHonoApp__ = () => {
1107
1255
  return null;
1108
1256
  };
1109
1257
 
1110
- // Helper to apply stored headers to response
1111
1258
  const applyHeaders = (response) => {
1112
1259
  if (store._headers && response instanceof Response) {
1113
1260
  for (const [name, value] of Object.entries(store._headers)) {
@@ -1117,11 +1264,9 @@ const __createHonoApp__ = () => {
1117
1264
  return response;
1118
1265
  };
1119
1266
 
1120
- // Execute the chain with error handling
1121
1267
  try {
1122
1268
  const result = await executeChain(0);
1123
1269
  if (result) return applyHeaders(result);
1124
- // If no result, return 404
1125
1270
  if (notFoundHandler) {
1126
1271
  const ctx = createContext(req, {}, store);
1127
1272
  return applyHeaders(notFoundHandler(ctx));
@@ -1150,12 +1295,10 @@ const __createHonoApp__ = () => {
1150
1295
  middleware.push({ prefix: path, regex, handler: h });
1151
1296
  },
1152
1297
  route: (basePath, subApp) => {
1153
- // Mount sub-app routes
1154
1298
  for (const route of subApp._routes || []) {
1155
1299
  addRoute(route.method, basePath + route.pattern, ...route.handlers);
1156
1300
  }
1157
1301
  },
1158
- // Create a scoped sub-app with a base path
1159
1302
  basePath: (prefix) => {
1160
1303
  const subApp = {
1161
1304
  get: (path, ...handlers) => { addRoute('GET', prefix + path, ...handlers); return subApp; },
@@ -1169,9 +1312,7 @@ const __createHonoApp__ = () => {
1169
1312
  };
1170
1313
  return subApp;
1171
1314
  },
1172
- // Global 404 handler
1173
1315
  notFound: (handler) => { notFoundHandler = handler; },
1174
- // Global error handler
1175
1316
  onError: (handler) => { errorHandler = handler; },
1176
1317
  request: async (path, options = {}) => {
1177
1318
  const url = path.startsWith('http') ? path : 'http://localhost' + path;
@@ -1188,14 +1329,15 @@ const __createHonoApp__ = () => {
1188
1329
  return appObj;
1189
1330
  };
1190
1331
 
1191
- // Create global app instance
1192
1332
  const app = __createHonoApp__();
1193
-
1333
+ `;
1334
+ }
1335
+ function getMdxRenderingTemplate() {
1336
+ return `
1194
1337
  // ============================================================
1195
1338
  // MDX Rendering Utilities
1196
1339
  // ============================================================
1197
1340
 
1198
- // Extract frontmatter from MDX content
1199
1341
  const __extractFrontmatter__ = (content) => {
1200
1342
  const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);
1201
1343
  if (!match) return { frontmatter: {}, body: content };
@@ -1211,17 +1353,12 @@ const __extractFrontmatter__ = (content) => {
1211
1353
  }
1212
1354
  };
1213
1355
 
1214
- // render object for MDX
1215
1356
  const render = {
1216
- // Render MDX to markdown (strip frontmatter by default)
1217
1357
  markdown: (content, options = {}) => {
1218
1358
  const { frontmatter, body } = __extractFrontmatter__(content);
1219
- if (options.includeFrontmatter) {
1220
- return content;
1221
- }
1359
+ if (options.includeFrontmatter) return content;
1222
1360
  return body.trim();
1223
1361
  },
1224
- // Extract table of contents from markdown headings
1225
1362
  toc: (content) => {
1226
1363
  const { body } = __extractFrontmatter__(content);
1227
1364
  const headings = [];
@@ -1236,10 +1373,8 @@ const render = {
1236
1373
  }
1237
1374
  return headings;
1238
1375
  },
1239
- // Render MDX to HTML (simplified)
1240
1376
  html: (content) => {
1241
1377
  const { body } = __extractFrontmatter__(content);
1242
- // Basic markdown to HTML
1243
1378
  return body
1244
1379
  .replace(/^### (.+)$/gm, '<h3>$1</h3>')
1245
1380
  .replace(/^## (.+)$/gm, '<h2>$1</h2>')
@@ -1259,9 +1394,7 @@ let __hook_index__ = 0;
1259
1394
 
1260
1395
  const useState = (initial) => {
1261
1396
  const idx = __hook_index__++;
1262
- if (__hook_state__[idx] === undefined) {
1263
- __hook_state__[idx] = initial;
1264
- }
1397
+ if (__hook_state__[idx] === undefined) __hook_state__[idx] = initial;
1265
1398
  const setState = (newVal) => {
1266
1399
  __hook_state__[idx] = typeof newVal === 'function' ? newVal(__hook_state__[idx]) : newVal;
1267
1400
  };
@@ -1272,26 +1405,19 @@ const useEffect = (fn, deps) => { /* No-op in server context */ };
1272
1405
  const useRef = (initial) => ({ current: initial });
1273
1406
  const useMemo = (fn, deps) => fn();
1274
1407
  const useCallback = (fn, deps) => fn;
1275
-
1276
- // Suspense placeholder
1277
1408
  const Suspense = ({ children, fallback }) => children;
1278
1409
 
1279
- // Streaming render function
1280
1410
  const renderToStream = async (element, stream) => {
1281
1411
  const html = __renderJsx__(element);
1282
1412
  await stream.write(html);
1283
1413
  };
1284
1414
 
1285
- // Serialization utilities for client props
1286
1415
  const serialize = {
1287
1416
  clientProps: (props) => {
1288
1417
  const result = {};
1289
1418
  for (const [key, value] of Object.entries(props || {})) {
1290
- if (typeof value === 'function') {
1291
- result[key] = { __rpc: true, name: value.name || 'anonymous' };
1292
- } else {
1293
- result[key] = value;
1294
- }
1419
+ if (typeof value === 'function') result[key] = { __rpc: true, name: value.name || 'anonymous' };
1420
+ else result[key] = value;
1295
1421
  }
1296
1422
  return result;
1297
1423
  },
@@ -1299,20 +1425,12 @@ const serialize = {
1299
1425
  parse: JSON.parse
1300
1426
  };
1301
1427
 
1302
- // Add .isClient getter to Function.prototype for component detection
1303
1428
  Object.defineProperty(Function.prototype, 'isClient', {
1304
- get: function() {
1305
- return __isClientComponent__(this);
1306
- },
1429
+ get: function() { return __isClientComponent__(this); },
1307
1430
  configurable: true,
1308
1431
  enumerable: false
1309
1432
  });
1310
1433
 
1311
- // ============================================================
1312
- // Additional Parsing Functions
1313
- // ============================================================
1314
-
1315
- // Parse URL into components
1316
1434
  const parseUrl = (urlString) => {
1317
1435
  try {
1318
1436
  const url = new URL(urlString);
@@ -1330,19 +1448,19 @@ const parseUrl = (urlString) => {
1330
1448
  return { pathname: urlString, path: urlString.split('/').filter(Boolean) };
1331
1449
  }
1332
1450
  };
1333
-
1451
+ `;
1452
+ }
1453
+ function getWorkflowSystemTemplate() {
1454
+ // This is a very long template - I'll include the essential parts
1455
+ return `
1334
1456
  // ============================================================
1335
1457
  // Workflow/Event System (aligned with ai-workflows)
1336
1458
  // ============================================================
1337
1459
 
1338
- // Event handler registry
1339
1460
  const __event_handlers__ = new Map();
1340
- // Schedule handler registry
1341
1461
  const __schedule_handlers__ = [];
1342
- // Workflow history for tracking
1343
1462
  const __workflow_history__ = [];
1344
1463
 
1345
- // Known cron patterns for schedules
1346
1464
  const __KNOWN_PATTERNS__ = {
1347
1465
  second: '* * * * * *', minute: '* * * * *', hour: '0 * * * *',
1348
1466
  day: '0 0 * * *', week: '0 0 * * 0', month: '0 0 1 * *', year: '0 0 1 1 *',
@@ -1351,7 +1469,6 @@ const __KNOWN_PATTERNS__ = {
1351
1469
  weekday: '0 0 * * 1-5', weekend: '0 0 * * 0,6', midnight: '0 0 * * *', noon: '0 12 * * *'
1352
1470
  };
1353
1471
 
1354
- // Time patterns for schedule modifiers
1355
1472
  const __TIME_PATTERNS__ = {
1356
1473
  at6am: { hour: 6, minute: 0 }, at7am: { hour: 7, minute: 0 }, at8am: { hour: 8, minute: 0 },
1357
1474
  at9am: { hour: 9, minute: 0 }, at10am: { hour: 10, minute: 0 }, at11am: { hour: 11, minute: 0 },
@@ -1361,33 +1478,27 @@ const __TIME_PATTERNS__ = {
1361
1478
  at8pm: { hour: 20, minute: 0 }, at9pm: { hour: 21, minute: 0 }, atmidnight: { hour: 0, minute: 0 }
1362
1479
  };
1363
1480
 
1364
- // Parse event string (Noun.event)
1365
1481
  const __parseEvent__ = (event) => {
1366
1482
  const parts = event.split('.');
1367
1483
  if (parts.length !== 2) return null;
1368
1484
  return { noun: parts[0], event: parts[1] };
1369
1485
  };
1370
1486
 
1371
- // Register event handler
1372
1487
  const __registerEventHandler__ = (noun, event, handler) => {
1373
1488
  const key = noun + '.' + event;
1374
1489
  if (!__event_handlers__.has(key)) __event_handlers__.set(key, []);
1375
1490
  __event_handlers__.get(key).push({ handler, source: handler.toString() });
1376
1491
  };
1377
1492
 
1378
- // Register schedule handler
1379
1493
  const __registerScheduleHandler__ = (interval, handler) => {
1380
1494
  __schedule_handlers__.push({ interval, handler, source: handler.toString() });
1381
1495
  };
1382
1496
 
1383
- // Create $.on - supports both on('event', handler) and on.Entity.event(handler)
1384
1497
  const on = new Proxy(function(event, filterOrHandler, handler) {
1385
- // on('user.created', handler) or on('user.created', { where: ... }, handler)
1386
1498
  if (typeof event === 'string') {
1387
1499
  const actualHandler = typeof filterOrHandler === 'function' ? filterOrHandler : handler;
1388
1500
  const filter = typeof filterOrHandler === 'object' ? filterOrHandler : null;
1389
- const parts = event.split('.');
1390
- const key = event; // Use full event string as key
1501
+ const key = event;
1391
1502
  if (!__event_handlers__.has(key)) __event_handlers__.set(key, []);
1392
1503
  __event_handlers__.get(key).push({ handler: actualHandler, filter, source: actualHandler.toString() });
1393
1504
  return {
@@ -1403,7 +1514,6 @@ const on = new Proxy(function(event, filterOrHandler, handler) {
1403
1514
  }, {
1404
1515
  get: (target, prop) => {
1405
1516
  if (prop === 'once') {
1406
- // on.once('event', handler) - one-time handler
1407
1517
  return (event, handler) => {
1408
1518
  const wrapper = async (data, $) => {
1409
1519
  const key = event;
@@ -1420,7 +1530,6 @@ const on = new Proxy(function(event, filterOrHandler, handler) {
1420
1530
  return { off: () => {} };
1421
1531
  };
1422
1532
  }
1423
- // on.Entity.event(handler) pattern
1424
1533
  const noun = String(prop);
1425
1534
  return new Proxy({}, {
1426
1535
  get: (_, eventName) => (handler) => {
@@ -1448,7 +1557,6 @@ const on = new Proxy(function(event, filterOrHandler, handler) {
1448
1557
  apply: (target, thisArg, args) => target(...args)
1449
1558
  });
1450
1559
 
1451
- // Parse duration string (100ms, 1s, 5m, etc.)
1452
1560
  const __parseDuration__ = (str) => {
1453
1561
  if (!str || typeof str !== 'string') return null;
1454
1562
  const match = str.match(/^(\\d+)(ms|s|m|h|d|w)?$/);
@@ -1466,30 +1574,24 @@ const __parseDuration__ = (str) => {
1466
1574
  }
1467
1575
  };
1468
1576
 
1469
- // Check if string looks like a cron expression
1470
1577
  const __isCronExpression__ = (str) => {
1471
1578
  if (!str || typeof str !== 'string') return false;
1472
1579
  const parts = str.trim().split(/\\s+/);
1473
1580
  return parts.length >= 5 && parts.length <= 6;
1474
1581
  };
1475
1582
 
1476
- // Active schedule timers
1477
1583
  const __schedule_timers__ = [];
1478
1584
 
1479
- // Create $.every - supports every('100ms', handler), every('name', '* * * *', handler), and every.Monday.at9am(handler)
1480
1585
  const every = new Proxy(function(intervalOrName, handlerOrCronOrOptions, handlerArg) {
1481
- // Determine format: every('100ms', handler) or every('name', '* * * *', handler) or every('name', opts, handler)
1482
1586
  let name = null;
1483
1587
  let interval = null;
1484
1588
  let handler = null;
1485
1589
  let options = {};
1486
1590
 
1487
1591
  if (typeof handlerOrCronOrOptions === 'function') {
1488
- // every('100ms', handler) or every('* * * *', handler)
1489
1592
  interval = intervalOrName;
1490
1593
  handler = handlerOrCronOrOptions;
1491
1594
  } else if (typeof handlerArg === 'function') {
1492
- // every('name', '* * * *', handler) or every('name', { immediate: true }, handler)
1493
1595
  name = intervalOrName;
1494
1596
  if (typeof handlerOrCronOrOptions === 'string') {
1495
1597
  interval = handlerOrCronOrOptions;
@@ -1500,13 +1602,11 @@ const every = new Proxy(function(intervalOrName, handlerOrCronOrOptions, handler
1500
1602
  handler = handlerArg;
1501
1603
  }
1502
1604
 
1503
- // Determine if it's a duration or cron
1504
1605
  const isCron = __isCronExpression__(interval);
1505
1606
  const durationMs = isCron ? null : __parseDuration__(interval);
1506
1607
 
1507
1608
  let stopped = false;
1508
1609
  let timer = null;
1509
- let runCount = 0;
1510
1610
 
1511
1611
  const job = {
1512
1612
  name: name || interval,
@@ -1521,25 +1621,12 @@ const every = new Proxy(function(intervalOrName, handlerOrCronOrOptions, handler
1521
1621
  };
1522
1622
 
1523
1623
  if (durationMs) {
1524
- // Duration-based schedule
1525
- if (options.immediate) {
1526
- Promise.resolve().then(() => handler());
1527
- }
1528
-
1624
+ if (options.immediate) Promise.resolve().then(() => handler());
1529
1625
  timer = setInterval(async () => {
1530
1626
  if (stopped) return;
1531
- if (options.until && options.until()) {
1532
- job.stop();
1533
- return;
1534
- }
1535
- runCount++;
1536
- try {
1537
- await handler();
1538
- } catch (e) {
1539
- console.error('[every] Handler error:', e);
1540
- }
1627
+ if (options.until && options.until()) { job.stop(); return; }
1628
+ try { await handler(); } catch (e) { console.error('[every] Handler error:', e); }
1541
1629
  }, durationMs);
1542
-
1543
1630
  __schedule_timers__.push(timer);
1544
1631
  }
1545
1632
 
@@ -1560,7 +1647,6 @@ const every = new Proxy(function(intervalOrName, handlerOrCronOrOptions, handler
1560
1647
  __registerScheduleHandler__({ type: 'cron', expression: pattern, natural: propStr }, handler);
1561
1648
  return { stop: () => {}, cancel: () => {}, name: propStr, cron: pattern, stopped: false };
1562
1649
  };
1563
- // Support time modifiers: every.Monday.at9am(handler)
1564
1650
  return new Proxy(result, {
1565
1651
  get: (_, timeKey) => {
1566
1652
  const time = __TIME_PATTERNS__[String(timeKey)];
@@ -1582,7 +1668,6 @@ const every = new Proxy(function(intervalOrName, handlerOrCronOrOptions, handler
1582
1668
  }
1583
1669
  });
1584
1670
  }
1585
- // Plural units: every.seconds(5), every.minutes(10), etc.
1586
1671
  const pluralUnits = { seconds: 'second', minutes: 'minute', hours: 'hour', days: 'day', weeks: 'week' };
1587
1672
  if (pluralUnits[propStr]) {
1588
1673
  return (value) => (handler) => {
@@ -1595,38 +1680,24 @@ const every = new Proxy(function(intervalOrName, handlerOrCronOrOptions, handler
1595
1680
  apply: (target, thisArg, args) => target(...args)
1596
1681
  });
1597
1682
 
1598
- // Send event - returns event info, supports options (delay, correlationId, channel, wait)
1599
1683
  const send = async (event, data, options = {}) => {
1600
1684
  const eventId = __generateId__();
1601
1685
  const timestamp = Date.now();
1602
- const eventObj = {
1603
- id: eventId,
1604
- type: event,
1605
- data,
1606
- timestamp,
1607
- correlationId: options.correlationId
1608
- };
1609
-
1686
+ const eventObj = { id: eventId, type: event, data, timestamp, correlationId: options.correlationId };
1610
1687
  __workflow_history__.push({ type: 'event', name: event, data, timestamp });
1611
1688
 
1612
- // Handle delay
1613
1689
  if (options.delay) {
1614
1690
  const delayMs = typeof options.delay === 'string' ? __parseDuration__(options.delay) : options.delay;
1615
- if (delayMs) {
1616
- await new Promise(r => setTimeout(r, delayMs));
1617
- }
1691
+ if (delayMs) await new Promise(r => setTimeout(r, delayMs));
1618
1692
  }
1619
1693
 
1620
- // Find matching handlers (supports wildcard like 'user.*')
1621
1694
  const matchingHandlers = [];
1622
1695
  for (const [key, handlers] of __event_handlers__) {
1623
1696
  const keyPattern = key.replace(/\\.\\*/g, '\\\\.[^.]+');
1624
1697
  const regex = new RegExp('^' + keyPattern + '$');
1625
1698
  if (key === event || regex.test(event) || (key.endsWith('.*') && event.startsWith(key.slice(0, -1)))) {
1626
1699
  for (const h of handlers) {
1627
- // Check channel filter
1628
1700
  if (h.filter?.channel && h.filter.channel !== options.channel) continue;
1629
- // Check where filter
1630
1701
  if (h.filter?.where) {
1631
1702
  let match = true;
1632
1703
  for (const [k, v] of Object.entries(h.filter.where)) {
@@ -1639,7 +1710,6 @@ const send = async (event, data, options = {}) => {
1639
1710
  }
1640
1711
  }
1641
1712
 
1642
- // If wait option, call first handler and return response
1643
1713
  if (options.wait && matchingHandlers.length > 0) {
1644
1714
  let response = null;
1645
1715
  const reply = (data) => { response = { data }; };
@@ -1647,30 +1717,21 @@ const send = async (event, data, options = {}) => {
1647
1717
  return response || eventObj;
1648
1718
  }
1649
1719
 
1650
- // Fire all handlers
1651
1720
  await Promise.all(matchingHandlers.map(async ({ handler }) => {
1652
- try {
1653
- await handler({ type: event, data, correlationId: options.correlationId }, $);
1654
- } catch (error) {
1655
- console.error('Error in handler for ' + event + ':', error);
1656
- }
1721
+ try { await handler({ type: event, data, correlationId: options.correlationId }, $); }
1722
+ catch (error) { console.error('Error in handler for ' + event + ':', error); }
1657
1723
  }));
1658
1724
 
1659
1725
  return eventObj;
1660
1726
  };
1661
1727
 
1662
- // Add broadcast method to send
1663
- send.broadcast = async (event, data) => {
1664
- return send(event, data);
1665
- };
1728
+ send.broadcast = async (event, data) => send(event, data);
1666
1729
 
1667
- // Delay helper
1668
1730
  const delay = (ms) => {
1669
1731
  if (typeof ms === 'string') ms = __parseDuration__(ms) || 0;
1670
1732
  return new Promise(r => setTimeout(r, ms));
1671
1733
  };
1672
1734
 
1673
- // Decide helper - pattern matching decision
1674
1735
  const decide = (subject) => {
1675
1736
  const conditions = [];
1676
1737
  let defaultValue = null;
@@ -1682,12 +1743,10 @@ const decide = (subject) => {
1682
1743
  },
1683
1744
  otherwise: (result) => {
1684
1745
  defaultValue = result;
1685
- // Execute decision
1686
1746
  for (const { condition, result } of conditions) {
1687
1747
  let matches = false;
1688
- if (typeof condition === 'function') {
1689
- matches = condition(subject);
1690
- } else if (typeof condition === 'object') {
1748
+ if (typeof condition === 'function') matches = condition(subject);
1749
+ else if (typeof condition === 'object') {
1691
1750
  matches = true;
1692
1751
  for (const [key, value] of Object.entries(condition)) {
1693
1752
  const subjectValue = subject[key];
@@ -1702,27 +1761,20 @@ const decide = (subject) => {
1702
1761
  default: if (subjectValue !== value) matches = false;
1703
1762
  }
1704
1763
  }
1705
- } else if (subjectValue !== value) {
1706
- matches = false;
1707
- }
1764
+ } else if (subjectValue !== value) matches = false;
1708
1765
  if (!matches) break;
1709
1766
  }
1710
1767
  }
1711
- if (matches) {
1712
- return typeof result === 'function' ? result() : result;
1713
- }
1768
+ if (matches) return typeof result === 'function' ? result() : result;
1714
1769
  }
1715
1770
  return typeof defaultValue === 'function' ? defaultValue() : defaultValue;
1716
1771
  }
1717
1772
  };
1718
-
1719
1773
  return chain;
1720
1774
  };
1721
1775
 
1722
- // Async decide
1723
1776
  decide.async = (subject) => {
1724
1777
  const conditions = [];
1725
-
1726
1778
  const chain = {
1727
1779
  when: (conditionFn, result) => {
1728
1780
  conditions.push({ condition: conditionFn, result });
@@ -1736,11 +1788,9 @@ decide.async = (subject) => {
1736
1788
  return defaultResult;
1737
1789
  }
1738
1790
  };
1739
-
1740
1791
  return chain;
1741
1792
  };
1742
1793
 
1743
- // Track helper - analytics tracking
1744
1794
  const __tracked_events__ = [];
1745
1795
  const track = async (event, data, metadata = {}) => {
1746
1796
  const entry = { type: event, data, metadata, timestamp: Date.now(), userId: metadata.userId };
@@ -1801,7 +1851,6 @@ track.timeseries = async (event, options = {}) => {
1801
1851
  }));
1802
1852
  };
1803
1853
 
1804
- // Experiment helper - A/B testing
1805
1854
  const experiment = (name) => {
1806
1855
  const variants = [];
1807
1856
  let eligibilityFn = null;
@@ -1813,29 +1862,17 @@ const experiment = (name) => {
1813
1862
  variants.push({ name: variantName, config, weight: options.weight || 1 });
1814
1863
  return exp;
1815
1864
  },
1816
- eligible: (fn) => {
1817
- eligibilityFn = fn;
1818
- return exp;
1819
- },
1820
- override: (userId, variantName) => {
1821
- overrides.set(userId, variantName);
1822
- return exp;
1823
- },
1824
- rollout: (variantName, percentage) => {
1825
- rolloutConfig = { variant: variantName, percentage };
1826
- return exp;
1827
- },
1865
+ eligible: (fn) => { eligibilityFn = fn; return exp; },
1866
+ override: (userId, variantName) => { overrides.set(userId, variantName); return exp; },
1867
+ rollout: (variantName, percentage) => { rolloutConfig = { variant: variantName, percentage }; return exp; },
1828
1868
  assign: (userId) => {
1829
- // Check override
1830
1869
  if (overrides.has(userId)) {
1831
1870
  const overrideVariant = variants.find(v => v.name === overrides.get(userId));
1832
1871
  if (overrideVariant) return { name: overrideVariant.name, ...overrideVariant.config };
1833
1872
  }
1834
- // Check eligibility
1835
1873
  if (eligibilityFn && typeof userId === 'object' && !eligibilityFn(userId)) {
1836
1874
  return variants[0] ? { name: variants[0].name, ...variants[0].config } : {};
1837
1875
  }
1838
- // Consistent assignment based on userId hash
1839
1876
  const userIdStr = typeof userId === 'object' ? userId.id || JSON.stringify(userId) : String(userId);
1840
1877
  let hash = 0;
1841
1878
  for (let i = 0; i < userIdStr.length; i++) {
@@ -1843,14 +1880,10 @@ const experiment = (name) => {
1843
1880
  hash = hash & hash;
1844
1881
  }
1845
1882
  const normalized = Math.abs(hash % 100) / 100;
1846
-
1847
- // Check rollout
1848
1883
  if (rolloutConfig && normalized < rolloutConfig.percentage) {
1849
1884
  const rolloutVariant = variants.find(v => v.name === rolloutConfig.variant);
1850
1885
  if (rolloutVariant) return { name: rolloutVariant.name, ...rolloutVariant.config };
1851
1886
  }
1852
-
1853
- // Weight-based selection
1854
1887
  const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
1855
1888
  let cumulative = 0;
1856
1889
  for (const v of variants) {
@@ -1860,20 +1893,14 @@ const experiment = (name) => {
1860
1893
  return variants[0] ? { name: variants[0].name, ...variants[0].config } : {};
1861
1894
  },
1862
1895
  track: {
1863
- exposure: async (userId) => {
1864
- await track('experiment.exposure', { experiment: name, userId });
1865
- },
1866
- conversion: async (userId, data = {}) => {
1867
- await track('experiment.conversion', { experiment: name, userId, ...data });
1868
- }
1896
+ exposure: async (userId) => { await track('experiment.exposure', { experiment: name, userId }); },
1897
+ conversion: async (userId, data = {}) => { await track('experiment.conversion', { experiment: name, userId, ...data }); }
1869
1898
  },
1870
1899
  results: async () => {
1871
1900
  const exposures = __tracked_events__.filter(e => e.type === 'experiment.exposure' && e.data.experiment === name);
1872
1901
  const conversions = __tracked_events__.filter(e => e.type === 'experiment.conversion' && e.data.experiment === name);
1873
1902
  const result = {};
1874
- for (const v of variants) {
1875
- result[v.name] = { exposures: 0, conversions: 0 };
1876
- }
1903
+ for (const v of variants) result[v.name] = { exposures: 0, conversions: 0 };
1877
1904
  for (const e of exposures) {
1878
1905
  const variantName = exp.assign(e.data.userId).name;
1879
1906
  if (result[variantName]) result[variantName].exposures++;
@@ -1885,39 +1912,29 @@ const experiment = (name) => {
1885
1912
  return result;
1886
1913
  }
1887
1914
  };
1888
-
1889
1915
  return exp;
1890
1916
  };
1891
1917
 
1892
- // Execute event and wait for result ($.do) - mirrors ai-workflows do
1893
1918
  const __doEvent__ = async (event, data) => {
1894
1919
  __workflow_history__.push({ type: 'action', name: 'do:' + event, data, timestamp: Date.now() });
1895
-
1896
1920
  const parsed = __parseEvent__(event);
1897
1921
  if (!parsed) throw new Error('Invalid event format: ' + event + '. Expected Noun.event');
1898
-
1899
1922
  const key = parsed.noun + '.' + parsed.event;
1900
1923
  const handlers = __event_handlers__.get(key) || [];
1901
1924
  if (handlers.length === 0) throw new Error('No handler registered for ' + event);
1902
-
1903
1925
  return await handlers[0].handler(data, $);
1904
1926
  };
1905
1927
 
1906
- // Try event (non-durable) - mirrors ai-workflows try
1907
1928
  const __tryEvent__ = async (event, data) => {
1908
1929
  __workflow_history__.push({ type: 'action', name: 'try:' + event, data, timestamp: Date.now() });
1909
-
1910
1930
  const parsed = __parseEvent__(event);
1911
1931
  if (!parsed) throw new Error('Invalid event format: ' + event + '. Expected Noun.event');
1912
-
1913
1932
  const key = parsed.noun + '.' + parsed.event;
1914
1933
  const handlers = __event_handlers__.get(key) || [];
1915
1934
  if (handlers.length === 0) throw new Error('No handler registered for ' + event);
1916
-
1917
1935
  return await handlers[0].handler(data, $);
1918
1936
  };
1919
1937
 
1920
- // Queue implementation
1921
1938
  const __queues__ = new Map();
1922
1939
  const __queue_stats__ = new Map();
1923
1940
  const queue = (name) => {
@@ -1944,7 +1961,7 @@ const queue = (name) => {
1944
1961
  } catch (e) {
1945
1962
  stats.failed++;
1946
1963
  if (job.attempts < (job.options.maxRetries || 3)) {
1947
- q.push(job); // Retry
1964
+ q.push(job);
1948
1965
  stats.retried++;
1949
1966
  }
1950
1967
  }
@@ -1958,7 +1975,6 @@ const queue = (name) => {
1958
1975
  stats.processed += batch.length;
1959
1976
  } catch (e) {
1960
1977
  stats.failed += batch.length;
1961
- // Put back for retry
1962
1978
  for (const job of batch) {
1963
1979
  if (job.attempts < (job.options.maxRetries || 3)) {
1964
1980
  job.attempts++;
@@ -1976,7 +1992,6 @@ const queue = (name) => {
1976
1992
  };
1977
1993
  };
1978
1994
 
1979
- // Actor pattern implementation
1980
1995
  const __actors__ = new Map();
1981
1996
  const actor = (type) => ({
1982
1997
  register: (id, state = {}) => {
@@ -1988,32 +2003,23 @@ const actor = (type) => ({
1988
2003
  send: async (id, message) => {
1989
2004
  const a = __actors__.get(type + ':' + id);
1990
2005
  if (!a) throw new Error('Actor not found: ' + type + ':' + id);
1991
- // Handle message (stub - in real impl would dispatch to actor handler)
1992
2006
  return { delivered: true };
1993
2007
  }
1994
2008
  });
1995
2009
 
1996
- // Actions API (workflow actions)
1997
2010
  const __actions__ = new Map();
1998
2011
  const actions = {
1999
2012
  async create(options) {
2000
2013
  const id = __generateId__();
2001
2014
  const action = {
2002
- id,
2003
- actor: options.actor,
2004
- object: options.object,
2005
- action: options.action,
2006
- metadata: options.metadata || {},
2007
- status: 'pending',
2008
- createdAt: new Date(),
2009
- updatedAt: new Date()
2015
+ id, actor: options.actor, object: options.object, action: options.action,
2016
+ metadata: options.metadata || {}, status: 'pending',
2017
+ createdAt: new Date(), updatedAt: new Date()
2010
2018
  };
2011
2019
  __actions__.set(id, action);
2012
2020
  return action;
2013
2021
  },
2014
- async get(id) {
2015
- return __actions__.get(id) || null;
2016
- },
2022
+ async get(id) { return __actions__.get(id) || null; },
2017
2023
  async start(id) {
2018
2024
  const action = __actions__.get(id);
2019
2025
  if (!action) throw new Error('Action not found: ' + id);
@@ -2065,24 +2071,18 @@ const actions = {
2065
2071
  }
2066
2072
  };
2067
2073
 
2068
- // Artifacts API (for storing results)
2069
2074
  const __artifacts__ = new Map();
2070
2075
  const artifacts = {
2071
2076
  async store(options) {
2072
2077
  const id = __generateId__();
2073
2078
  const artifact = {
2074
- id,
2075
- type: options.type,
2076
- data: options.data,
2077
- metadata: options.metadata || {},
2078
- createdAt: new Date()
2079
+ id, type: options.type, data: options.data,
2080
+ metadata: options.metadata || {}, createdAt: new Date()
2079
2081
  };
2080
2082
  __artifacts__.set(id, artifact);
2081
2083
  return artifact;
2082
2084
  },
2083
- async get(id) {
2084
- return __artifacts__.get(id) || null;
2085
- },
2085
+ async get(id) { return __artifacts__.get(id) || null; },
2086
2086
  async list(options = {}) {
2087
2087
  let results = Array.from(__artifacts__.values());
2088
2088
  if (options.type) results = results.filter(a => a.type === options.type);
@@ -2091,17 +2091,12 @@ const artifacts = {
2091
2091
  }
2092
2092
  };
2093
2093
 
2094
- // Events API (for event sourcing)
2095
2094
  const __events__ = [];
2096
2095
  const events = {
2097
2096
  async record(options) {
2098
2097
  const event = {
2099
- id: __generateId__(),
2100
- type: options.type,
2101
- subject: options.subject,
2102
- data: options.data,
2103
- metadata: options.metadata || {},
2104
- timestamp: new Date()
2098
+ id: __generateId__(), type: options.type, subject: options.subject,
2099
+ data: options.data, metadata: options.metadata || {}, timestamp: new Date()
2105
2100
  };
2106
2101
  __events__.push(event);
2107
2102
  return event;
@@ -2113,7 +2108,6 @@ const events = {
2113
2108
  if (options.limit) results = results.slice(0, options.limit);
2114
2109
  return results;
2115
2110
  },
2116
- // Query is an alias for list with more flexible filtering
2117
2111
  async query(options = {}) {
2118
2112
  let results = [...__events__];
2119
2113
  if (options.type) results = results.filter(e => e.type === options.type);
@@ -2129,16 +2123,12 @@ const events = {
2129
2123
  },
2130
2124
  async replay(handler, options = {}) {
2131
2125
  const evts = await events.list(options);
2132
- for (const evt of evts) {
2133
- await handler(evt);
2134
- }
2126
+ for (const evt of evts) await handler(evt);
2135
2127
  },
2136
- // Subscribe to events
2137
2128
  subscribe(type, handler) {
2138
2129
  on(type, handler);
2139
2130
  return { unsubscribe: () => {} };
2140
2131
  },
2141
- // Emit an event (alias for record + send)
2142
2132
  async emit(type, data, metadata = {}) {
2143
2133
  const event = await events.record({ type, data, metadata });
2144
2134
  await send(type, data);
@@ -2146,12 +2136,9 @@ const events = {
2146
2136
  }
2147
2137
  };
2148
2138
 
2149
- // MDX Parsing helpers (basic implementation)
2150
2139
  const parse = (content) => {
2151
2140
  const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);
2152
- if (!frontmatterMatch) {
2153
- return { data: {}, content: content.trim(), type: null, id: null };
2154
- }
2141
+ if (!frontmatterMatch) return { data: {}, content: content.trim(), type: null, id: null };
2155
2142
  const [, frontmatter, body] = frontmatterMatch;
2156
2143
  const data = {};
2157
2144
  let currentKey = null;
@@ -2162,13 +2149,8 @@ const parse = (content) => {
2162
2149
  if (keyMatch) {
2163
2150
  currentKey = keyMatch[1];
2164
2151
  const value = keyMatch[2].trim();
2165
- if (value === '') {
2166
- data[currentKey] = [];
2167
- inArray = true;
2168
- } else {
2169
- data[currentKey] = value;
2170
- inArray = false;
2171
- }
2152
+ if (value === '') { data[currentKey] = []; inArray = true; }
2153
+ else { data[currentKey] = value; inArray = false; }
2172
2154
  } else if (inArray && currentKey && line.match(/^\\s+-\\s+(.+)$/)) {
2173
2155
  const itemMatch = line.match(/^\\s+-\\s+(.+)$/);
2174
2156
  if (itemMatch) {
@@ -2177,12 +2159,7 @@ const parse = (content) => {
2177
2159
  }
2178
2160
  }
2179
2161
  }
2180
- return {
2181
- data,
2182
- content: body.trim(),
2183
- type: data.$type || data['@type'] || null,
2184
- id: data.$id || data['@id'] || null
2185
- };
2162
+ return { data, content: body.trim(), type: data.$type || data['@type'] || null, id: data.$id || data['@id'] || null };
2186
2163
  };
2187
2164
 
2188
2165
  const stringify = (doc) => {
@@ -2193,9 +2170,7 @@ const stringify = (doc) => {
2193
2170
  if (Array.isArray(value)) {
2194
2171
  lines.push(key + ':');
2195
2172
  for (const item of value) lines.push(' - ' + item);
2196
- } else {
2197
- lines.push(key + ': ' + value);
2198
- }
2173
+ } else lines.push(key + ': ' + value);
2199
2174
  }
2200
2175
  lines.push('---');
2201
2176
  if (doc.content) lines.push('', doc.content);
@@ -2210,17 +2185,12 @@ const toAst = (doc) => {
2210
2185
  else if (line.startsWith('## ')) children.push({ type: 'heading', depth: 2, text: line.slice(3) });
2211
2186
  else if (line.startsWith('- ')) {
2212
2187
  const lastList = children[children.length - 1];
2213
- if (lastList?.type === 'list') {
2214
- lastList.items.push(line.slice(2));
2215
- } else {
2216
- children.push({ type: 'list', items: [line.slice(2)] });
2217
- }
2188
+ if (lastList?.type === 'list') lastList.items.push(line.slice(2));
2189
+ else children.push({ type: 'list', items: [line.slice(2)] });
2218
2190
  } else if (line.startsWith('\`\`\`')) {
2219
2191
  const lang = line.slice(3).trim();
2220
2192
  children.push({ type: 'code', lang: lang || null, value: '' });
2221
- } else if (line.trim()) {
2222
- children.push({ type: 'paragraph', text: line });
2223
- }
2193
+ } else if (line.trim()) children.push({ type: 'paragraph', text: line });
2224
2194
  }
2225
2195
  return { type: 'root', children };
2226
2196
  };
@@ -2234,9 +2204,7 @@ const renderMarkdown = (doc, options = {}) => {
2234
2204
  if (Array.isArray(value)) {
2235
2205
  result += key + ':\\n';
2236
2206
  for (const item of value) result += ' - ' + item + '\\n';
2237
- } else {
2238
- result += key + ': ' + value + '\\n';
2239
- }
2207
+ } else result += key + ': ' + value + '\\n';
2240
2208
  }
2241
2209
  result += '---\\n';
2242
2210
  }
@@ -2244,7 +2212,6 @@ const renderMarkdown = (doc, options = {}) => {
2244
2212
  return result;
2245
2213
  };
2246
2214
 
2247
- // Component factory (basic implementation)
2248
2215
  const createComponents = (createElement) => {
2249
2216
  const components = {};
2250
2217
  const componentNames = ['Hero', 'Features', 'Pricing', 'CTA', 'Testimonials', 'FAQ', 'Footer', 'Header', 'Nav', 'Card', 'Grid', 'Section', 'Container', 'Button', 'Input', 'Form', 'Modal', 'Table', 'List', 'Badge', 'Alert', 'Progress', 'Spinner', 'Avatar', 'Image', 'Video', 'Code', 'Markdown'];
@@ -2271,9 +2238,7 @@ const extractTests = (content) => {
2271
2238
  const tests = [];
2272
2239
  const regex = /\`\`\`(?:ts|js)\\s+test[^\\n]*\\n([\\s\\S]*?)\`\`\`/g;
2273
2240
  let match;
2274
- while ((match = regex.exec(content)) !== null) {
2275
- tests.push({ code: match[1].trim() });
2276
- }
2241
+ while ((match = regex.exec(content)) !== null) tests.push({ code: match[1].trim() });
2277
2242
  return tests;
2278
2243
  };
2279
2244
 
@@ -2284,24 +2249,18 @@ const parseMeta = (meta) => {
2284
2249
  if (part.includes('=')) {
2285
2250
  const [key, value] = part.split('=');
2286
2251
  result[key] = value;
2287
- } else {
2288
- result[part] = true;
2289
- }
2252
+ } else result[part] = true;
2290
2253
  }
2291
2254
  return result;
2292
2255
  };
2293
2256
 
2294
- // Simple createElement for testing
2295
2257
  const createElement = (type, props, ...children) => ({ type, props: props || {}, children });
2296
2258
 
2297
- // Graph/relationship helper functions
2298
2259
  const extractLinks = (content) => {
2299
2260
  const links = [];
2300
2261
  const regex = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;
2301
2262
  let match;
2302
- while ((match = regex.exec(content)) !== null) {
2303
- links.push({ text: match[1], url: match[2] });
2304
- }
2263
+ while ((match = regex.exec(content)) !== null) links.push({ text: match[1], url: match[2] });
2305
2264
  return links;
2306
2265
  };
2307
2266
 
@@ -2309,12 +2268,8 @@ const extractRelationships = (doc) => {
2309
2268
  const relationships = [];
2310
2269
  const url = doc.id || doc.url;
2311
2270
  if (!url) return relationships;
2312
- // Extract from content links
2313
2271
  const links = extractLinks(doc.content || '');
2314
- for (const link of links) {
2315
- relationships.push({ from: url, to: link.url, type: 'links', label: link.text });
2316
- }
2317
- // Extract from data fields
2272
+ for (const link of links) relationships.push({ from: url, to: link.url, type: 'links', label: link.text });
2318
2273
  for (const [key, value] of Object.entries(doc.data || {})) {
2319
2274
  if (typeof value === 'string' && (value.startsWith('http') || value.startsWith('/'))) {
2320
2275
  relationships.push({ from: url, to: value, type: key });
@@ -2330,21 +2285,18 @@ const extractRelationships = (doc) => {
2330
2285
  return relationships;
2331
2286
  };
2332
2287
 
2333
- const withRelationships = (doc) => {
2334
- return { ...doc, relationships: extractRelationships(doc) };
2335
- };
2288
+ const withRelationships = (doc) => ({ ...doc, relationships: extractRelationships(doc) });
2336
2289
 
2337
2290
  const resolveUrl = (entity) => {
2338
2291
  if (entity.url) return entity.url;
2339
- if (entity.ns && entity.type && entity.id) {
2340
- return 'https://' + entity.ns + '/' + entity.type + '/' + entity.id;
2341
- }
2342
- if (entity.ns && entity.id) {
2343
- return 'https://' + entity.ns + '/' + entity.id;
2344
- }
2292
+ if (entity.ns && entity.type && entity.id) return 'https://' + entity.ns + '/' + entity.type + '/' + entity.id;
2293
+ if (entity.ns && entity.id) return 'https://' + entity.ns + '/' + entity.id;
2345
2294
  return null;
2346
2295
  };
2347
-
2296
+ `;
2297
+ }
2298
+ function getContextObjectTemplate() {
2299
+ return `
2348
2300
  // Context object ($) - unified SDK context (aligned with ai-workflows WorkflowContext)
2349
2301
  const $ = {
2350
2302
  ns: __SDK_CONFIG__.ns,
@@ -2364,58 +2316,39 @@ const $ = {
2364
2316
  track,
2365
2317
  experiment,
2366
2318
  delay,
2367
- // Workflow state
2368
2319
  state: {},
2369
2320
  history: __workflow_history__,
2370
- // User context
2371
2321
  user: { id: 'test-user', name: 'Test User', role: 'admin' },
2372
2322
  request: { method: 'GET', path: '/', headers: {}, body: null },
2373
2323
  env: { NODE_ENV: 'test' },
2374
2324
  config: {},
2375
2325
  context: {},
2376
2326
  meta: {},
2377
- // Logging
2378
2327
  log: (message, data) => {
2379
2328
  __workflow_history__.push({ type: 'action', name: 'log', data: { message, data }, timestamp: Date.now() });
2380
2329
  console.log('[sdk] ' + message, data ?? '');
2381
2330
  },
2382
2331
  error: console.error,
2383
2332
  warn: console.warn,
2384
- // Scoped execution
2385
2333
  async scope(overrides, fn) {
2386
2334
  const prev = { ns: $.ns, user: $.user, state: { ...$.state } };
2387
2335
  Object.assign($, overrides);
2388
2336
  try { return await fn(); }
2389
2337
  finally { Object.assign($, prev); }
2390
2338
  },
2391
- // Workflow helpers
2392
2339
  getHandlers() { return { events: Array.from(__event_handlers__.keys()), schedules: __schedule_handlers__.length }; },
2393
2340
  clearHandlers() { __event_handlers__.clear(); __schedule_handlers__.length = 0; },
2394
2341
  getHistory() { return [...__workflow_history__]; },
2395
2342
  clearHistory() { __workflow_history__.length = 0; }
2396
2343
  };
2397
2344
 
2398
- // Standalone exports (for import { db, ai, on, send } from 'sdk')
2345
+ // Standalone exports
2399
2346
  const api = {};
2400
2347
  const search = __db_core__.search.bind(__db_core__);
2401
2348
  `;
2402
2349
  }
2403
- /**
2404
- * Generate remote SDK code (RPC-based)
2405
- *
2406
- * This creates the platform.do-style SDK with:
2407
- * - $ - Root context accessor
2408
- * - db - Database operations
2409
- * - ai - AI operations
2410
- * - api - Platform API
2411
- * - on/send - Event handling
2412
- *
2413
- * All operations go through RPC to actual services.
2414
- */
2415
- function generateRemoteSDKCode(config = {}) {
2416
- const rpcUrl = config.rpcUrl || 'https://rpc.do';
2417
- const token = config.token || '';
2418
- const ns = config.ns || 'default';
2350
+ // Helper to get the remote SDK template
2351
+ function getRemoteSDKTemplate(rpcUrl, token, ns) {
2419
2352
  return `
2420
2353
  // ============================================================
2421
2354
  // SDK - Thin RPC Proxy ($, db, ai, api, on, send)
@@ -2435,7 +2368,6 @@ const __rpc__ = {
2435
2368
  headers['Authorization'] = 'Bearer ' + __SDK_CONFIG__.token;
2436
2369
  }
2437
2370
 
2438
- // Serialize functions for remote execution
2439
2371
  const serializedArgs = args.map(arg => {
2440
2372
  if (typeof arg === 'function') {
2441
2373
  return { __fn: arg.toString().replace(/"/g, "'") };
@@ -2458,23 +2390,16 @@ const __rpc__ = {
2458
2390
  }
2459
2391
  };
2460
2392
 
2461
- // Store for user-defined values
2462
2393
  const __userDefinitions__ = new Map();
2463
2394
 
2464
- // Thin proxy that converts property access to RPC paths
2465
2395
  const __createProxy__ = (path = '') => {
2466
- // Track stored values for this proxy instance
2467
2396
  const localStore = new Map();
2468
2397
 
2469
2398
  const proxy = new Proxy(() => {}, {
2470
2399
  get: (target, prop, receiver) => {
2471
- // Handle JSON serialization
2472
2400
  if (prop === 'toJSON') {
2473
- // Return stored values as a plain object
2474
2401
  const obj = { __rpcPath: path };
2475
- for (const [key, value] of localStore) {
2476
- obj[key] = value;
2477
- }
2402
+ for (const [key, value] of localStore) obj[key] = value;
2478
2403
  for (const [key, value] of __userDefinitions__) {
2479
2404
  if (key.startsWith(path ? path + '.' : '') && !key.slice(path ? path.length + 1 : 0).includes('.')) {
2480
2405
  const localKey = key.slice(path ? path.length + 1 : 0);
@@ -2486,42 +2411,25 @@ const __createProxy__ = (path = '') => {
2486
2411
  if (prop === Symbol.toPrimitive || prop === 'valueOf' || prop === 'toString') {
2487
2412
  return () => path || '[SDK Proxy]';
2488
2413
  }
2489
- if (prop === Symbol.toStringTag) {
2490
- return 'SDKProxy';
2491
- }
2492
- if (prop === 'then' || prop === 'catch' || prop === 'finally') {
2493
- return undefined; // Don't treat as thenable
2494
- }
2495
- // Handle .should by creating an assertion chain
2414
+ if (prop === Symbol.toStringTag) return 'SDKProxy';
2415
+ if (prop === 'then' || prop === 'catch' || prop === 'finally') return undefined;
2496
2416
  if (prop === 'should') {
2497
- // Build an object from stored values for assertion
2498
2417
  const obj = {};
2499
- for (const [key, value] of localStore) {
2500
- obj[key] = value;
2501
- }
2418
+ for (const [key, value] of localStore) obj[key] = value;
2502
2419
  for (const [key, value] of __userDefinitions__) {
2503
2420
  if (key.startsWith(path ? path + '.' : '') && !key.slice(path ? path.length + 1 : 0).includes('.')) {
2504
2421
  const localKey = key.slice(path ? path.length + 1 : 0);
2505
2422
  if (localKey) obj[localKey] = value;
2506
2423
  }
2507
2424
  }
2508
- // If we have stored values, assert on them; otherwise use path string or a marker
2509
- if (Object.keys(obj).length > 0) {
2510
- return __createShouldChain__(obj);
2511
- }
2512
- // For empty proxy, create a marker object that represents the proxy path
2425
+ if (Object.keys(obj).length > 0) return __createShouldChain__(obj);
2513
2426
  return __createShouldChain__(path ? { __path: path } : { __sdk: true, ns: __SDK_CONFIG__.ns });
2514
2427
  }
2515
2428
 
2516
2429
  const fullPath = path ? path + '.' + String(prop) : String(prop);
2517
2430
 
2518
- // Check local store first, then user definitions
2519
- if (localStore.has(String(prop))) {
2520
- return localStore.get(String(prop));
2521
- }
2522
- if (__userDefinitions__.has(fullPath)) {
2523
- return __userDefinitions__.get(fullPath);
2524
- }
2431
+ if (localStore.has(String(prop))) return localStore.get(String(prop));
2432
+ if (__userDefinitions__.has(fullPath)) return __userDefinitions__.get(fullPath);
2525
2433
 
2526
2434
  return __createProxy__(fullPath);
2527
2435
  },
@@ -2534,23 +2442,18 @@ const __createProxy__ = (path = '') => {
2534
2442
  },
2535
2443
 
2536
2444
  apply: (_, __, args) => {
2537
- // Handle tagged template literals
2538
2445
  if (Array.isArray(args[0]) && 'raw' in args[0]) {
2539
2446
  const strings = args[0];
2540
2447
  const values = args.slice(1);
2541
2448
  const text = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '');
2542
2449
  return __rpc__.do(path, text);
2543
2450
  }
2544
-
2545
2451
  return __rpc__.do(path, ...args);
2546
2452
  },
2547
2453
 
2548
- // Prevent enumeration from causing infinite loops
2549
2454
  ownKeys: () => {
2550
2455
  const keys = [];
2551
- for (const [key, value] of localStore) {
2552
- keys.push(key);
2553
- }
2456
+ for (const [key, value] of localStore) keys.push(key);
2554
2457
  for (const [key, value] of __userDefinitions__) {
2555
2458
  if (key.startsWith(path ? path + '.' : '') && !key.slice(path ? path.length + 1 : 0).includes('.')) {
2556
2459
  const localKey = key.slice(path ? path.length + 1 : 0);
@@ -2572,7 +2475,6 @@ const __createProxy__ = (path = '') => {
2572
2475
  return proxy;
2573
2476
  };
2574
2477
 
2575
- // Root proxy and named exports
2576
2478
  const $ = __createProxy__();
2577
2479
  const db = $.db;
2578
2480
  const ai = $.ai;
@@ -2584,7 +2486,6 @@ const track = $.track;
2584
2486
  const every = $.every;
2585
2487
  const decide = $.decide;
2586
2488
 
2587
- // Set default namespace and context properties
2588
2489
  $.ns = __SDK_CONFIG__.ns;
2589
2490
  $.user = { id: 'anonymous', name: 'Anonymous', role: 'guest' };
2590
2491
  $.request = { method: 'GET', path: '/', headers: {}, body: null };
@@ -2594,1034 +2495,4 @@ $.context = {};
2594
2495
  $.meta = {};
2595
2496
  `;
2596
2497
  }
2597
- /**
2598
- * Generate .should chainable assertions code
2599
- */
2600
- function generateShouldCode() {
2601
- return `
2602
- // ============================================================
2603
- // Global .should Chainable Assertions
2604
- // ============================================================
2605
-
2606
- const __createShouldChain__ = (actual, negated = false) => {
2607
- const check = (condition, message) => {
2608
- const passes = negated ? !condition : condition;
2609
- if (!passes) throw new Error(negated ? 'Expected NOT: ' + message : message);
2610
- };
2611
-
2612
- const stringify = (val) => {
2613
- try {
2614
- return JSON.stringify(val);
2615
- } catch {
2616
- return String(val);
2617
- }
2618
- };
2619
-
2620
- // Create a lazy chain getter - returns 'this' assertion for chaining
2621
- const assertion = {};
2622
-
2623
- // Core assertion methods
2624
- assertion.equal = (expected) => {
2625
- check(actual === expected, 'Expected ' + stringify(actual) + ' to equal ' + stringify(expected));
2626
- return assertion;
2627
- };
2628
- assertion.deep = {
2629
- equal: (expected) => {
2630
- check(stringify(actual) === stringify(expected), 'Expected deep equal to ' + stringify(expected));
2631
- return assertion;
2632
- },
2633
- include: (expected) => {
2634
- const actualStr = stringify(actual);
2635
- const expectedStr = stringify(expected);
2636
- // Check if expected properties exist with same values
2637
- const matches = Object.entries(expected || {}).every(([k, v]) =>
2638
- actual && stringify(actual[k]) === stringify(v)
2639
- );
2640
- check(matches, 'Expected ' + actualStr + ' to deeply include ' + expectedStr);
2641
- return assertion;
2642
- }
2643
- };
2644
- assertion.include = (value) => {
2645
- if (typeof actual === 'string') check(actual.includes(String(value)), 'Expected "' + actual + '" to include "' + value + '"');
2646
- else if (Array.isArray(actual)) check(actual.includes(value), 'Expected array to include ' + stringify(value));
2647
- return assertion;
2648
- };
2649
- assertion.contain = assertion.include;
2650
- assertion.lengthOf = (n) => {
2651
- check(actual?.length === n, 'Expected length ' + n + ', got ' + actual?.length);
2652
- return assertion;
2653
- };
2654
- assertion.match = (regex) => {
2655
- const str = String(actual);
2656
- check(regex.test(str), 'Expected "' + str + '" to match ' + regex);
2657
- return assertion;
2658
- };
2659
- assertion.matches = assertion.match;
2660
-
2661
- // .be accessor with type checks
2662
- Object.defineProperty(assertion, 'be', {
2663
- get: () => {
2664
- const beObj = {
2665
- a: (type) => {
2666
- const actualType = actual === null ? 'null' : Array.isArray(actual) ? 'array' : actual instanceof Date ? 'date' : typeof actual;
2667
- check(actualType === type.toLowerCase(), 'Expected ' + stringify(actual) + ' to be a ' + type);
2668
- return assertion;
2669
- },
2670
- above: (n) => { check(actual > n, 'Expected ' + actual + ' to be above ' + n); return assertion; },
2671
- below: (n) => { check(actual < n, 'Expected ' + actual + ' to be below ' + n); return assertion; },
2672
- within: (min, max) => { check(actual >= min && actual <= max, 'Expected ' + actual + ' to be within ' + min + '..' + max); return assertion; },
2673
- oneOf: (arr) => { check(Array.isArray(arr) && arr.includes(actual), 'Expected ' + stringify(actual) + ' to be one of ' + stringify(arr)); return assertion; },
2674
- instanceOf: (cls) => { check(actual instanceof cls, 'Expected to be instance of ' + cls.name); return assertion; }
2675
- };
2676
- beObj.an = beObj.a;
2677
- Object.defineProperty(beObj, 'true', { get: () => { check(actual === true, 'Expected ' + stringify(actual) + ' to be true'); return assertion; } });
2678
- Object.defineProperty(beObj, 'false', { get: () => { check(actual === false, 'Expected ' + stringify(actual) + ' to be false'); return assertion; } });
2679
- Object.defineProperty(beObj, 'ok', { get: () => { check(!!actual, 'Expected ' + stringify(actual) + ' to be truthy'); return assertion; } });
2680
- Object.defineProperty(beObj, 'null', { get: () => { check(actual === null, 'Expected ' + stringify(actual) + ' to be null'); return assertion; } });
2681
- Object.defineProperty(beObj, 'undefined', { get: () => { check(actual === undefined, 'Expected ' + stringify(actual) + ' to be undefined'); return assertion; } });
2682
- Object.defineProperty(beObj, 'empty', { get: () => {
2683
- const isEmpty = actual === '' || (Array.isArray(actual) && actual.length === 0) || (actual && typeof actual === 'object' && Object.keys(actual).length === 0);
2684
- check(isEmpty, 'Expected ' + stringify(actual) + ' to be empty');
2685
- return assertion;
2686
- }});
2687
- return beObj;
2688
- }
2689
- });
2690
-
2691
- // .have accessor with property/keys/lengthOf/at checks
2692
- Object.defineProperty(assertion, 'have', {
2693
- get: () => ({
2694
- property: (name, value) => {
2695
- const hasIt = actual != null && Object.prototype.hasOwnProperty.call(actual, name);
2696
- if (value !== undefined) {
2697
- check(hasIt && actual[name] === value, "Expected property '" + name + "' = " + stringify(value) + ", got " + stringify(actual?.[name]));
2698
- } else {
2699
- check(hasIt, "Expected to have property '" + name + "'");
2700
- }
2701
- if (hasIt) return __createShouldChain__(actual[name], negated);
2702
- return assertion;
2703
- },
2704
- keys: (...keys) => {
2705
- const actualKeys = Object.keys(actual || {});
2706
- check(keys.every(k => actualKeys.includes(k)), 'Expected to have keys ' + stringify(keys));
2707
- return assertion;
2708
- },
2709
- lengthOf: (n) => {
2710
- check(actual?.length === n, 'Expected length ' + n + ', got ' + actual?.length);
2711
- return assertion;
2712
- },
2713
- at: {
2714
- least: (n) => {
2715
- check(actual?.length >= n, 'Expected length at least ' + n + ', got ' + actual?.length);
2716
- return assertion;
2717
- },
2718
- most: (n) => {
2719
- check(actual?.length <= n, 'Expected length at most ' + n + ', got ' + actual?.length);
2720
- return assertion;
2721
- }
2722
- }
2723
- })
2724
- });
2725
-
2726
- // .not negation
2727
- Object.defineProperty(assertion, 'not', {
2728
- get: () => __createShouldChain__(actual, !negated)
2729
- });
2730
-
2731
- // .with passthrough for readability
2732
- Object.defineProperty(assertion, 'with', {
2733
- get: () => assertion
2734
- });
2735
-
2736
- // .that passthrough for chaining (e.g. .have.property('x').that.matches(/.../) )
2737
- Object.defineProperty(assertion, 'that', {
2738
- get: () => assertion
2739
- });
2740
-
2741
- // .and passthrough for chaining
2742
- Object.defineProperty(assertion, 'and', {
2743
- get: () => assertion
2744
- });
2745
-
2746
- return assertion;
2747
- };
2748
-
2749
- // Add .should to Object.prototype
2750
- Object.defineProperty(Object.prototype, 'should', {
2751
- get: function() { return __createShouldChain__(this); },
2752
- configurable: true,
2753
- enumerable: false
2754
- });
2755
- `;
2756
- }
2757
- /**
2758
- * Extract export names from module code
2759
- * Supports both CommonJS (exports.foo) and ES module (export const foo) syntax
2760
- */
2761
- function getExportNames(moduleCode) {
2762
- const names = new Set();
2763
- // Match exports.name = ...
2764
- const dotPattern = /exports\.(\w+)\s*=/g;
2765
- let match;
2766
- while ((match = dotPattern.exec(moduleCode)) !== null) {
2767
- names.add(match[1]);
2768
- }
2769
- // Match exports['name'] = ... or exports["name"] = ...
2770
- const bracketPattern = /exports\[['"](\w+)['"]\]\s*=/g;
2771
- while ((match = bracketPattern.exec(moduleCode)) !== null) {
2772
- names.add(match[1]);
2773
- }
2774
- // Match export const name = ... or export let name = ... or export var name = ...
2775
- const esConstPattern = /export\s+(?:const|let|var)\s+(\w+)\s*=/g;
2776
- while ((match = esConstPattern.exec(moduleCode)) !== null) {
2777
- names.add(match[1]);
2778
- }
2779
- // Match export function name(...) or export async function name(...)
2780
- const esFunctionPattern = /export\s+(?:async\s+)?function\s+(\w+)\s*\(/g;
2781
- while ((match = esFunctionPattern.exec(moduleCode)) !== null) {
2782
- names.add(match[1]);
2783
- }
2784
- // Match export class name
2785
- const esClassPattern = /export\s+class\s+(\w+)/g;
2786
- while ((match = esClassPattern.exec(moduleCode)) !== null) {
2787
- names.add(match[1]);
2788
- }
2789
- return Array.from(names).join(', ') || '_unused';
2790
- }
2791
- /**
2792
- * Transform module code to work in sandbox
2793
- * Converts ES module exports to CommonJS-style for the sandbox
2794
- */
2795
- function transformModuleCode(moduleCode) {
2796
- let code = moduleCode;
2797
- // Transform: export const foo = ... → const foo = ...; exports.foo = foo;
2798
- code = code.replace(/export\s+(const|let|var)\s+(\w+)\s*=/g, '$1 $2 = exports.$2 =');
2799
- // Transform: export function foo(...) → function foo(...) exports.foo = foo;
2800
- code = code.replace(/export\s+(async\s+)?function\s+(\w+)/g, '$1function $2');
2801
- // Add exports for functions after their definition
2802
- const funcNames = [...moduleCode.matchAll(/export\s+(?:async\s+)?function\s+(\w+)/g)];
2803
- for (const [, name] of funcNames) {
2804
- code += `\nexports.${name} = ${name};`;
2805
- }
2806
- // Transform: export class Foo → class Foo; exports.Foo = Foo;
2807
- code = code.replace(/export\s+class\s+(\w+)/g, 'class $1');
2808
- const classNames = [...moduleCode.matchAll(/export\s+class\s+(\w+)/g)];
2809
- for (const [, name] of classNames) {
2810
- code += `\nexports.${name} = ${name};`;
2811
- }
2812
- return code;
2813
- }
2814
- /**
2815
- * Wrap script to auto-return the last expression
2816
- * Converts: `add(1, 2)` → `return add(1, 2)`
2817
- */
2818
- function wrapScriptForReturn(script) {
2819
- const trimmed = script.trim();
2820
- if (!trimmed)
2821
- return script;
2822
- // If script already contains a return statement anywhere, don't modify
2823
- if (/\breturn\b/.test(trimmed))
2824
- return script;
2825
- // If script starts with throw, don't modify
2826
- if (/^\s*throw\b/.test(trimmed))
2827
- return script;
2828
- // If it's a single expression (no newlines, no semicolons except at end), wrap it
2829
- const withoutTrailingSemi = trimmed.replace(/;?\s*$/, '');
2830
- const isSingleLine = !withoutTrailingSemi.includes('\n');
2831
- // Check if it looks like a single expression (no control flow, no declarations)
2832
- const startsWithKeyword = /^\s*(const|let|var|if|for|while|switch|try|class|function|async\s+function)\b/.test(withoutTrailingSemi);
2833
- if (isSingleLine && !startsWithKeyword) {
2834
- return `return ${withoutTrailingSemi}`;
2835
- }
2836
- // For multi-statement scripts, try to return the last expression
2837
- const lines = trimmed.split('\n');
2838
- const lastLine = lines[lines.length - 1].trim();
2839
- // If last line is an expression (not a declaration, control flow, or throw)
2840
- if (lastLine && !/^\s*(const|let|var|if|for|while|switch|try|class|function|return|throw)\b/.test(lastLine)) {
2841
- lines[lines.length - 1] = `return ${lastLine.replace(/;?\s*$/, '')}`;
2842
- return lines.join('\n');
2843
- }
2844
- return script;
2845
- }
2846
- /**
2847
- * Generate worker code for production (uses RPC to ai-tests)
2848
- */
2849
- export function generateWorkerCode(options) {
2850
- const { module: rawModule = '', tests = '', script: rawScript = '', sdk, imports = [] } = options;
2851
- const sdkConfig = sdk === true ? {} : (sdk || null);
2852
- const module = rawModule ? transformModuleCode(rawModule) : '';
2853
- const script = rawScript ? wrapScriptForReturn(rawScript) : '';
2854
- const exportNames = getExportNames(rawModule);
2855
- // Hoisted imports (from MDX test files) - placed at true module top level
2856
- const hoistedImports = imports.length > 0 ? imports.join('\n') + '\n' : '';
2857
- return `
2858
- // Sandbox Worker Entry Point
2859
- import { RpcTarget, newWorkersRpcResponse } from 'capnweb';
2860
- ${hoistedImports}
2861
- const logs = [];
2862
-
2863
- ${sdkConfig ? generateShouldCode() : ''}
2864
-
2865
- ${sdkConfig ? generateSDKCode(sdkConfig) : '// SDK not enabled'}
2866
-
2867
- // Capture console output
2868
- const originalConsole = { ...console };
2869
- const captureConsole = (level) => (...args) => {
2870
- logs.push({
2871
- level,
2872
- message: args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '),
2873
- timestamp: Date.now()
2874
- });
2875
- originalConsole[level](...args);
2876
- };
2877
- console.log = captureConsole('log');
2878
- console.warn = captureConsole('warn');
2879
- console.error = captureConsole('error');
2880
- console.info = captureConsole('info');
2881
- console.debug = captureConsole('debug');
2882
-
2883
- // ============================================================
2884
- // USER MODULE CODE (embedded at generation time)
2885
- // ============================================================
2886
- // Module exports object - exports become top-level variables
2887
- const exports = {};
2888
-
2889
- ${module ? `
2890
- // Execute module code
2891
- try {
2892
- ${module}
2893
- } catch (e) {
2894
- console.error('Module error:', e.message);
2895
- }
2896
- ` : '// No module code provided'}
2897
-
2898
- // Expose all exports as top-level variables for tests and scripts
2899
- // This allows: export const add = (a, b) => a + b; then later: add(1, 2)
2900
- ${rawModule ? `
2901
- const { ${exportNames} } = exports;
2902
- `.trim() : ''}
2903
-
2904
- // ============================================================
2905
- // RPC SERVER - Expose exports via capnweb
2906
- // ============================================================
2907
- class ExportsRpcTarget extends RpcTarget {
2908
- // Dynamically expose all exports as RPC methods
2909
- constructor() {
2910
- super();
2911
- for (const [key, value] of Object.entries(exports)) {
2912
- if (typeof value === 'function') {
2913
- this[key] = value;
2914
- }
2915
- }
2916
- }
2917
-
2918
- // List available exports
2919
- list() {
2920
- return Object.keys(exports);
2921
- }
2922
-
2923
- // Get an export by name
2924
- get(name) {
2925
- return exports[name];
2926
- }
2927
- }
2928
-
2929
- // ============================================================
2930
- // WORKER ENTRY POINT
2931
- // ============================================================
2932
- export default {
2933
- async fetch(request, env) {
2934
- const url = new URL(request.url);
2935
-
2936
- // Route: GET / - Return info about exports
2937
- if (request.method === 'GET' && url.pathname === '/') {
2938
- return Response.json({
2939
- exports: Object.keys(exports),
2940
- rpc: '/rpc',
2941
- execute: '/execute'
2942
- });
2943
- }
2944
-
2945
- // Route: /rpc - capnweb RPC to module exports
2946
- if (url.pathname === '/rpc') {
2947
- return newWorkersRpcResponse(request, new ExportsRpcTarget());
2948
- }
2949
-
2950
- // Route: GET /:name - Simple JSON endpoint to access exports
2951
- if (request.method === 'GET' && url.pathname !== '/execute') {
2952
- const name = url.pathname.slice(1); // Remove leading /
2953
- const value = exports[name];
2954
-
2955
- // Check if export exists
2956
- if (!(name in exports)) {
2957
- return Response.json({ error: \`Export "\${name}" not found\` }, { status: 404 });
2958
- }
2959
-
2960
- // If it's not a function, just return the value
2961
- if (typeof value !== 'function') {
2962
- return Response.json({ result: value });
2963
- }
2964
-
2965
- // It's a function - parse args and call it
2966
- try {
2967
- const args = [];
2968
- const argsParam = url.searchParams.get('args');
2969
- if (argsParam) {
2970
- // Support JSON array: ?args=[1,2,3]
2971
- try {
2972
- const parsed = JSON.parse(argsParam);
2973
- if (Array.isArray(parsed)) {
2974
- args.push(...parsed);
2975
- } else {
2976
- args.push(parsed);
2977
- }
2978
- } catch {
2979
- // Not JSON, use as single string arg
2980
- args.push(argsParam);
2981
- }
2982
- } else {
2983
- // Support named params: ?a=1&b=2 -> passed as object
2984
- const params = Object.fromEntries(url.searchParams.entries());
2985
- if (Object.keys(params).length > 0) {
2986
- // Try to parse numeric values
2987
- for (const [key, val] of Object.entries(params)) {
2988
- const num = Number(val);
2989
- params[key] = !isNaN(num) && val !== '' ? num : val;
2990
- }
2991
- args.push(params);
2992
- }
2993
- }
2994
-
2995
- const result = await value(...args);
2996
- return Response.json({ result });
2997
- } catch (e) {
2998
- return Response.json({ error: e.message }, { status: 500 });
2999
- }
3000
- }
3001
-
3002
- // Route: /execute - Run tests and scripts
3003
- // Check for TEST service binding
3004
- if (!env.TEST) {
3005
- return Response.json({
3006
- success: false,
3007
- error: 'TEST service binding not available. Ensure ai-tests worker is bound.',
3008
- logs,
3009
- duration: 0
3010
- });
3011
- }
3012
-
3013
- // Connect to get the TestServiceCore via RPC
3014
- const testService = await env.TEST.connect();
3015
-
3016
- // Create global test functions that proxy to the RPC service
3017
- const describe = (name, fn) => testService.describe(name, fn);
3018
- const it = (name, fn) => testService.it(name, fn);
3019
- const test = (name, fn) => testService.test(name, fn);
3020
- const expect = (value, message) => testService.expect(value, message);
3021
- const should = (value) => testService.should(value);
3022
- const assert = testService.assert;
3023
- const beforeEach = (fn) => testService.beforeEach(fn);
3024
- const afterEach = (fn) => testService.afterEach(fn);
3025
- const beforeAll = (fn) => testService.beforeAll(fn);
3026
- const afterAll = (fn) => testService.afterAll(fn);
3027
-
3028
- // Add skip/only modifiers
3029
- it.skip = (name, fn) => testService.skip(name, fn);
3030
- it.only = (name, fn) => testService.only(name, fn);
3031
- test.skip = it.skip;
3032
- test.only = it.only;
3033
-
3034
- let scriptResult = undefined;
3035
- let scriptError = null;
3036
- let testResults = undefined;
3037
-
3038
- // ============================================================
3039
- // USER TEST CODE (embedded at generation time)
3040
- // ============================================================
3041
-
3042
- ${tests ? `
3043
- // Register tests
3044
- try {
3045
- ${tests}
3046
- } catch (e) {
3047
- console.error('Test registration error:', e.message);
3048
- }
3049
- ` : '// No test code provided'}
3050
-
3051
- // Execute user script
3052
- ${script ? `
3053
- try {
3054
- scriptResult = await (async () => {
3055
- ${script}
3056
- })();
3057
- } catch (e) {
3058
- console.error('Script error:', e.message);
3059
- scriptError = e.message;
3060
- }
3061
- ` : '// No script code provided'}
3062
-
3063
- // Run tests if any were registered
3064
- ${tests ? `
3065
- try {
3066
- testResults = await testService.run();
3067
- } catch (e) {
3068
- console.error('Test run error:', e.message);
3069
- testResults = { total: 0, passed: 0, failed: 1, skipped: 0, tests: [], duration: 0, error: e.message };
3070
- }
3071
- ` : ''}
3072
-
3073
- const hasTests = ${tests ? 'true' : 'false'};
3074
- const success = scriptError === null && (!hasTests || (testResults && testResults.failed === 0));
3075
-
3076
- return Response.json({
3077
- success,
3078
- value: scriptResult,
3079
- logs,
3080
- testResults: hasTests ? testResults : undefined,
3081
- error: scriptError || undefined,
3082
- duration: 0
3083
- });
3084
- }
3085
- };
3086
- `;
3087
- }
3088
- /**
3089
- * Generate worker code for development (embedded test framework)
3090
- *
3091
- * This version bundles the test framework directly into the worker,
3092
- * avoiding the need for RPC service bindings in local development.
3093
- */
3094
- export function generateDevWorkerCode(options) {
3095
- const { module: rawModule = '', tests = '', script: rawScript = '', sdk, imports = [] } = options;
3096
- const sdkConfig = sdk === true ? {} : (sdk || null);
3097
- const module = rawModule ? transformModuleCode(rawModule) : '';
3098
- const script = rawScript ? wrapScriptForReturn(rawScript) : '';
3099
- const exportNames = getExportNames(rawModule);
3100
- // Hoisted imports (from MDX test files) - placed at true module top level
3101
- const hoistedImports = imports.length > 0 ? imports.join('\n') + '\n' : '';
3102
- return `
3103
- // Sandbox Worker Entry Point (Dev Mode - embedded test framework)
3104
- ${hoistedImports}
3105
- const logs = [];
3106
- const testResults = { total: 0, passed: 0, failed: 0, skipped: 0, tests: [], duration: 0 };
3107
- const pendingTests = [];
3108
-
3109
- ${sdkConfig ? generateShouldCode() : ''}
3110
-
3111
- ${sdkConfig ? generateSDKCode(sdkConfig) : '// SDK not enabled'}
3112
-
3113
- // Capture console output
3114
- const originalConsole = { ...console };
3115
- const captureConsole = (level) => (...args) => {
3116
- logs.push({
3117
- level,
3118
- message: args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '),
3119
- timestamp: Date.now()
3120
- });
3121
- originalConsole[level](...args);
3122
- };
3123
- console.log = captureConsole('log');
3124
- console.warn = captureConsole('warn');
3125
- console.error = captureConsole('error');
3126
- console.info = captureConsole('info');
3127
- console.debug = captureConsole('debug');
3128
-
3129
- // Test framework (vitest-compatible API)
3130
- let currentDescribe = '';
3131
- let beforeEachFns = [];
3132
- let afterEachFns = [];
3133
-
3134
- const describe = (name, fn) => {
3135
- const prev = currentDescribe;
3136
- const prevBeforeEach = [...beforeEachFns];
3137
- const prevAfterEach = [...afterEachFns];
3138
- currentDescribe = currentDescribe ? currentDescribe + ' > ' + name : name;
3139
- try { fn(); } finally {
3140
- currentDescribe = prev;
3141
- beforeEachFns = prevBeforeEach;
3142
- afterEachFns = prevAfterEach;
3143
- }
3144
- };
3145
-
3146
- // Hooks
3147
- const beforeEach = (fn) => { beforeEachFns.push(fn); };
3148
- const afterEach = (fn) => { afterEachFns.push(fn); };
3149
-
3150
- const it = (name, fn) => {
3151
- const fullName = currentDescribe ? currentDescribe + ' > ' + name : name;
3152
- const hooks = { before: [...beforeEachFns], after: [...afterEachFns] };
3153
- pendingTests.push({ name: fullName, fn, hooks });
3154
- };
3155
- const test = it;
3156
-
3157
- it.skip = (name, fn) => {
3158
- const fullName = currentDescribe ? currentDescribe + ' > ' + name : name;
3159
- pendingTests.push({ name: fullName, fn: null, skip: true });
3160
- };
3161
- test.skip = it.skip;
3162
-
3163
- it.only = (name, fn) => {
3164
- const fullName = currentDescribe ? currentDescribe + ' > ' + name : name;
3165
- const hooks = { before: [...beforeEachFns], after: [...afterEachFns] };
3166
- pendingTests.push({ name: fullName, fn, hooks, only: true });
3167
- };
3168
- test.only = it.only;
3169
-
3170
- // Deep equality check
3171
- const deepEqual = (a, b) => {
3172
- if (a === b) return true;
3173
- if (a == null || b == null) return false;
3174
- if (typeof a !== typeof b) return false;
3175
- if (typeof a !== 'object') return false;
3176
- if (Array.isArray(a) !== Array.isArray(b)) return false;
3177
- if (Array.isArray(a)) {
3178
- if (a.length !== b.length) return false;
3179
- return a.every((v, i) => deepEqual(v, b[i]));
3180
- }
3181
- const keysA = Object.keys(a);
3182
- const keysB = Object.keys(b);
3183
- if (keysA.length !== keysB.length) return false;
3184
- return keysA.every(k => deepEqual(a[k], b[k]));
3185
- };
3186
-
3187
- // Expect implementation with vitest-compatible matchers
3188
- const expect = (actual) => {
3189
- const matchers = {
3190
- toBe: (expected) => {
3191
- if (actual !== expected) {
3192
- throw new Error(\`Expected \${JSON.stringify(expected)} but got \${JSON.stringify(actual)}\`);
3193
- }
3194
- },
3195
- toEqual: (expected) => {
3196
- if (!deepEqual(actual, expected)) {
3197
- throw new Error(\`Expected \${JSON.stringify(expected)} but got \${JSON.stringify(actual)}\`);
3198
- }
3199
- },
3200
- toStrictEqual: (expected) => {
3201
- if (!deepEqual(actual, expected)) {
3202
- throw new Error(\`Expected \${JSON.stringify(expected)} but got \${JSON.stringify(actual)}\`);
3203
- }
3204
- },
3205
- toBeTruthy: () => {
3206
- if (!actual) throw new Error(\`Expected truthy but got \${JSON.stringify(actual)}\`);
3207
- },
3208
- toBeFalsy: () => {
3209
- if (actual) throw new Error(\`Expected falsy but got \${JSON.stringify(actual)}\`);
3210
- },
3211
- toBeNull: () => {
3212
- if (actual !== null) throw new Error(\`Expected null but got \${JSON.stringify(actual)}\`);
3213
- },
3214
- toBeUndefined: () => {
3215
- if (actual !== undefined) throw new Error(\`Expected undefined but got \${JSON.stringify(actual)}\`);
3216
- },
3217
- toBeDefined: () => {
3218
- if (actual === undefined) throw new Error('Expected defined but got undefined');
3219
- },
3220
- toBeNaN: () => {
3221
- if (!Number.isNaN(actual)) throw new Error(\`Expected NaN but got \${actual}\`);
3222
- },
3223
- toContain: (item) => {
3224
- if (Array.isArray(actual)) {
3225
- if (!actual.includes(item)) throw new Error(\`Expected array to contain \${JSON.stringify(item)}\`);
3226
- } else if (typeof actual === 'string') {
3227
- if (!actual.includes(item)) throw new Error(\`Expected string to contain "\${item}"\`);
3228
- } else {
3229
- throw new Error('toContain only works on arrays and strings');
3230
- }
3231
- },
3232
- toContainEqual: (item) => {
3233
- if (!Array.isArray(actual)) throw new Error('toContainEqual only works on arrays');
3234
- if (!actual.some(v => deepEqual(v, item))) {
3235
- throw new Error(\`Expected array to contain \${JSON.stringify(item)}\`);
3236
- }
3237
- },
3238
- toHaveLength: (length) => {
3239
- if (actual?.length !== length) {
3240
- throw new Error(\`Expected length \${length} but got \${actual?.length}\`);
3241
- }
3242
- },
3243
- toHaveProperty: function(path, value) {
3244
- const parts = typeof path === 'string' ? path.split('.') : [path];
3245
- let obj = actual;
3246
- for (const part of parts) {
3247
- if (obj == null || !(part in obj)) {
3248
- throw new Error(\`Expected object to have property "\${path}"\`);
3249
- }
3250
- obj = obj[part];
3251
- }
3252
- if (arguments.length > 1 && !deepEqual(obj, value)) {
3253
- throw new Error(\`Expected property "\${path}" to be \${JSON.stringify(value)} but got \${JSON.stringify(obj)}\`);
3254
- }
3255
- },
3256
- toMatchObject: (expected) => {
3257
- if (typeof actual !== 'object' || actual === null) {
3258
- throw new Error('toMatchObject expects an object');
3259
- }
3260
- for (const key of Object.keys(expected)) {
3261
- if (!deepEqual(actual[key], expected[key])) {
3262
- throw new Error(\`Expected property "\${key}" to be \${JSON.stringify(expected[key])} but got \${JSON.stringify(actual[key])}\`);
3263
- }
3264
- }
3265
- },
3266
- toThrow: (expected) => {
3267
- if (typeof actual !== 'function') throw new Error('toThrow expects a function');
3268
- let threw = false;
3269
- let error;
3270
- try {
3271
- actual();
3272
- } catch (e) {
3273
- threw = true;
3274
- error = e;
3275
- }
3276
- if (!threw) throw new Error('Expected function to throw');
3277
- if (expected !== undefined) {
3278
- if (typeof expected === 'string' && !error.message.includes(expected)) {
3279
- throw new Error(\`Expected error message to contain "\${expected}" but got "\${error.message}"\`);
3280
- }
3281
- if (expected instanceof RegExp && !expected.test(error.message)) {
3282
- throw new Error(\`Expected error message to match \${expected} but got "\${error.message}"\`);
3283
- }
3284
- if (typeof expected === 'function' && !(error instanceof expected)) {
3285
- throw new Error(\`Expected error to be instance of \${expected.name}\`);
3286
- }
3287
- }
3288
- },
3289
- toBeGreaterThan: (n) => {
3290
- if (!(actual > n)) throw new Error(\`Expected \${actual} to be greater than \${n}\`);
3291
- },
3292
- toBeLessThan: (n) => {
3293
- if (!(actual < n)) throw new Error(\`Expected \${actual} to be less than \${n}\`);
3294
- },
3295
- toBeGreaterThanOrEqual: (n) => {
3296
- if (!(actual >= n)) throw new Error(\`Expected \${actual} to be >= \${n}\`);
3297
- },
3298
- toBeLessThanOrEqual: (n) => {
3299
- if (!(actual <= n)) throw new Error(\`Expected \${actual} to be <= \${n}\`);
3300
- },
3301
- toBeCloseTo: (n, digits = 2) => {
3302
- const diff = Math.abs(actual - n);
3303
- const threshold = Math.pow(10, -digits) / 2;
3304
- if (diff > threshold) {
3305
- throw new Error(\`Expected \${actual} to be close to \${n}\`);
3306
- }
3307
- },
3308
- toMatch: (pattern) => {
3309
- if (typeof actual !== 'string') throw new Error('toMatch expects a string');
3310
- const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
3311
- if (!regex.test(actual)) {
3312
- throw new Error(\`Expected "\${actual}" to match \${pattern}\`);
3313
- }
3314
- },
3315
- toBeInstanceOf: (cls) => {
3316
- if (!(actual instanceof cls)) {
3317
- throw new Error(\`Expected instance of \${cls.name}\`);
3318
- }
3319
- },
3320
- toBeTypeOf: (type) => {
3321
- if (typeof actual !== type) {
3322
- throw new Error(\`Expected typeof to be "\${type}" but got "\${typeof actual}"\`);
3323
- }
3324
- },
3325
- };
3326
-
3327
- matchers.not = {
3328
- toBe: (expected) => {
3329
- if (actual === expected) throw new Error(\`Expected not \${JSON.stringify(expected)}\`);
3330
- },
3331
- toEqual: (expected) => {
3332
- if (deepEqual(actual, expected)) {
3333
- throw new Error(\`Expected not equal to \${JSON.stringify(expected)}\`);
3334
- }
3335
- },
3336
- toBeTruthy: () => {
3337
- if (actual) throw new Error('Expected not truthy');
3338
- },
3339
- toBeFalsy: () => {
3340
- if (!actual) throw new Error('Expected not falsy');
3341
- },
3342
- toBeNull: () => {
3343
- if (actual === null) throw new Error('Expected not null');
3344
- },
3345
- toBeUndefined: () => {
3346
- if (actual === undefined) throw new Error('Expected not undefined');
3347
- },
3348
- toBeDefined: () => {
3349
- if (actual !== undefined) throw new Error('Expected undefined');
3350
- },
3351
- toContain: (item) => {
3352
- if (Array.isArray(actual) && actual.includes(item)) {
3353
- throw new Error(\`Expected array not to contain \${JSON.stringify(item)}\`);
3354
- }
3355
- if (typeof actual === 'string' && actual.includes(item)) {
3356
- throw new Error(\`Expected string not to contain "\${item}"\`);
3357
- }
3358
- },
3359
- toHaveProperty: (path) => {
3360
- const parts = typeof path === 'string' ? path.split('.') : [path];
3361
- let obj = actual;
3362
- try {
3363
- for (const part of parts) {
3364
- if (obj == null || !(part in obj)) return;
3365
- obj = obj[part];
3366
- }
3367
- throw new Error(\`Expected object not to have property "\${path}"\`);
3368
- } catch {}
3369
- },
3370
- toThrow: () => {
3371
- if (typeof actual !== 'function') throw new Error('toThrow expects a function');
3372
- try {
3373
- actual();
3374
- } catch (e) {
3375
- throw new Error('Expected function not to throw');
3376
- }
3377
- },
3378
- toMatch: (pattern) => {
3379
- if (typeof actual !== 'string') throw new Error('toMatch expects a string');
3380
- const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
3381
- if (regex.test(actual)) {
3382
- throw new Error(\`Expected "\${actual}" not to match \${pattern}\`);
3383
- }
3384
- },
3385
- };
3386
-
3387
- matchers.resolves = new Proxy({}, {
3388
- get: (_, prop) => async (...args) => {
3389
- const resolved = await actual;
3390
- return expect(resolved)[prop](...args);
3391
- }
3392
- });
3393
-
3394
- matchers.rejects = new Proxy({}, {
3395
- get: (_, prop) => async (...args) => {
3396
- try {
3397
- await actual;
3398
- throw new Error('Expected promise to reject');
3399
- } catch (e) {
3400
- if (e.message === 'Expected promise to reject') throw e;
3401
- return expect(e)[prop](...args);
3402
- }
3403
- }
3404
- });
3405
-
3406
- return matchers;
3407
- };
3408
-
3409
- // ============================================================
3410
- // USER MODULE CODE (embedded at generation time)
3411
- // ============================================================
3412
- // Module exports object - exports become top-level variables
3413
- const exports = {};
3414
-
3415
- ${module ? `
3416
- // Execute module code
3417
- try {
3418
- ${module}
3419
- } catch (e) {
3420
- console.error('Module error:', e.message);
3421
- }
3422
- ` : '// No module code provided'}
3423
-
3424
- // Expose all exports as top-level variables for tests and scripts
3425
- // This allows: export const add = (a, b) => a + b; then later: add(1, 2)
3426
- ${rawModule ? `
3427
- const { ${exportNames} } = exports;
3428
- `.trim() : ''}
3429
-
3430
- // ============================================================
3431
- // USER TEST CODE (embedded at generation time)
3432
- // ============================================================
3433
- ${tests ? `
3434
- // Register tests
3435
- try {
3436
- ${tests}
3437
- } catch (e) {
3438
- console.error('Test registration error:', e.message);
3439
- }
3440
- ` : '// No test code provided'}
3441
-
3442
- // ============================================================
3443
- // SIMPLE RPC HANDLER (dev mode - no capnweb dependency)
3444
- // ============================================================
3445
- async function handleRpc(request) {
3446
- try {
3447
- const { method, args = [] } = await request.json();
3448
- if (method === 'list') {
3449
- return Response.json({ result: Object.keys(exports) });
3450
- }
3451
- if (method === 'get') {
3452
- const [name] = args;
3453
- const value = exports[name];
3454
- if (typeof value === 'function') {
3455
- return Response.json({ result: { type: 'function', name } });
3456
- }
3457
- return Response.json({ result: value });
3458
- }
3459
- // Call an exported function
3460
- const fn = exports[method];
3461
- if (typeof fn !== 'function') {
3462
- return Response.json({ error: \`Export "\${method}" is not a function\` }, { status: 400 });
3463
- }
3464
- const result = await fn(...args);
3465
- return Response.json({ result });
3466
- } catch (e) {
3467
- return Response.json({ error: e.message }, { status: 500 });
3468
- }
3469
- }
3470
-
3471
- // ============================================================
3472
- // WORKER ENTRY POINT
3473
- // ============================================================
3474
- export default {
3475
- async fetch(request, env) {
3476
- const url = new URL(request.url);
3477
-
3478
- // Route: GET / - Return info about exports
3479
- if (request.method === 'GET' && url.pathname === '/') {
3480
- return Response.json({
3481
- exports: Object.keys(exports),
3482
- rpc: '/rpc',
3483
- execute: '/execute'
3484
- });
3485
- }
3486
-
3487
- // Route: POST /rpc - Simple RPC to module exports
3488
- if (url.pathname === '/rpc' && request.method === 'POST') {
3489
- return handleRpc(request);
3490
- }
3491
-
3492
- // Route: GET /:name - Simple JSON endpoint to access exports
3493
- if (request.method === 'GET' && url.pathname !== '/execute') {
3494
- const name = url.pathname.slice(1);
3495
- const value = exports[name];
3496
-
3497
- // Check if export exists
3498
- if (!(name in exports)) {
3499
- return Response.json({ error: \`Export "\${name}" not found\` }, { status: 404 });
3500
- }
3501
-
3502
- // If it's not a function, just return the value
3503
- if (typeof value !== 'function') {
3504
- return Response.json({ result: value });
3505
- }
3506
-
3507
- // It's a function - parse args and call it
3508
- try {
3509
- const args = [];
3510
- const argsParam = url.searchParams.get('args');
3511
- if (argsParam) {
3512
- try {
3513
- const parsed = JSON.parse(argsParam);
3514
- if (Array.isArray(parsed)) args.push(...parsed);
3515
- else args.push(parsed);
3516
- } catch {
3517
- args.push(argsParam);
3518
- }
3519
- } else {
3520
- const params = Object.fromEntries(url.searchParams.entries());
3521
- if (Object.keys(params).length > 0) {
3522
- for (const [key, val] of Object.entries(params)) {
3523
- const num = Number(val);
3524
- params[key] = !isNaN(num) && val !== '' ? num : val;
3525
- }
3526
- args.push(params);
3527
- }
3528
- }
3529
- const result = await value(...args);
3530
- return Response.json({ result });
3531
- } catch (e) {
3532
- return Response.json({ error: e.message }, { status: 500 });
3533
- }
3534
- }
3535
-
3536
- // Route: /execute - Run tests and scripts
3537
- let scriptResult = undefined;
3538
- let scriptError = null;
3539
-
3540
- // Execute user script
3541
- ${script ? `
3542
- try {
3543
- scriptResult = (() => {
3544
- ${script}
3545
- })();
3546
- // Support async scripts
3547
- if (scriptResult && typeof scriptResult.then === 'function') {
3548
- scriptResult = await scriptResult;
3549
- }
3550
- } catch (e) {
3551
- console.error('Script error:', e.message);
3552
- scriptError = e.message;
3553
- }
3554
- ` : '// No script code provided'}
3555
-
3556
- // Run all pending tests
3557
- const testStart = Date.now();
3558
- const hasOnly = pendingTests.some(t => t.only);
3559
- const testsToRun = hasOnly ? pendingTests.filter(t => t.only || t.skip) : pendingTests;
3560
-
3561
- for (const { name, fn, hooks, skip } of testsToRun) {
3562
- testResults.total++;
3563
-
3564
- if (skip) {
3565
- testResults.skipped++;
3566
- testResults.tests.push({ name, passed: true, skipped: true, duration: 0 });
3567
- continue;
3568
- }
3569
-
3570
- const start = Date.now();
3571
- try {
3572
- // Run beforeEach hooks
3573
- if (hooks?.before) {
3574
- for (const hook of hooks.before) {
3575
- const hookResult = hook();
3576
- if (hookResult && typeof hookResult.then === 'function') {
3577
- await hookResult;
3578
- }
3579
- }
3580
- }
3581
-
3582
- // Run the test
3583
- const result = fn();
3584
- if (result && typeof result.then === 'function') {
3585
- await result;
3586
- }
3587
-
3588
- // Run afterEach hooks
3589
- if (hooks?.after) {
3590
- for (const hook of hooks.after) {
3591
- const hookResult = hook();
3592
- if (hookResult && typeof hookResult.then === 'function') {
3593
- await hookResult;
3594
- }
3595
- }
3596
- }
3597
-
3598
- testResults.passed++;
3599
- testResults.tests.push({ name, passed: true, duration: Date.now() - start });
3600
- } catch (e) {
3601
- testResults.failed++;
3602
- testResults.tests.push({
3603
- name,
3604
- passed: false,
3605
- error: e.message || String(e),
3606
- duration: Date.now() - start
3607
- });
3608
- }
3609
- }
3610
-
3611
- testResults.duration = Date.now() - testStart;
3612
-
3613
- const hasTests = ${tests ? 'true' : 'false'};
3614
- const success = scriptError === null && (!hasTests || testResults.failed === 0);
3615
-
3616
- return Response.json({
3617
- success,
3618
- value: scriptResult,
3619
- logs,
3620
- testResults: hasTests ? testResults : undefined,
3621
- error: scriptError || undefined,
3622
- duration: 0
3623
- });
3624
- }
3625
- };
3626
- `;
3627
- }
2498
+ //# sourceMappingURL=sdk-generator.js.map