ai-evaluate 2.1.8 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/evaluate.d.ts.map +1 -1
- package/dist/evaluate.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/miniflare-pool.d.ts.map +1 -1
- package/dist/miniflare-pool.js.map +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js.map +1 -1
- package/dist/static/index.d.ts +111 -0
- package/dist/static/index.d.ts.map +1 -0
- package/dist/static/index.js +347 -0
- package/dist/static/index.js.map +1 -0
- package/dist/type-guards.d.ts.map +1 -1
- package/dist/type-guards.js.map +1 -1
- package/dist/worker-template/core.d.ts.map +1 -1
- package/dist/worker-template/core.js +1 -1
- package/dist/worker-template/core.js.map +1 -1
- package/package.json +17 -4
- package/public/capnweb.mjs +220 -0
- package/public/index.mjs +426 -0
- package/public/scaffold.mjs +198 -0
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-test.log +0 -54
- package/.turbo/turbo-typecheck.log +0 -4
- package/CHANGELOG.md +0 -48
- package/example/package.json +0 -20
- package/example/src/index.ts +0 -221
- package/example/wrangler.jsonc +0 -25
- package/src/capnweb-bundle.ts +0 -2596
- package/src/evaluate.ts +0 -329
- package/src/index.ts +0 -23
- package/src/miniflare-pool.ts +0 -395
- package/src/node.ts +0 -245
- package/src/repl.ts +0 -228
- package/src/shared.ts +0 -186
- package/src/type-guards.ts +0 -323
- package/src/types.ts +0 -196
- package/src/validation.ts +0 -120
- package/src/worker-template/code-transforms.ts +0 -32
- package/src/worker-template/core.ts +0 -557
- package/src/worker-template/helpers.ts +0 -90
- package/src/worker-template/index.ts +0 -23
- package/src/worker-template/sdk-generator.ts +0 -2515
- package/src/worker-template/test-generator.ts +0 -358
- package/test/evaluate-extended.test.js +0 -429
- package/test/evaluate-extended.test.ts +0 -469
- package/test/evaluate.test.js +0 -235
- package/test/evaluate.test.ts +0 -253
- package/test/index.test.js +0 -77
- package/test/index.test.ts +0 -95
- package/test/miniflare-pool.test.ts +0 -246
- package/test/node.test.ts +0 -467
- package/test/security.test.ts +0 -1009
- package/test/shared.test.ts +0 -105
- package/test/type-guards.test.ts +0 -303
- package/test/validation.test.ts +0 -240
- package/test/worker-template.test.js +0 -365
- package/test/worker-template.test.ts +0 -432
- package/tsconfig.json +0 -22
- package/vitest.config.js +0 -21
- package/vitest.config.ts +0 -28
|
@@ -1,2515 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SDK code generation for worker templates
|
|
3
|
-
*
|
|
4
|
-
* Supports two modes:
|
|
5
|
-
* - local: In-memory implementations (for testing without network)
|
|
6
|
-
* - remote: RPC-based implementations (for production/integration tests)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { SDKConfig } from '../types.js'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Generate SDK code for injection into sandbox
|
|
13
|
-
*/
|
|
14
|
-
export function generateSDKCode(config: SDKConfig = {}): string {
|
|
15
|
-
if (config.context === 'remote') {
|
|
16
|
-
return generateRemoteSDKCode(config)
|
|
17
|
-
}
|
|
18
|
-
return generateLocalSDKCode(config)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate .should chainable assertions code
|
|
23
|
-
*/
|
|
24
|
-
export function generateShouldCode(): string {
|
|
25
|
-
return `
|
|
26
|
-
// ============================================================
|
|
27
|
-
// Global .should Chainable Assertions
|
|
28
|
-
// ============================================================
|
|
29
|
-
|
|
30
|
-
const __createShouldChain__ = (actual, negated = false) => {
|
|
31
|
-
const check = (condition, message) => {
|
|
32
|
-
const passes = negated ? !condition : condition;
|
|
33
|
-
if (!passes) throw new Error(negated ? 'Expected NOT: ' + message : message);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const stringify = (val) => {
|
|
37
|
-
try {
|
|
38
|
-
return JSON.stringify(val);
|
|
39
|
-
} catch {
|
|
40
|
-
return String(val);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Create a lazy chain getter - returns 'this' assertion for chaining
|
|
45
|
-
const assertion = {};
|
|
46
|
-
|
|
47
|
-
// Core assertion methods
|
|
48
|
-
assertion.equal = (expected) => {
|
|
49
|
-
check(actual === expected, 'Expected ' + stringify(actual) + ' to equal ' + stringify(expected));
|
|
50
|
-
return assertion;
|
|
51
|
-
};
|
|
52
|
-
assertion.deep = {
|
|
53
|
-
equal: (expected) => {
|
|
54
|
-
check(stringify(actual) === stringify(expected), 'Expected deep equal to ' + stringify(expected));
|
|
55
|
-
return assertion;
|
|
56
|
-
},
|
|
57
|
-
include: (expected) => {
|
|
58
|
-
const actualStr = stringify(actual);
|
|
59
|
-
const expectedStr = stringify(expected);
|
|
60
|
-
// Check if expected properties exist with same values
|
|
61
|
-
const matches = Object.entries(expected || {}).every(([k, v]) =>
|
|
62
|
-
actual && stringify(actual[k]) === stringify(v)
|
|
63
|
-
);
|
|
64
|
-
check(matches, 'Expected ' + actualStr + ' to deeply include ' + expectedStr);
|
|
65
|
-
return assertion;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
assertion.include = (value) => {
|
|
69
|
-
if (typeof actual === 'string') check(actual.includes(String(value)), 'Expected "' + actual + '" to include "' + value + '"');
|
|
70
|
-
else if (Array.isArray(actual)) check(actual.includes(value), 'Expected array to include ' + stringify(value));
|
|
71
|
-
return assertion;
|
|
72
|
-
};
|
|
73
|
-
assertion.contain = assertion.include;
|
|
74
|
-
assertion.lengthOf = (n) => {
|
|
75
|
-
check(actual?.length === n, 'Expected length ' + n + ', got ' + actual?.length);
|
|
76
|
-
return assertion;
|
|
77
|
-
};
|
|
78
|
-
assertion.match = (regex) => {
|
|
79
|
-
const str = String(actual);
|
|
80
|
-
check(regex.test(str), 'Expected "' + str + '" to match ' + regex);
|
|
81
|
-
return assertion;
|
|
82
|
-
};
|
|
83
|
-
assertion.matches = assertion.match;
|
|
84
|
-
|
|
85
|
-
// .be accessor with type checks
|
|
86
|
-
Object.defineProperty(assertion, 'be', {
|
|
87
|
-
get: () => {
|
|
88
|
-
const beObj = {
|
|
89
|
-
a: (type) => {
|
|
90
|
-
const actualType = actual === null ? 'null' : Array.isArray(actual) ? 'array' : actual instanceof Date ? 'date' : typeof actual;
|
|
91
|
-
check(actualType === type.toLowerCase(), 'Expected ' + stringify(actual) + ' to be a ' + type);
|
|
92
|
-
return assertion;
|
|
93
|
-
},
|
|
94
|
-
above: (n) => { check(actual > n, 'Expected ' + actual + ' to be above ' + n); return assertion; },
|
|
95
|
-
below: (n) => { check(actual < n, 'Expected ' + actual + ' to be below ' + n); return assertion; },
|
|
96
|
-
within: (min, max) => { check(actual >= min && actual <= max, 'Expected ' + actual + ' to be within ' + min + '..' + max); return assertion; },
|
|
97
|
-
oneOf: (arr) => { check(Array.isArray(arr) && arr.includes(actual), 'Expected ' + stringify(actual) + ' to be one of ' + stringify(arr)); return assertion; },
|
|
98
|
-
instanceOf: (cls) => { check(actual instanceof cls, 'Expected to be instance of ' + cls.name); return assertion; }
|
|
99
|
-
};
|
|
100
|
-
beObj.an = beObj.a;
|
|
101
|
-
Object.defineProperty(beObj, 'true', { get: () => { check(actual === true, 'Expected ' + stringify(actual) + ' to be true'); return assertion; } });
|
|
102
|
-
Object.defineProperty(beObj, 'false', { get: () => { check(actual === false, 'Expected ' + stringify(actual) + ' to be false'); return assertion; } });
|
|
103
|
-
Object.defineProperty(beObj, 'ok', { get: () => { check(!!actual, 'Expected ' + stringify(actual) + ' to be truthy'); return assertion; } });
|
|
104
|
-
Object.defineProperty(beObj, 'null', { get: () => { check(actual === null, 'Expected ' + stringify(actual) + ' to be null'); return assertion; } });
|
|
105
|
-
Object.defineProperty(beObj, 'undefined', { get: () => { check(actual === undefined, 'Expected ' + stringify(actual) + ' to be undefined'); return assertion; } });
|
|
106
|
-
Object.defineProperty(beObj, 'empty', { get: () => {
|
|
107
|
-
const isEmpty = actual === '' || (Array.isArray(actual) && actual.length === 0) || (actual && typeof actual === 'object' && Object.keys(actual).length === 0);
|
|
108
|
-
check(isEmpty, 'Expected ' + stringify(actual) + ' to be empty');
|
|
109
|
-
return assertion;
|
|
110
|
-
}});
|
|
111
|
-
return beObj;
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// .have accessor with property/keys/lengthOf/at checks
|
|
116
|
-
Object.defineProperty(assertion, 'have', {
|
|
117
|
-
get: () => ({
|
|
118
|
-
property: (name, value) => {
|
|
119
|
-
const hasIt = actual != null && Object.prototype.hasOwnProperty.call(actual, name);
|
|
120
|
-
if (value !== undefined) {
|
|
121
|
-
check(hasIt && actual[name] === value, "Expected property '" + name + "' = " + stringify(value) + ", got " + stringify(actual?.[name]));
|
|
122
|
-
} else {
|
|
123
|
-
check(hasIt, "Expected to have property '" + name + "'");
|
|
124
|
-
}
|
|
125
|
-
if (hasIt) return __createShouldChain__(actual[name], negated);
|
|
126
|
-
return assertion;
|
|
127
|
-
},
|
|
128
|
-
keys: (...keys) => {
|
|
129
|
-
const actualKeys = Object.keys(actual || {});
|
|
130
|
-
check(keys.every(k => actualKeys.includes(k)), 'Expected to have keys ' + stringify(keys));
|
|
131
|
-
return assertion;
|
|
132
|
-
},
|
|
133
|
-
lengthOf: (n) => {
|
|
134
|
-
check(actual?.length === n, 'Expected length ' + n + ', got ' + actual?.length);
|
|
135
|
-
return assertion;
|
|
136
|
-
},
|
|
137
|
-
at: {
|
|
138
|
-
least: (n) => {
|
|
139
|
-
check(actual?.length >= n, 'Expected length at least ' + n + ', got ' + actual?.length);
|
|
140
|
-
return assertion;
|
|
141
|
-
},
|
|
142
|
-
most: (n) => {
|
|
143
|
-
check(actual?.length <= n, 'Expected length at most ' + n + ', got ' + actual?.length);
|
|
144
|
-
return assertion;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// .not negation
|
|
151
|
-
Object.defineProperty(assertion, 'not', {
|
|
152
|
-
get: () => __createShouldChain__(actual, !negated)
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// .with passthrough for readability
|
|
156
|
-
Object.defineProperty(assertion, 'with', {
|
|
157
|
-
get: () => assertion
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// .that passthrough for chaining (e.g. .have.property('x').that.matches(/.../) )
|
|
161
|
-
Object.defineProperty(assertion, 'that', {
|
|
162
|
-
get: () => assertion
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// .and passthrough for chaining
|
|
166
|
-
Object.defineProperty(assertion, 'and', {
|
|
167
|
-
get: () => assertion
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
return assertion;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
// Add .should to Object.prototype
|
|
174
|
-
Object.defineProperty(Object.prototype, 'should', {
|
|
175
|
-
get: function() { return __createShouldChain__(this); },
|
|
176
|
-
configurable: true,
|
|
177
|
-
enumerable: false
|
|
178
|
-
});
|
|
179
|
-
`
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Generate local SDK code with in-memory implementations
|
|
184
|
-
*/
|
|
185
|
-
function generateLocalSDKCode(config: SDKConfig = {}): string {
|
|
186
|
-
const ns = config.ns || 'default'
|
|
187
|
-
const aiGatewayUrl = config.aiGatewayUrl || ''
|
|
188
|
-
const aiGatewayToken = config.aiGatewayToken || ''
|
|
189
|
-
|
|
190
|
-
// This is a very large string - the full local SDK implementation
|
|
191
|
-
// For brevity, I'm using a dynamic import approach to load the template
|
|
192
|
-
return getLocalSDKTemplate(ns, aiGatewayUrl, aiGatewayToken)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Generate remote SDK code (RPC-based)
|
|
197
|
-
*/
|
|
198
|
-
function generateRemoteSDKCode(config: SDKConfig = {}): string {
|
|
199
|
-
const rpcUrl = config.rpcUrl || 'https://rpc.do'
|
|
200
|
-
const token = config.token || ''
|
|
201
|
-
const ns = config.ns || 'default'
|
|
202
|
-
|
|
203
|
-
return getRemoteSDKTemplate(rpcUrl, token, ns)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Helper to get the local SDK template
|
|
207
|
-
function getLocalSDKTemplate(ns: string, aiGatewayUrl: string, aiGatewayToken: string): string {
|
|
208
|
-
return `
|
|
209
|
-
// ============================================================
|
|
210
|
-
// Local SDK - In-memory implementation (aligned with ai-database/ai-workflows)
|
|
211
|
-
// ============================================================
|
|
212
|
-
|
|
213
|
-
const __SDK_CONFIG__ = {
|
|
214
|
-
ns: '${ns}',
|
|
215
|
-
aiGatewayUrl: '${aiGatewayUrl}',
|
|
216
|
-
aiGatewayToken: '${aiGatewayToken}'
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
// In-memory database storage (mirrors MemoryDB structure)
|
|
220
|
-
const __db_things__ = new Map();
|
|
221
|
-
const __db_relationships__ = new Map();
|
|
222
|
-
// Indexes for efficient lookups
|
|
223
|
-
const __db_byUrl__ = new Map();
|
|
224
|
-
const __db_byNsType__ = new Map();
|
|
225
|
-
const __db_relFrom__ = new Map();
|
|
226
|
-
const __db_relTo__ = new Map();
|
|
227
|
-
|
|
228
|
-
// ID generator (crypto.randomUUID not available in all environments)
|
|
229
|
-
const __generateId__ = () => {
|
|
230
|
-
const bytes = new Uint8Array(16);
|
|
231
|
-
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
232
|
-
crypto.getRandomValues(bytes);
|
|
233
|
-
} else {
|
|
234
|
-
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
235
|
-
}
|
|
236
|
-
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
|
|
237
|
-
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant
|
|
238
|
-
const hex = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
239
|
-
return hex.slice(0, 8) + '-' + hex.slice(8, 12) + '-' + hex.slice(12, 16) + '-' + hex.slice(16, 20) + '-' + hex.slice(20);
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
// URL resolution (mirrors ai-database resolveUrl)
|
|
243
|
-
const __resolveUrl__ = (entity) => {
|
|
244
|
-
if (entity.url) return entity.url;
|
|
245
|
-
return 'https://' + entity.ns + '/' + entity.type + '/' + entity.id;
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
// Parse identifier (mirrors ai-database parseIdentifier)
|
|
249
|
-
const __parseIdentifier__ = (identifier, defaults = {}) => {
|
|
250
|
-
if (identifier.includes('://')) {
|
|
251
|
-
try {
|
|
252
|
-
const parsed = new URL(identifier);
|
|
253
|
-
const parts = parsed.pathname.split('/').filter(Boolean);
|
|
254
|
-
return {
|
|
255
|
-
ns: parsed.host,
|
|
256
|
-
type: parts[0] || '',
|
|
257
|
-
id: parts.slice(1).join('/') || '',
|
|
258
|
-
url: identifier
|
|
259
|
-
};
|
|
260
|
-
} catch { return { ns: defaults.ns, id: identifier }; }
|
|
261
|
-
}
|
|
262
|
-
if (identifier.includes('/')) {
|
|
263
|
-
const parts = identifier.split('/');
|
|
264
|
-
return { ns: defaults.ns, type: parts[0], id: parts.slice(1).join('/') };
|
|
265
|
-
}
|
|
266
|
-
return { ns: defaults.ns, type: defaults.type, id: identifier };
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
// Extract mdxld metadata from data object
|
|
270
|
-
const __extractType__ = (data) => data && (data.$type || data['@type']);
|
|
271
|
-
const __extractId__ = (data) => data && (data.$id || data['@id']);
|
|
272
|
-
const __extractContext__ = (data) => data && (data.$context || data['@context']);
|
|
273
|
-
|
|
274
|
-
// Index management
|
|
275
|
-
const __indexThing__ = (thing) => {
|
|
276
|
-
const url = __resolveUrl__(thing);
|
|
277
|
-
__db_byUrl__.set(url, thing);
|
|
278
|
-
const nsTypeKey = thing.ns + '/' + thing.type;
|
|
279
|
-
if (!__db_byNsType__.has(nsTypeKey)) __db_byNsType__.set(nsTypeKey, new Set());
|
|
280
|
-
__db_byNsType__.get(nsTypeKey).add(url);
|
|
281
|
-
};
|
|
282
|
-
const __unindexThing__ = (thing) => {
|
|
283
|
-
const url = __resolveUrl__(thing);
|
|
284
|
-
__db_byUrl__.delete(url);
|
|
285
|
-
const nsTypeKey = thing.ns + '/' + thing.type;
|
|
286
|
-
const set = __db_byNsType__.get(nsTypeKey);
|
|
287
|
-
if (set) set.delete(url);
|
|
288
|
-
};
|
|
289
|
-
const __indexRelationship__ = (rel) => {
|
|
290
|
-
if (!__db_relFrom__.has(rel.from)) __db_relFrom__.set(rel.from, new Set());
|
|
291
|
-
__db_relFrom__.get(rel.from).add(rel.id);
|
|
292
|
-
if (!__db_relTo__.has(rel.to)) __db_relTo__.set(rel.to, new Set());
|
|
293
|
-
__db_relTo__.get(rel.to).add(rel.id);
|
|
294
|
-
};
|
|
295
|
-
const __unindexRelationship__ = (rel) => {
|
|
296
|
-
const fromSet = __db_relFrom__.get(rel.from);
|
|
297
|
-
if (fromSet) fromSet.delete(rel.id);
|
|
298
|
-
const toSet = __db_relTo__.get(rel.to);
|
|
299
|
-
if (toSet) toSet.delete(rel.id);
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
// MongoDB-style query matching (mirrors MemoryDB.matchesQuery)
|
|
303
|
-
const __matchesQuery__ = (thing, options) => {
|
|
304
|
-
if (options.ns && thing.ns !== options.ns) return false;
|
|
305
|
-
if (options.type && thing.type !== options.type) return false;
|
|
306
|
-
if (options.where) {
|
|
307
|
-
for (const [key, value] of Object.entries(options.where)) {
|
|
308
|
-
const thingValue = thing.data[key];
|
|
309
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
310
|
-
for (const [op, opVal] of Object.entries(value)) {
|
|
311
|
-
switch (op) {
|
|
312
|
-
case '$gt': if (!(thingValue > opVal)) return false; break;
|
|
313
|
-
case '$gte': if (!(thingValue >= opVal)) return false; break;
|
|
314
|
-
case '$lt': if (!(thingValue < opVal)) return false; break;
|
|
315
|
-
case '$lte': if (!(thingValue <= opVal)) return false; break;
|
|
316
|
-
case '$ne': if (thingValue === opVal) return false; break;
|
|
317
|
-
case '$in': if (!Array.isArray(opVal) || !opVal.includes(thingValue)) return false; break;
|
|
318
|
-
case '$nin': if (Array.isArray(opVal) && opVal.includes(thingValue)) return false; break;
|
|
319
|
-
case '$exists': if (opVal && thingValue === undefined) return false; if (!opVal && thingValue !== undefined) return false; break;
|
|
320
|
-
case '$regex': if (typeof thingValue !== 'string') return false; const regex = typeof opVal === 'string' ? new RegExp(opVal) : opVal; if (!regex.test(thingValue)) return false; break;
|
|
321
|
-
default: if (thingValue !== value) return false;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
} else if (thingValue !== value) return false;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return true;
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
// Apply query options (sort, limit, offset)
|
|
331
|
-
const __applyQueryOptions__ = (items, options) => {
|
|
332
|
-
let result = [...items];
|
|
333
|
-
if (options.orderBy) {
|
|
334
|
-
const field = options.orderBy;
|
|
335
|
-
const dir = options.order === 'desc' ? -1 : 1;
|
|
336
|
-
result.sort((a, b) => {
|
|
337
|
-
const aVal = a[field] ?? a.data?.[field];
|
|
338
|
-
const bVal = b[field] ?? b.data?.[field];
|
|
339
|
-
if (aVal === undefined && bVal === undefined) return 0;
|
|
340
|
-
if (aVal === undefined) return dir;
|
|
341
|
-
if (bVal === undefined) return -dir;
|
|
342
|
-
if (aVal < bVal) return -dir;
|
|
343
|
-
if (aVal > bVal) return dir;
|
|
344
|
-
return 0;
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
if (options.offset) result = result.slice(options.offset);
|
|
348
|
-
if (options.limit) result = result.slice(0, options.limit);
|
|
349
|
-
return result;
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
// Cosine similarity helper for semantic search
|
|
353
|
-
const __cosineSimilarity__ = (a, b) => {
|
|
354
|
-
if (!a || !b || a.length !== b.length || a.length === 0) return 0;
|
|
355
|
-
let dotProduct = 0, normA = 0, normB = 0;
|
|
356
|
-
for (let i = 0; i < a.length; i++) {
|
|
357
|
-
dotProduct += a[i] * b[i];
|
|
358
|
-
normA += a[i] * a[i];
|
|
359
|
-
normB += b[i] * b[i];
|
|
360
|
-
}
|
|
361
|
-
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
362
|
-
return magnitude === 0 ? 0 : dotProduct / magnitude;
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
// Embedding cache for semantic search (stores URL -> embedding)
|
|
366
|
-
const __embeddings__ = new Map();
|
|
367
|
-
|
|
368
|
-
// AI embed helper for semantic search - defined early so __db_core__.search can use it
|
|
369
|
-
const __aiEmbed__ = async (text) => {
|
|
370
|
-
if (!__SDK_CONFIG__.aiGatewayUrl) return [];
|
|
371
|
-
try {
|
|
372
|
-
const url = __SDK_CONFIG__.aiGatewayUrl + '/google-ai-studio/v1beta/models/gemini-embedding-001:embedContent';
|
|
373
|
-
const headers = { 'Content-Type': 'application/json' };
|
|
374
|
-
if (__SDK_CONFIG__.aiGatewayToken) {
|
|
375
|
-
headers['cf-aig-authorization'] = 'Bearer ' + __SDK_CONFIG__.aiGatewayToken;
|
|
376
|
-
}
|
|
377
|
-
const response = await fetch(url, {
|
|
378
|
-
method: 'POST',
|
|
379
|
-
headers,
|
|
380
|
-
body: JSON.stringify({
|
|
381
|
-
content: { parts: [{ text }] },
|
|
382
|
-
outputDimensionality: 768
|
|
383
|
-
})
|
|
384
|
-
});
|
|
385
|
-
if (!response.ok) return [];
|
|
386
|
-
const result = await response.json();
|
|
387
|
-
return result.embedding?.values || [];
|
|
388
|
-
} catch (err) {
|
|
389
|
-
console.warn('Embedding failed:', err.message);
|
|
390
|
-
return [];
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
${getDbCoreTemplate()}
|
|
395
|
-
|
|
396
|
-
${getTypedCollectionTemplate()}
|
|
397
|
-
|
|
398
|
-
${getAiGatewayTemplate()}
|
|
399
|
-
|
|
400
|
-
${getAiMethodsTemplate()}
|
|
401
|
-
|
|
402
|
-
${getHonoAppTemplate()}
|
|
403
|
-
|
|
404
|
-
${getMdxRenderingTemplate()}
|
|
405
|
-
|
|
406
|
-
${getWorkflowSystemTemplate()}
|
|
407
|
-
|
|
408
|
-
${getContextObjectTemplate()}
|
|
409
|
-
`
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Helper functions that return template strings for different parts of the SDK
|
|
413
|
-
function getDbCoreTemplate(): string {
|
|
414
|
-
return `
|
|
415
|
-
// Local DB implementation (aligned with ai-database DBClient interface)
|
|
416
|
-
const __db_core__ = {
|
|
417
|
-
ns: __SDK_CONFIG__.ns,
|
|
418
|
-
|
|
419
|
-
async list(options = {}) {
|
|
420
|
-
const results = [];
|
|
421
|
-
for (const thing of __db_things__.values()) {
|
|
422
|
-
if (__matchesQuery__(thing, options)) results.push(thing);
|
|
423
|
-
}
|
|
424
|
-
return __applyQueryOptions__(results, options);
|
|
425
|
-
},
|
|
426
|
-
|
|
427
|
-
async find(options = {}) {
|
|
428
|
-
return this.list(options);
|
|
429
|
-
},
|
|
430
|
-
|
|
431
|
-
async search(options = {}) {
|
|
432
|
-
const query = (options.query || '').toLowerCase();
|
|
433
|
-
const fields = options.fields || ['data'];
|
|
434
|
-
const minScore = options.minScore || 0;
|
|
435
|
-
const results = [];
|
|
436
|
-
|
|
437
|
-
// Semantic search using embeddings
|
|
438
|
-
if (options.semantic && typeof __aiEmbed__ === 'function') {
|
|
439
|
-
const queryEmbedding = await __aiEmbed__(options.query || '');
|
|
440
|
-
if (!queryEmbedding || queryEmbedding.length === 0) {
|
|
441
|
-
console.warn('Semantic search: embeddings unavailable, using fuzzy text matching');
|
|
442
|
-
const queryTerms = (options.query || '').toLowerCase().split(/\\s+/);
|
|
443
|
-
const textResults = [];
|
|
444
|
-
for (const thing of __db_things__.values()) {
|
|
445
|
-
if (!__matchesQuery__(thing, options)) continue;
|
|
446
|
-
const content = JSON.stringify(thing.data).toLowerCase();
|
|
447
|
-
let score = 0;
|
|
448
|
-
for (const term of queryTerms) {
|
|
449
|
-
if (content.includes(term)) score += 1;
|
|
450
|
-
for (const word of content.split(/[\\s\\W]+/)) {
|
|
451
|
-
if (word.includes(term) || term.includes(word)) score += 0.1;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (score > 0) textResults.push({ thing, score });
|
|
455
|
-
}
|
|
456
|
-
textResults.sort((a, b) => b.score - a.score);
|
|
457
|
-
return __applyQueryOptions__(textResults.map(r => r.thing), options);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
for (const thing of __db_things__.values()) {
|
|
461
|
-
if (!__matchesQuery__(thing, options)) continue;
|
|
462
|
-
const thingUrl = thing.url || thing.id;
|
|
463
|
-
let thingEmbedding = __embeddings__.get(thingUrl);
|
|
464
|
-
if (!thingEmbedding) {
|
|
465
|
-
const textContent = JSON.stringify(thing.data);
|
|
466
|
-
thingEmbedding = await __aiEmbed__(textContent);
|
|
467
|
-
if (thingEmbedding && thingEmbedding.length > 0) {
|
|
468
|
-
__embeddings__.set(thingUrl, thingEmbedding);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
if (thingEmbedding && thingEmbedding.length > 0) {
|
|
472
|
-
const score = __cosineSimilarity__(queryEmbedding, thingEmbedding);
|
|
473
|
-
if (score >= minScore) results.push({ thing, score });
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
results.sort((a, b) => b.score - a.score);
|
|
477
|
-
return __applyQueryOptions__(results.map(r => r.thing), options);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// Text-based search
|
|
481
|
-
for (const thing of __db_things__.values()) {
|
|
482
|
-
if (!__matchesQuery__(thing, options)) continue;
|
|
483
|
-
const searchIn = fields.includes('data')
|
|
484
|
-
? JSON.stringify(thing.data).toLowerCase()
|
|
485
|
-
: fields.map(f => String(thing[f] || '')).join(' ').toLowerCase();
|
|
486
|
-
if (searchIn.includes(query)) {
|
|
487
|
-
const index = searchIn.indexOf(query);
|
|
488
|
-
const score = 1 - (index / searchIn.length);
|
|
489
|
-
if (score >= minScore) results.push({ thing, score });
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
results.sort((a, b) => b.score - a.score);
|
|
493
|
-
return __applyQueryOptions__(results.map(r => r.thing), options);
|
|
494
|
-
},
|
|
495
|
-
|
|
496
|
-
async get(identifier, options = {}) {
|
|
497
|
-
let url;
|
|
498
|
-
try {
|
|
499
|
-
const parsed = __parseIdentifier__(identifier, { ns: __SDK_CONFIG__.ns });
|
|
500
|
-
if (parsed.url) url = parsed.url;
|
|
501
|
-
else if (parsed.ns && parsed.type && parsed.id) url = 'https://' + parsed.ns + '/' + parsed.type + '/' + parsed.id;
|
|
502
|
-
else if (parsed.ns && parsed.id) {
|
|
503
|
-
for (const [thingUrl, thing] of __db_byUrl__) {
|
|
504
|
-
if (thing.ns === parsed.ns && thing.id === parsed.id) return thing;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
} catch { }
|
|
508
|
-
if (url) {
|
|
509
|
-
const thing = __db_byUrl__.get(url);
|
|
510
|
-
if (thing) return thing;
|
|
511
|
-
}
|
|
512
|
-
for (const thing of __db_things__.values()) {
|
|
513
|
-
if (thing.id === identifier || thing.url === identifier) return thing;
|
|
514
|
-
}
|
|
515
|
-
if (options.create || options.generate) {
|
|
516
|
-
const parsed = __parseIdentifier__(identifier, { ns: __SDK_CONFIG__.ns });
|
|
517
|
-
if (options.generate) return this.generate(identifier, typeof options.generate === 'object' ? options.generate : {});
|
|
518
|
-
const data = typeof options.create === 'object' ? options.create : {};
|
|
519
|
-
const type = __extractType__(data) || parsed.type || 'Thing';
|
|
520
|
-
const id = parsed.id || __extractId__(data) || __generateId__();
|
|
521
|
-
return this.create({ ns: parsed.ns || __SDK_CONFIG__.ns, type, id, data });
|
|
522
|
-
}
|
|
523
|
-
return null;
|
|
524
|
-
},
|
|
525
|
-
|
|
526
|
-
async set(url, data) {
|
|
527
|
-
const existing = __db_byUrl__.get(url);
|
|
528
|
-
if (existing) {
|
|
529
|
-
existing.data = data;
|
|
530
|
-
existing.updatedAt = new Date();
|
|
531
|
-
return existing;
|
|
532
|
-
}
|
|
533
|
-
const parsed = __parseIdentifier__(url, { ns: __SDK_CONFIG__.ns });
|
|
534
|
-
const thing = {
|
|
535
|
-
ns: parsed.ns || __SDK_CONFIG__.ns,
|
|
536
|
-
type: parsed.type || '',
|
|
537
|
-
id: parsed.id || __generateId__(),
|
|
538
|
-
url,
|
|
539
|
-
createdAt: new Date(),
|
|
540
|
-
updatedAt: new Date(),
|
|
541
|
-
data
|
|
542
|
-
};
|
|
543
|
-
__db_things__.set(url, thing);
|
|
544
|
-
__indexThing__(thing);
|
|
545
|
-
return thing;
|
|
546
|
-
},
|
|
547
|
-
|
|
548
|
-
async create(urlOrOptions, dataArg) {
|
|
549
|
-
if (typeof urlOrOptions === 'string') {
|
|
550
|
-
const url = urlOrOptions;
|
|
551
|
-
const data = dataArg || {};
|
|
552
|
-
const parsed = __parseIdentifier__(url, { ns: __SDK_CONFIG__.ns });
|
|
553
|
-
const type = __extractType__(data) || parsed.type || 'Thing';
|
|
554
|
-
const id = parsed.id || __extractId__(data) || __generateId__();
|
|
555
|
-
const context = __extractContext__(data);
|
|
556
|
-
const ns = parsed.ns || __SDK_CONFIG__.ns;
|
|
557
|
-
const cleanData = { ...data };
|
|
558
|
-
delete cleanData.$type; delete cleanData.$id; delete cleanData.$context;
|
|
559
|
-
delete cleanData['@type']; delete cleanData['@id']; delete cleanData['@context'];
|
|
560
|
-
const thingUrl = parsed.url || 'https://' + ns + '/' + type + '/' + id;
|
|
561
|
-
if (__db_byUrl__.has(thingUrl)) throw new Error('Thing already exists: ' + thingUrl);
|
|
562
|
-
const thing = { ns, type, id, url: thingUrl, createdAt: new Date(), updatedAt: new Date(), data: cleanData };
|
|
563
|
-
if (context) thing['@context'] = context;
|
|
564
|
-
__db_things__.set(thingUrl, thing);
|
|
565
|
-
__indexThing__(thing);
|
|
566
|
-
return thing;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const options = urlOrOptions;
|
|
570
|
-
if ((__extractType__(options) && !('data' in options)) || ('$type' in options) || ('@type' in options)) {
|
|
571
|
-
const type = __extractType__(options) || 'Thing';
|
|
572
|
-
const id = __extractId__(options) || __generateId__();
|
|
573
|
-
const context = __extractContext__(options);
|
|
574
|
-
const ns = __SDK_CONFIG__.ns;
|
|
575
|
-
const cleanData = { ...options };
|
|
576
|
-
delete cleanData.$type; delete cleanData.$id; delete cleanData.$context;
|
|
577
|
-
delete cleanData['@type']; delete cleanData['@id']; delete cleanData['@context'];
|
|
578
|
-
const thingUrl = 'https://' + ns + '/' + type + '/' + id;
|
|
579
|
-
if (__db_byUrl__.has(thingUrl)) throw new Error('Thing already exists: ' + thingUrl);
|
|
580
|
-
const thing = { ns, type, id, url: thingUrl, createdAt: new Date(), updatedAt: new Date(), data: cleanData };
|
|
581
|
-
if (context) thing['@context'] = context;
|
|
582
|
-
__db_things__.set(thingUrl, thing);
|
|
583
|
-
__indexThing__(thing);
|
|
584
|
-
return thing;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
const id = options.id || __generateId__();
|
|
588
|
-
const thingUrl = options.url || 'https://' + options.ns + '/' + options.type + '/' + id;
|
|
589
|
-
if (__db_byUrl__.has(thingUrl)) throw new Error('Thing already exists: ' + thingUrl);
|
|
590
|
-
const thing = { ns: options.ns, type: options.type, id, url: thingUrl, createdAt: new Date(), updatedAt: new Date(), data: options.data };
|
|
591
|
-
if (options['@context']) thing['@context'] = options['@context'];
|
|
592
|
-
__db_things__.set(thingUrl, thing);
|
|
593
|
-
__indexThing__(thing);
|
|
594
|
-
return thing;
|
|
595
|
-
},
|
|
596
|
-
|
|
597
|
-
async update(url, options) {
|
|
598
|
-
const parsed = __parseIdentifier__(url, { ns: __SDK_CONFIG__.ns });
|
|
599
|
-
const resolvedUrl = parsed.url || 'https://' + parsed.ns + '/' + parsed.type + '/' + parsed.id;
|
|
600
|
-
const existing = __db_byUrl__.get(resolvedUrl);
|
|
601
|
-
if (!existing) throw new Error('Thing not found: ' + resolvedUrl);
|
|
602
|
-
existing.data = { ...existing.data, ...options.data };
|
|
603
|
-
existing.updatedAt = new Date();
|
|
604
|
-
return existing;
|
|
605
|
-
},
|
|
606
|
-
|
|
607
|
-
async upsert(urlOrOptions, dataArg) {
|
|
608
|
-
if (typeof urlOrOptions === 'string') {
|
|
609
|
-
const parsed = __parseIdentifier__(urlOrOptions, { ns: __SDK_CONFIG__.ns });
|
|
610
|
-
const resolvedUrl = parsed.url || (parsed.ns && parsed.type && parsed.id ? 'https://' + parsed.ns + '/' + parsed.type + '/' + parsed.id : null);
|
|
611
|
-
if (resolvedUrl && __db_byUrl__.has(resolvedUrl)) {
|
|
612
|
-
return this.update(resolvedUrl, { data: dataArg || {} });
|
|
613
|
-
}
|
|
614
|
-
return this.create(urlOrOptions, dataArg);
|
|
615
|
-
}
|
|
616
|
-
const options = urlOrOptions;
|
|
617
|
-
const url = options.url || 'https://' + options.ns + '/' + options.type + '/' + (options.id || __generateId__());
|
|
618
|
-
if (__db_byUrl__.has(url)) return this.update(url, { data: options.data });
|
|
619
|
-
return this.create({ ...options, url });
|
|
620
|
-
},
|
|
621
|
-
|
|
622
|
-
async delete(url) {
|
|
623
|
-
const parsed = __parseIdentifier__(url, { ns: __SDK_CONFIG__.ns });
|
|
624
|
-
let resolvedUrl = parsed.url;
|
|
625
|
-
if (!resolvedUrl && parsed.ns && parsed.type && parsed.id) {
|
|
626
|
-
resolvedUrl = 'https://' + parsed.ns + '/' + parsed.type + '/' + parsed.id;
|
|
627
|
-
}
|
|
628
|
-
if (!resolvedUrl) return false;
|
|
629
|
-
const thing = __db_byUrl__.get(resolvedUrl);
|
|
630
|
-
if (!thing) return false;
|
|
631
|
-
__unindexThing__(thing);
|
|
632
|
-
__db_things__.delete(resolvedUrl);
|
|
633
|
-
const relIds = new Set([...(__db_relFrom__.get(resolvedUrl) || []), ...(__db_relTo__.get(resolvedUrl) || [])]);
|
|
634
|
-
for (const relId of relIds) {
|
|
635
|
-
const rel = __db_relationships__.get(relId);
|
|
636
|
-
if (rel) { __unindexRelationship__(rel); __db_relationships__.delete(relId); }
|
|
637
|
-
}
|
|
638
|
-
return true;
|
|
639
|
-
},
|
|
640
|
-
|
|
641
|
-
async generate(identifier, options = {}) {
|
|
642
|
-
const parsed = __parseIdentifier__(identifier, { ns: __SDK_CONFIG__.ns });
|
|
643
|
-
const type = parsed.type || 'Thing';
|
|
644
|
-
const id = parsed.id || __generateId__();
|
|
645
|
-
const thing = {
|
|
646
|
-
ns: parsed.ns || __SDK_CONFIG__.ns, type, id,
|
|
647
|
-
url: parsed.url || 'https://' + (parsed.ns || __SDK_CONFIG__.ns) + '/' + type + '/' + id,
|
|
648
|
-
createdAt: new Date(), updatedAt: new Date(),
|
|
649
|
-
data: { _generated: true, _prompt: options.prompt, _model: options.model }
|
|
650
|
-
};
|
|
651
|
-
__db_things__.set(thing.url, thing);
|
|
652
|
-
__indexThing__(thing);
|
|
653
|
-
return thing;
|
|
654
|
-
},
|
|
655
|
-
|
|
656
|
-
async forEach(options, callback) {
|
|
657
|
-
const things = await this.list(options);
|
|
658
|
-
for (const thing of things) await callback(thing);
|
|
659
|
-
},
|
|
660
|
-
|
|
661
|
-
async relate(options) {
|
|
662
|
-
const id = __generateId__();
|
|
663
|
-
const rel = { id, type: options.type, from: options.from, to: options.to, createdAt: new Date(), data: options.data };
|
|
664
|
-
__db_relationships__.set(id, rel);
|
|
665
|
-
__indexRelationship__(rel);
|
|
666
|
-
return rel;
|
|
667
|
-
},
|
|
668
|
-
|
|
669
|
-
async unrelate(from, type, to) {
|
|
670
|
-
for (const [id, rel] of __db_relationships__) {
|
|
671
|
-
if (rel.from === from && rel.type === type && rel.to === to) {
|
|
672
|
-
__unindexRelationship__(rel);
|
|
673
|
-
__db_relationships__.delete(id);
|
|
674
|
-
return true;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return false;
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
async related(url, relationshipType, direction = 'both') {
|
|
681
|
-
const parsed = __parseIdentifier__(url, { ns: __SDK_CONFIG__.ns });
|
|
682
|
-
const resolvedUrl = parsed.url || 'https://' + parsed.ns + '/' + parsed.type + '/' + parsed.id;
|
|
683
|
-
const relatedUrls = new Set();
|
|
684
|
-
if (direction === 'from' || direction === 'both') {
|
|
685
|
-
const fromRels = __db_relFrom__.get(resolvedUrl);
|
|
686
|
-
if (fromRels) {
|
|
687
|
-
for (const relId of fromRels) {
|
|
688
|
-
const rel = __db_relationships__.get(relId);
|
|
689
|
-
if (rel && (!relationshipType || rel.type === relationshipType)) relatedUrls.add(rel.to);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
if (direction === 'to' || direction === 'both') {
|
|
694
|
-
const toRels = __db_relTo__.get(resolvedUrl);
|
|
695
|
-
if (toRels) {
|
|
696
|
-
for (const relId of toRels) {
|
|
697
|
-
const rel = __db_relationships__.get(relId);
|
|
698
|
-
if (rel && (!relationshipType || rel.type === relationshipType)) relatedUrls.add(rel.from);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
const results = [];
|
|
703
|
-
for (const relatedUrl of relatedUrls) {
|
|
704
|
-
const thing = __db_byUrl__.get(relatedUrl);
|
|
705
|
-
if (thing) results.push(thing);
|
|
706
|
-
}
|
|
707
|
-
return results;
|
|
708
|
-
},
|
|
709
|
-
|
|
710
|
-
async relationships(url, type, direction = 'both') {
|
|
711
|
-
const parsed = __parseIdentifier__(url, { ns: __SDK_CONFIG__.ns });
|
|
712
|
-
const resolvedUrl = parsed.url || 'https://' + parsed.ns + '/' + parsed.type + '/' + parsed.id;
|
|
713
|
-
const results = [];
|
|
714
|
-
if (direction === 'from' || direction === 'both') {
|
|
715
|
-
const fromRels = __db_relFrom__.get(resolvedUrl);
|
|
716
|
-
if (fromRels) {
|
|
717
|
-
for (const relId of fromRels) {
|
|
718
|
-
const rel = __db_relationships__.get(relId);
|
|
719
|
-
if (rel && (!type || rel.type === type)) results.push(rel);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
if (direction === 'to' || direction === 'both') {
|
|
724
|
-
const toRels = __db_relTo__.get(resolvedUrl);
|
|
725
|
-
if (toRels) {
|
|
726
|
-
for (const relId of toRels) {
|
|
727
|
-
const rel = __db_relationships__.get(relId);
|
|
728
|
-
if (rel && (!type || rel.type === type)) results.push(rel);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
return results;
|
|
733
|
-
},
|
|
734
|
-
|
|
735
|
-
clear() {
|
|
736
|
-
__db_things__.clear(); __db_relationships__.clear();
|
|
737
|
-
__db_byUrl__.clear(); __db_byNsType__.clear();
|
|
738
|
-
__db_relFrom__.clear(); __db_relTo__.clear();
|
|
739
|
-
},
|
|
740
|
-
|
|
741
|
-
stats() {
|
|
742
|
-
return { things: __db_things__.size, relationships: __db_relationships__.size };
|
|
743
|
-
}
|
|
744
|
-
};
|
|
745
|
-
`
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
function getTypedCollectionTemplate(): string {
|
|
749
|
-
return `
|
|
750
|
-
// Typed collection accessor (db.Users, db.Posts, etc.) - mirrors ai-database TypedDBOperations
|
|
751
|
-
const db = new Proxy(__db_core__, {
|
|
752
|
-
get: (target, prop) => {
|
|
753
|
-
if (prop in target) return target[prop];
|
|
754
|
-
if (prop === 'then' || prop === 'catch' || prop === 'finally') return undefined;
|
|
755
|
-
const type = String(prop);
|
|
756
|
-
const collectionNs = __SDK_CONFIG__.ns;
|
|
757
|
-
const makeUrl = (id) => 'https://' + collectionNs + '/' + type + '/' + id;
|
|
758
|
-
return {
|
|
759
|
-
async list(options = {}) {
|
|
760
|
-
return __db_core__.list({ ...options, type, ns: collectionNs });
|
|
761
|
-
},
|
|
762
|
-
async find(options = {}) {
|
|
763
|
-
return __db_core__.find({ ...options, type, ns: collectionNs });
|
|
764
|
-
},
|
|
765
|
-
async search(options) {
|
|
766
|
-
return __db_core__.search({ ...options, type, ns: collectionNs });
|
|
767
|
-
},
|
|
768
|
-
async get(id, options = {}) {
|
|
769
|
-
return __db_core__.get(makeUrl(id), options);
|
|
770
|
-
},
|
|
771
|
-
async create(idOrData, data) {
|
|
772
|
-
if (typeof idOrData === 'string') {
|
|
773
|
-
return __db_core__.create({ ns: collectionNs, type, id: idOrData, data: data || {} });
|
|
774
|
-
}
|
|
775
|
-
const extractedId = __extractId__(idOrData);
|
|
776
|
-
return __db_core__.create({ ns: collectionNs, type, id: extractedId || __generateId__(), data: idOrData });
|
|
777
|
-
},
|
|
778
|
-
async update(id, data) {
|
|
779
|
-
return __db_core__.update(makeUrl(id), { data });
|
|
780
|
-
},
|
|
781
|
-
async upsert(idOrData, data) {
|
|
782
|
-
if (typeof idOrData === 'string') {
|
|
783
|
-
return __db_core__.upsert({ ns: collectionNs, type, id: idOrData, data: data || {} });
|
|
784
|
-
}
|
|
785
|
-
const extractedId = __extractId__(idOrData);
|
|
786
|
-
return __db_core__.upsert({ ns: collectionNs, type, id: extractedId || __generateId__(), data: idOrData });
|
|
787
|
-
},
|
|
788
|
-
async delete(id) {
|
|
789
|
-
return __db_core__.delete(makeUrl(id));
|
|
790
|
-
},
|
|
791
|
-
async forEach(optionsOrCallback, callback) {
|
|
792
|
-
if (typeof optionsOrCallback === 'function') {
|
|
793
|
-
return __db_core__.forEach({ type, ns: collectionNs }, optionsOrCallback);
|
|
794
|
-
}
|
|
795
|
-
return __db_core__.forEach({ ...optionsOrCallback, type, ns: collectionNs }, callback);
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
});
|
|
800
|
-
`
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
function getAiGatewayTemplate(): string {
|
|
804
|
-
return `
|
|
805
|
-
// AI Gateway client - makes real API calls through Cloudflare AI Gateway
|
|
806
|
-
const __aiGateway__ = {
|
|
807
|
-
async fetch(provider, endpoint, body, extraHeaders = {}) {
|
|
808
|
-
if (!__SDK_CONFIG__.aiGatewayUrl) {
|
|
809
|
-
let prompt = '';
|
|
810
|
-
if (body?.messages?.[0]?.content?.[0]?.text) {
|
|
811
|
-
prompt = body.messages[0].content[0].text;
|
|
812
|
-
} else if (body?.content?.parts?.[0]?.text) {
|
|
813
|
-
prompt = body.content.parts[0].text;
|
|
814
|
-
}
|
|
815
|
-
if (endpoint.includes('converse')) {
|
|
816
|
-
return {
|
|
817
|
-
output: {
|
|
818
|
-
message: {
|
|
819
|
-
content: [{ text: 'Mock AI response to: ' + prompt.slice(0, 50) }]
|
|
820
|
-
}
|
|
821
|
-
},
|
|
822
|
-
usage: { inputTokens: 10, outputTokens: 20 }
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
if (endpoint.includes('embed')) {
|
|
826
|
-
return {
|
|
827
|
-
embedding: {
|
|
828
|
-
values: new Array(body.outputDimensionality || 768).fill(0).map(() => Math.random())
|
|
829
|
-
}
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
return { mock: true, prompt };
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
const url = __SDK_CONFIG__.aiGatewayUrl + '/' + provider + endpoint;
|
|
836
|
-
const headers = {
|
|
837
|
-
'Content-Type': 'application/json',
|
|
838
|
-
...extraHeaders
|
|
839
|
-
};
|
|
840
|
-
if (__SDK_CONFIG__.aiGatewayToken) {
|
|
841
|
-
headers['cf-aig-authorization'] = 'Bearer ' + __SDK_CONFIG__.aiGatewayToken;
|
|
842
|
-
}
|
|
843
|
-
const response = await fetch(url, {
|
|
844
|
-
method: 'POST',
|
|
845
|
-
headers,
|
|
846
|
-
body: JSON.stringify(body)
|
|
847
|
-
});
|
|
848
|
-
if (!response.ok) {
|
|
849
|
-
const error = await response.text();
|
|
850
|
-
throw new Error('AI Gateway error: ' + response.status + ' ' + error);
|
|
851
|
-
}
|
|
852
|
-
return response.json();
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
`
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
function getAiMethodsTemplate(): string {
|
|
859
|
-
return `
|
|
860
|
-
// AI implementation - callable as function or via methods
|
|
861
|
-
const __aiMethods__ = {
|
|
862
|
-
async generate(prompt, options = {}) {
|
|
863
|
-
const model = options.model || 'anthropic.claude-opus-4-5-20251101-v1:0';
|
|
864
|
-
const result = await __aiGateway__.fetch('aws-bedrock', '/model/' + model + '/converse', {
|
|
865
|
-
messages: [{ role: 'user', content: [{ text: prompt }] }],
|
|
866
|
-
inferenceConfig: { maxTokens: options.maxTokens || 1024 }
|
|
867
|
-
});
|
|
868
|
-
const text = result.output?.message?.content?.[0]?.text || '';
|
|
869
|
-
return { text, model, usage: result.usage };
|
|
870
|
-
},
|
|
871
|
-
async embed(text, options = {}) {
|
|
872
|
-
const dimensions = options.dimensions || 768;
|
|
873
|
-
const result = await __aiGateway__.fetch('google-ai-studio', '/v1beta/models/gemini-embedding-001:embedContent', {
|
|
874
|
-
content: { parts: [{ text }] },
|
|
875
|
-
outputDimensionality: dimensions
|
|
876
|
-
});
|
|
877
|
-
return result.embedding?.values || [];
|
|
878
|
-
},
|
|
879
|
-
async embedMany(texts, options = {}) {
|
|
880
|
-
const dimensions = options.dimensions || 768;
|
|
881
|
-
const embeddings = [];
|
|
882
|
-
for (const text of texts) {
|
|
883
|
-
const result = await __aiGateway__.fetch('google-ai-studio', '/v1beta/models/gemini-embedding-001:embedContent', {
|
|
884
|
-
content: { parts: [{ text }] },
|
|
885
|
-
outputDimensionality: dimensions
|
|
886
|
-
});
|
|
887
|
-
embeddings.push(result.embedding?.values || []);
|
|
888
|
-
}
|
|
889
|
-
return embeddings;
|
|
890
|
-
},
|
|
891
|
-
async chat(messages, options = {}) {
|
|
892
|
-
const model = options.model || 'anthropic.claude-opus-4-5-20251101-v1:0';
|
|
893
|
-
const result = await __aiGateway__.fetch('aws-bedrock', '/model/' + model + '/converse', {
|
|
894
|
-
messages: messages.map(m => ({ role: m.role, content: [{ text: m.content }] })),
|
|
895
|
-
inferenceConfig: { maxTokens: options.maxTokens || 1024 }
|
|
896
|
-
});
|
|
897
|
-
const content = result.output?.message?.content?.[0]?.text || '';
|
|
898
|
-
return { role: 'assistant', content, usage: result.usage };
|
|
899
|
-
},
|
|
900
|
-
async complete(prompt, options = {}) {
|
|
901
|
-
const result = await ai.generate(prompt, options);
|
|
902
|
-
return result.text;
|
|
903
|
-
},
|
|
904
|
-
async classify(text, labels, options = {}) {
|
|
905
|
-
const prompt = 'Classify the following text into one of these categories: ' + labels.join(', ') + '\\n\\nText: ' + text + '\\n\\nRespond with just the category name.';
|
|
906
|
-
const result = await ai.generate(prompt, { ...options, maxTokens: 50 });
|
|
907
|
-
const label = labels.find(l => result.text.toLowerCase().includes(l.toLowerCase())) || labels[0];
|
|
908
|
-
return { label, confidence: 0.9 };
|
|
909
|
-
},
|
|
910
|
-
async extract(text, schema, options = {}) {
|
|
911
|
-
const prompt = 'Extract the following information from the text and return as JSON:\\n\\nSchema: ' + JSON.stringify(schema) + '\\n\\nText: ' + text + '\\n\\nRespond with valid JSON only.';
|
|
912
|
-
const result = await ai.generate(prompt, options);
|
|
913
|
-
try {
|
|
914
|
-
return JSON.parse(result.text);
|
|
915
|
-
} catch {
|
|
916
|
-
return { _extracted: true, raw: result.text };
|
|
917
|
-
}
|
|
918
|
-
},
|
|
919
|
-
async summarize(text, options = {}) {
|
|
920
|
-
const prompt = 'Summarize the following text concisely:\\n\\n' + text;
|
|
921
|
-
const result = await ai.generate(prompt, { ...options, maxTokens: options.maxTokens || 256 });
|
|
922
|
-
return result.text;
|
|
923
|
-
},
|
|
924
|
-
createDatabaseTools(database) {
|
|
925
|
-
const dbInstance = database || __db_core__;
|
|
926
|
-
const success = (data) => ({
|
|
927
|
-
content: [{ type: 'text', text: JSON.stringify(data) }]
|
|
928
|
-
});
|
|
929
|
-
const error = (message) => ({
|
|
930
|
-
content: [{ type: 'text', text: message }],
|
|
931
|
-
isError: true
|
|
932
|
-
});
|
|
933
|
-
return [
|
|
934
|
-
{
|
|
935
|
-
name: 'mdxdb_list',
|
|
936
|
-
description: 'List documents from the database by type.',
|
|
937
|
-
handler: async (args) => {
|
|
938
|
-
try {
|
|
939
|
-
const { type, prefix, limit = 100 } = args || {};
|
|
940
|
-
const result = await dbInstance.list({ type, prefix, limit });
|
|
941
|
-
return success(result);
|
|
942
|
-
} catch (err) {
|
|
943
|
-
return error('Failed to list documents: ' + (err.message || String(err)));
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
},
|
|
947
|
-
{
|
|
948
|
-
name: 'mdxdb_search',
|
|
949
|
-
description: 'Search for documents by query.',
|
|
950
|
-
handler: async (args) => {
|
|
951
|
-
try {
|
|
952
|
-
const { query, type, limit = 10, semantic = false } = args || {};
|
|
953
|
-
const result = await dbInstance.search({ query, type, limit, semantic });
|
|
954
|
-
return success(result);
|
|
955
|
-
} catch (err) {
|
|
956
|
-
return error('Failed to search documents: ' + (err.message || String(err)));
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
},
|
|
960
|
-
{
|
|
961
|
-
name: 'mdxdb_get',
|
|
962
|
-
description: 'Get a specific document by ID.',
|
|
963
|
-
handler: async (args) => {
|
|
964
|
-
try {
|
|
965
|
-
const { id, url } = args || {};
|
|
966
|
-
const identifier = url || id;
|
|
967
|
-
if (!identifier) return error('Either id or url is required');
|
|
968
|
-
const doc = await dbInstance.get(identifier);
|
|
969
|
-
if (!doc) return error('Document not found: ' + identifier);
|
|
970
|
-
return success(doc);
|
|
971
|
-
} catch (err) {
|
|
972
|
-
return error('Failed to get document: ' + (err.message || String(err)));
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
name: 'mdxdb_set',
|
|
978
|
-
description: 'Create or update a document.',
|
|
979
|
-
handler: async (args) => {
|
|
980
|
-
try {
|
|
981
|
-
const { id, url, data, content, type } = args || {};
|
|
982
|
-
const identifier = url || id;
|
|
983
|
-
if (!identifier) return error('Either id or url is required');
|
|
984
|
-
const docData = { ...(data || {}), ...(type ? { $type: type } : {}) };
|
|
985
|
-
await dbInstance.set(identifier, docData);
|
|
986
|
-
return success({ success: true, id: identifier });
|
|
987
|
-
} catch (err) {
|
|
988
|
-
return error('Failed to set document: ' + (err.message || String(err)));
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
},
|
|
992
|
-
{
|
|
993
|
-
name: 'mdxdb_delete',
|
|
994
|
-
description: 'Delete a document by ID.',
|
|
995
|
-
handler: async (args) => {
|
|
996
|
-
try {
|
|
997
|
-
const { id, url } = args || {};
|
|
998
|
-
const identifier = url || id;
|
|
999
|
-
if (!identifier) return error('Either id or url is required');
|
|
1000
|
-
const result = await dbInstance.delete(identifier);
|
|
1001
|
-
return success({ deleted: result.deleted !== false });
|
|
1002
|
-
} catch (err) {
|
|
1003
|
-
return error('Failed to delete document: ' + (err.message || String(err)));
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
];
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
|
|
1011
|
-
// Create callable ai function
|
|
1012
|
-
const ai = Object.assign(
|
|
1013
|
-
async function ai(promptOrStrings, ...values) {
|
|
1014
|
-
if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
|
|
1015
|
-
const prompt = promptOrStrings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '');
|
|
1016
|
-
const result = await __aiMethods__.generate(prompt);
|
|
1017
|
-
return result.text || result;
|
|
1018
|
-
}
|
|
1019
|
-
const prompt = promptOrStrings;
|
|
1020
|
-
const options = values[0] || {};
|
|
1021
|
-
const result = await __aiMethods__.generate(prompt, options);
|
|
1022
|
-
return result.text || result;
|
|
1023
|
-
},
|
|
1024
|
-
__aiMethods__
|
|
1025
|
-
);
|
|
1026
|
-
|
|
1027
|
-
// Add references method to db_core
|
|
1028
|
-
__db_core__.references = async function(url, direction = 'both') {
|
|
1029
|
-
const rels = await this.relationships(url, undefined, direction);
|
|
1030
|
-
const refs = [];
|
|
1031
|
-
for (const rel of rels) {
|
|
1032
|
-
const targetUrl = direction === 'from' ? rel.to : direction === 'to' ? rel.from : (rel.from === url ? rel.to : rel.from);
|
|
1033
|
-
const target = await this.get(targetUrl);
|
|
1034
|
-
if (target) refs.push({ ...target, relationship: rel });
|
|
1035
|
-
}
|
|
1036
|
-
return refs;
|
|
1037
|
-
};
|
|
1038
|
-
`
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
function getHonoAppTemplate(): string {
|
|
1042
|
-
// This is a very large template - returning a simplified version
|
|
1043
|
-
// The full implementation is in the original file
|
|
1044
|
-
return `
|
|
1045
|
-
// ============================================================
|
|
1046
|
-
// Hono-compatible HTTP App (for testing)
|
|
1047
|
-
// ============================================================
|
|
1048
|
-
|
|
1049
|
-
const __clientComponentCache__ = new WeakMap();
|
|
1050
|
-
|
|
1051
|
-
const __isClientComponent__ = (fn) => {
|
|
1052
|
-
if (typeof fn !== 'function') return false;
|
|
1053
|
-
if (__clientComponentCache__.has(fn)) return __clientComponentCache__.get(fn);
|
|
1054
|
-
__clientComponentCache__.set(fn, false);
|
|
1055
|
-
const source = fn.toString();
|
|
1056
|
-
let result = false;
|
|
1057
|
-
if (source.includes("'use client'") || source.includes('"use client"')) result = true;
|
|
1058
|
-
else if (source.includes("'use server'") || source.includes('"use server"')) result = false;
|
|
1059
|
-
else if (source.includes('useState(') || source.includes('useEffect(') || source.includes('useRef(')) result = true;
|
|
1060
|
-
__clientComponentCache__.set(fn, result);
|
|
1061
|
-
return result;
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
const __renderJsx__ = (element) => {
|
|
1065
|
-
if (element === null || element === undefined) return '';
|
|
1066
|
-
if (typeof element === 'string' || typeof element === 'number') return String(element);
|
|
1067
|
-
if (Array.isArray(element)) return element.map(__renderJsx__).join('');
|
|
1068
|
-
if (typeof element !== 'object') return String(element);
|
|
1069
|
-
const { type, props } = element;
|
|
1070
|
-
if (!type) return '';
|
|
1071
|
-
if (typeof type === 'function') {
|
|
1072
|
-
const isClient = __isClientComponent__(type);
|
|
1073
|
-
try {
|
|
1074
|
-
const result = type(props || {});
|
|
1075
|
-
const rendered = __renderJsx__(result);
|
|
1076
|
-
if (isClient) {
|
|
1077
|
-
const componentName = type.name || 'Component';
|
|
1078
|
-
return '<div data-hono-hydrate="' + componentName + '">' + rendered + '</div><script>/* hydrate: ' + componentName + ' */</script>';
|
|
1079
|
-
}
|
|
1080
|
-
return rendered;
|
|
1081
|
-
} catch (e) {
|
|
1082
|
-
return '<!-- Error: ' + e.message + ' -->';
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
const tag = String(type);
|
|
1086
|
-
const attrs = Object.entries(props || {})
|
|
1087
|
-
.filter(([k, v]) => k !== 'children' && v !== undefined && v !== null && v !== false)
|
|
1088
|
-
.map(([k, v]) => {
|
|
1089
|
-
if (v === true) return k;
|
|
1090
|
-
const attrName = k === 'className' ? 'class' : k.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
1091
|
-
return attrName + '="' + String(v).replace(/"/g, '"') + '"';
|
|
1092
|
-
})
|
|
1093
|
-
.join(' ');
|
|
1094
|
-
const children = props?.children;
|
|
1095
|
-
const childContent = Array.isArray(children) ? children.map(__renderJsx__).join('') : children !== undefined ? __renderJsx__(children) : '';
|
|
1096
|
-
const voidElements = new Set(['br', 'hr', 'img', 'input', 'link', 'meta', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr']);
|
|
1097
|
-
if (voidElements.has(tag.toLowerCase())) return '<' + tag + (attrs ? ' ' + attrs : '') + ' />';
|
|
1098
|
-
return '<' + tag + (attrs ? ' ' + attrs : '') + '>' + childContent + '</' + tag + '>';
|
|
1099
|
-
};
|
|
1100
|
-
|
|
1101
|
-
const __createHonoApp__ = () => {
|
|
1102
|
-
const routes = [];
|
|
1103
|
-
const middleware = [];
|
|
1104
|
-
let notFoundHandler = null;
|
|
1105
|
-
let errorHandler = null;
|
|
1106
|
-
|
|
1107
|
-
const parsePattern = (pattern) => {
|
|
1108
|
-
const params = [];
|
|
1109
|
-
let normalizedPattern = pattern.replace(/\\/+$/, '') || '/';
|
|
1110
|
-
let regexStr = normalizedPattern
|
|
1111
|
-
.replace(/\\*$/, '(?<wildcard>.*)')
|
|
1112
|
-
.replace(/\\/:([^/]+)\\?/g, (_, name) => { params.push(name); return '(?:/(?<' + name + '>[^/]*))?'; })
|
|
1113
|
-
.replace(/:([^/]+)/g, (_, name) => { params.push(name); return '(?<' + name + '>[^/]+)'; });
|
|
1114
|
-
if (!regexStr.endsWith('.*') && !regexStr.endsWith(')?')) regexStr = regexStr + '/?';
|
|
1115
|
-
return { regex: new RegExp('^' + regexStr + '(?:\\\\?.*)?$'), params };
|
|
1116
|
-
};
|
|
1117
|
-
|
|
1118
|
-
const createContext = (req, pathParams, store) => {
|
|
1119
|
-
const url = new URL(req.url, 'http://localhost');
|
|
1120
|
-
return {
|
|
1121
|
-
req: {
|
|
1122
|
-
raw: req,
|
|
1123
|
-
url: req.url,
|
|
1124
|
-
method: req.method,
|
|
1125
|
-
path: url.pathname,
|
|
1126
|
-
param: (name) => name === '*' ? pathParams.wildcard : pathParams[name],
|
|
1127
|
-
query: (name) => url.searchParams.get(name),
|
|
1128
|
-
queries: () => {
|
|
1129
|
-
const result = {};
|
|
1130
|
-
for (const [key, value] of url.searchParams) {
|
|
1131
|
-
if (result[key]) {
|
|
1132
|
-
if (Array.isArray(result[key])) result[key].push(value);
|
|
1133
|
-
else result[key] = [result[key], value];
|
|
1134
|
-
} else result[key] = value;
|
|
1135
|
-
}
|
|
1136
|
-
return result;
|
|
1137
|
-
},
|
|
1138
|
-
header: (name) => req.headers.get(name),
|
|
1139
|
-
json: () => req.json(),
|
|
1140
|
-
text: () => req.text(),
|
|
1141
|
-
arrayBuffer: () => req.arrayBuffer(),
|
|
1142
|
-
parseBody: async () => {
|
|
1143
|
-
const contentType = req.headers.get('Content-Type') || '';
|
|
1144
|
-
if (contentType.includes('application/json')) return req.json();
|
|
1145
|
-
if (contentType.includes('multipart/form-data') || contentType.includes('application/x-www-form-urlencoded')) {
|
|
1146
|
-
const formData = await req.formData();
|
|
1147
|
-
const result = {};
|
|
1148
|
-
for (const [key, value] of formData.entries()) result[key] = value;
|
|
1149
|
-
return result;
|
|
1150
|
-
}
|
|
1151
|
-
return req.text();
|
|
1152
|
-
},
|
|
1153
|
-
},
|
|
1154
|
-
text: (body, status = 200, headers = {}) => {
|
|
1155
|
-
const h = { 'Content-Type': 'text/plain', ...headers };
|
|
1156
|
-
return new Response(body, { status, headers: h });
|
|
1157
|
-
},
|
|
1158
|
-
json: (data, status = 200) => new Response(JSON.stringify(data), { status, headers: { 'Content-Type': 'application/json' } }),
|
|
1159
|
-
html: (body, status = 200) => {
|
|
1160
|
-
const rendered = typeof body === 'object' ? __renderJsx__(body) : body;
|
|
1161
|
-
return new Response(rendered, { status, headers: { 'Content-Type': 'text/html' } });
|
|
1162
|
-
},
|
|
1163
|
-
body: (data, status = 200) => new Response(data, { status }),
|
|
1164
|
-
redirect: (url, status = 302) => new Response(null, { status, headers: { 'Location': url } }),
|
|
1165
|
-
notFound: () => new Response('Not Found', { status: 404 }),
|
|
1166
|
-
stream: (callback) => {
|
|
1167
|
-
const { readable, writable } = new TransformStream();
|
|
1168
|
-
const writer = writable.getWriter();
|
|
1169
|
-
const streamApi = {
|
|
1170
|
-
write: async (chunk) => {
|
|
1171
|
-
const data = typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk;
|
|
1172
|
-
await writer.write(data);
|
|
1173
|
-
},
|
|
1174
|
-
close: async () => await writer.close(),
|
|
1175
|
-
pipe: async (rs) => {
|
|
1176
|
-
const reader = rs.getReader();
|
|
1177
|
-
while (true) {
|
|
1178
|
-
const { done, value } = await reader.read();
|
|
1179
|
-
if (done) break;
|
|
1180
|
-
await writer.write(value);
|
|
1181
|
-
}
|
|
1182
|
-
await writer.close();
|
|
1183
|
-
}
|
|
1184
|
-
};
|
|
1185
|
-
Promise.resolve(callback(streamApi)).then(() => writer.close()).catch(() => writer.close());
|
|
1186
|
-
return new Response(readable, { headers: { 'Content-Type': 'text/html; charset=utf-8' } });
|
|
1187
|
-
},
|
|
1188
|
-
status: (code) => {
|
|
1189
|
-
const ctx = createContext(req, pathParams, store);
|
|
1190
|
-
const originalJson = ctx.json;
|
|
1191
|
-
const originalText = ctx.text;
|
|
1192
|
-
const originalHtml = ctx.html;
|
|
1193
|
-
ctx.json = (data) => originalJson(data, code);
|
|
1194
|
-
ctx.text = (body) => originalText(body, code);
|
|
1195
|
-
ctx.html = (body) => originalHtml(body, code);
|
|
1196
|
-
return ctx;
|
|
1197
|
-
},
|
|
1198
|
-
header: (name, value) => {
|
|
1199
|
-
store._headers = store._headers || {};
|
|
1200
|
-
store._headers[name] = value;
|
|
1201
|
-
},
|
|
1202
|
-
set: (key, value) => { store[key] = value; },
|
|
1203
|
-
get: (key) => store[key],
|
|
1204
|
-
};
|
|
1205
|
-
};
|
|
1206
|
-
|
|
1207
|
-
const addRoute = (method, path, ...handlers) => {
|
|
1208
|
-
const { regex, params } = parsePattern(path);
|
|
1209
|
-
routes.push({ method: method.toUpperCase(), pattern: path, regex, params, handlers });
|
|
1210
|
-
};
|
|
1211
|
-
|
|
1212
|
-
const handleRequest = async (req) => {
|
|
1213
|
-
const url = new URL(req.url, 'http://localhost');
|
|
1214
|
-
const path = url.pathname;
|
|
1215
|
-
const method = req.method;
|
|
1216
|
-
const store = {};
|
|
1217
|
-
|
|
1218
|
-
let matchedRoute = null;
|
|
1219
|
-
let routeParams = {};
|
|
1220
|
-
for (const route of routes) {
|
|
1221
|
-
if (route.method !== method && route.method !== 'ALL') continue;
|
|
1222
|
-
const match = path.match(route.regex);
|
|
1223
|
-
if (match) {
|
|
1224
|
-
matchedRoute = route;
|
|
1225
|
-
routeParams = match.groups || {};
|
|
1226
|
-
break;
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
const matchingMiddleware = middleware.filter(mw => {
|
|
1231
|
-
const prefix = mw.prefix.replace('/*', '').replace('*', '');
|
|
1232
|
-
return path.startsWith(prefix) || prefix === '';
|
|
1233
|
-
});
|
|
1234
|
-
|
|
1235
|
-
let chainResponse = null;
|
|
1236
|
-
|
|
1237
|
-
const executeChain = async (index) => {
|
|
1238
|
-
if (index < matchingMiddleware.length) {
|
|
1239
|
-
const mw = matchingMiddleware[index];
|
|
1240
|
-
const ctx = createContext(req, routeParams, store);
|
|
1241
|
-
const next = async () => {
|
|
1242
|
-
const downstreamResult = await executeChain(index + 1);
|
|
1243
|
-
if (downstreamResult) chainResponse = downstreamResult;
|
|
1244
|
-
return downstreamResult;
|
|
1245
|
-
};
|
|
1246
|
-
const result = await mw.handler(ctx, next);
|
|
1247
|
-
if (result) return result;
|
|
1248
|
-
return chainResponse;
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
if (!matchedRoute) {
|
|
1252
|
-
if (notFoundHandler) {
|
|
1253
|
-
const ctx = createContext(req, {}, store);
|
|
1254
|
-
return notFoundHandler(ctx);
|
|
1255
|
-
}
|
|
1256
|
-
return new Response('Not Found', { status: 404 });
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
const ctx = createContext(req, routeParams, store);
|
|
1260
|
-
for (let i = 0; i < matchedRoute.handlers.length; i++) {
|
|
1261
|
-
const handler = matchedRoute.handlers[i];
|
|
1262
|
-
const isLast = i === matchedRoute.handlers.length - 1;
|
|
1263
|
-
let nextCalled = false;
|
|
1264
|
-
const next = async () => { nextCalled = true; };
|
|
1265
|
-
const result = await handler(ctx, next);
|
|
1266
|
-
if (result) return result;
|
|
1267
|
-
if (!isLast && !nextCalled) break;
|
|
1268
|
-
}
|
|
1269
|
-
return null;
|
|
1270
|
-
};
|
|
1271
|
-
|
|
1272
|
-
const applyHeaders = (response) => {
|
|
1273
|
-
if (store._headers && response instanceof Response) {
|
|
1274
|
-
for (const [name, value] of Object.entries(store._headers)) {
|
|
1275
|
-
response.headers.set(name, value);
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
return response;
|
|
1279
|
-
};
|
|
1280
|
-
|
|
1281
|
-
try {
|
|
1282
|
-
const result = await executeChain(0);
|
|
1283
|
-
if (result) return applyHeaders(result);
|
|
1284
|
-
if (notFoundHandler) {
|
|
1285
|
-
const ctx = createContext(req, {}, store);
|
|
1286
|
-
return applyHeaders(notFoundHandler(ctx));
|
|
1287
|
-
}
|
|
1288
|
-
return new Response('Not Found', { status: 404 });
|
|
1289
|
-
} catch (err) {
|
|
1290
|
-
if (errorHandler) {
|
|
1291
|
-
const ctx = createContext(req, {}, store);
|
|
1292
|
-
return applyHeaders(errorHandler(err, ctx));
|
|
1293
|
-
}
|
|
1294
|
-
throw err;
|
|
1295
|
-
}
|
|
1296
|
-
};
|
|
1297
|
-
|
|
1298
|
-
const appObj = {
|
|
1299
|
-
get: (path, ...handlers) => addRoute('GET', path, ...handlers),
|
|
1300
|
-
post: (path, ...handlers) => addRoute('POST', path, ...handlers),
|
|
1301
|
-
put: (path, ...handlers) => addRoute('PUT', path, ...handlers),
|
|
1302
|
-
delete: (path, ...handlers) => addRoute('DELETE', path, ...handlers),
|
|
1303
|
-
patch: (path, ...handlers) => addRoute('PATCH', path, ...handlers),
|
|
1304
|
-
all: (path, ...handlers) => addRoute('ALL', path, ...handlers),
|
|
1305
|
-
use: (pathOrHandler, handler) => {
|
|
1306
|
-
const path = typeof pathOrHandler === 'string' ? pathOrHandler : '/*';
|
|
1307
|
-
const h = typeof pathOrHandler === 'function' ? pathOrHandler : handler;
|
|
1308
|
-
const { regex, params } = parsePattern(path);
|
|
1309
|
-
middleware.push({ prefix: path, regex, handler: h });
|
|
1310
|
-
},
|
|
1311
|
-
route: (basePath, subApp) => {
|
|
1312
|
-
for (const route of subApp._routes || []) {
|
|
1313
|
-
addRoute(route.method, basePath + route.pattern, ...route.handlers);
|
|
1314
|
-
}
|
|
1315
|
-
},
|
|
1316
|
-
basePath: (prefix) => {
|
|
1317
|
-
const subApp = {
|
|
1318
|
-
get: (path, ...handlers) => { addRoute('GET', prefix + path, ...handlers); return subApp; },
|
|
1319
|
-
post: (path, ...handlers) => { addRoute('POST', prefix + path, ...handlers); return subApp; },
|
|
1320
|
-
put: (path, ...handlers) => { addRoute('PUT', prefix + path, ...handlers); return subApp; },
|
|
1321
|
-
delete: (path, ...handlers) => { addRoute('DELETE', prefix + path, ...handlers); return subApp; },
|
|
1322
|
-
patch: (path, ...handlers) => { addRoute('PATCH', prefix + path, ...handlers); return subApp; },
|
|
1323
|
-
all: (path, ...handlers) => { addRoute('ALL', prefix + path, ...handlers); return subApp; },
|
|
1324
|
-
basePath: (subPrefix) => appObj.basePath(prefix + subPrefix),
|
|
1325
|
-
_routes: routes,
|
|
1326
|
-
};
|
|
1327
|
-
return subApp;
|
|
1328
|
-
},
|
|
1329
|
-
notFound: (handler) => { notFoundHandler = handler; },
|
|
1330
|
-
onError: (handler) => { errorHandler = handler; },
|
|
1331
|
-
request: async (path, options = {}) => {
|
|
1332
|
-
const url = path.startsWith('http') ? path : 'http://localhost' + path;
|
|
1333
|
-
const req = new Request(url, {
|
|
1334
|
-
method: options.method || 'GET',
|
|
1335
|
-
headers: options.headers || {},
|
|
1336
|
-
body: options.body,
|
|
1337
|
-
});
|
|
1338
|
-
return handleRequest(req);
|
|
1339
|
-
},
|
|
1340
|
-
fetch: handleRequest,
|
|
1341
|
-
_routes: routes,
|
|
1342
|
-
};
|
|
1343
|
-
return appObj;
|
|
1344
|
-
};
|
|
1345
|
-
|
|
1346
|
-
const app = __createHonoApp__();
|
|
1347
|
-
`
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
function getMdxRenderingTemplate(): string {
|
|
1351
|
-
return `
|
|
1352
|
-
// ============================================================
|
|
1353
|
-
// MDX Rendering Utilities
|
|
1354
|
-
// ============================================================
|
|
1355
|
-
|
|
1356
|
-
const __extractFrontmatter__ = (content) => {
|
|
1357
|
-
const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);
|
|
1358
|
-
if (!match) return { frontmatter: {}, body: content };
|
|
1359
|
-
try {
|
|
1360
|
-
const fm = {};
|
|
1361
|
-
match[1].split('\\n').forEach(line => {
|
|
1362
|
-
const [key, ...vals] = line.split(':');
|
|
1363
|
-
if (key && vals.length) fm[key.trim()] = vals.join(':').trim();
|
|
1364
|
-
});
|
|
1365
|
-
return { frontmatter: fm, body: match[2] };
|
|
1366
|
-
} catch {
|
|
1367
|
-
return { frontmatter: {}, body: content };
|
|
1368
|
-
}
|
|
1369
|
-
};
|
|
1370
|
-
|
|
1371
|
-
const render = {
|
|
1372
|
-
markdown: (content, options = {}) => {
|
|
1373
|
-
const { frontmatter, body } = __extractFrontmatter__(content);
|
|
1374
|
-
if (options.includeFrontmatter) return content;
|
|
1375
|
-
return body.trim();
|
|
1376
|
-
},
|
|
1377
|
-
toc: (content) => {
|
|
1378
|
-
const { body } = __extractFrontmatter__(content);
|
|
1379
|
-
const headings = [];
|
|
1380
|
-
const regex = /^(#{1,6})\\s+(.+)$/gm;
|
|
1381
|
-
let match;
|
|
1382
|
-
while ((match = regex.exec(body)) !== null) {
|
|
1383
|
-
headings.push({
|
|
1384
|
-
level: match[1].length,
|
|
1385
|
-
text: match[2].trim(),
|
|
1386
|
-
slug: match[2].trim().toLowerCase().replace(/[^a-z0-9]+/g, '-')
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
return headings;
|
|
1390
|
-
},
|
|
1391
|
-
html: (content) => {
|
|
1392
|
-
const { body } = __extractFrontmatter__(content);
|
|
1393
|
-
return body
|
|
1394
|
-
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
|
1395
|
-
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
|
1396
|
-
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
|
1397
|
-
.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')
|
|
1398
|
-
.replace(/\\*(.+?)\\*/g, '<em>$1</em>')
|
|
1399
|
-
.replace(/\\[(.+?)\\]\\((.+?)\\)/g, '<a href="$2">$1</a>')
|
|
1400
|
-
.replace(/^- (.+)$/gm, '<li>$1</li>')
|
|
1401
|
-
.replace(/(<li>.*<\\/li>\\n?)+/g, '<ul>$&</ul>')
|
|
1402
|
-
.replace(/^\\n+|\\n+$/g, '');
|
|
1403
|
-
}
|
|
1404
|
-
};
|
|
1405
|
-
|
|
1406
|
-
// React-like hooks stubs
|
|
1407
|
-
let __hook_state__ = [];
|
|
1408
|
-
let __hook_index__ = 0;
|
|
1409
|
-
|
|
1410
|
-
const useState = (initial) => {
|
|
1411
|
-
const idx = __hook_index__++;
|
|
1412
|
-
if (__hook_state__[idx] === undefined) __hook_state__[idx] = initial;
|
|
1413
|
-
const setState = (newVal) => {
|
|
1414
|
-
__hook_state__[idx] = typeof newVal === 'function' ? newVal(__hook_state__[idx]) : newVal;
|
|
1415
|
-
};
|
|
1416
|
-
return [__hook_state__[idx], setState];
|
|
1417
|
-
};
|
|
1418
|
-
|
|
1419
|
-
const useEffect = (fn, deps) => { /* No-op in server context */ };
|
|
1420
|
-
const useRef = (initial) => ({ current: initial });
|
|
1421
|
-
const useMemo = (fn, deps) => fn();
|
|
1422
|
-
const useCallback = (fn, deps) => fn;
|
|
1423
|
-
const Suspense = ({ children, fallback }) => children;
|
|
1424
|
-
|
|
1425
|
-
const renderToStream = async (element, stream) => {
|
|
1426
|
-
const html = __renderJsx__(element);
|
|
1427
|
-
await stream.write(html);
|
|
1428
|
-
};
|
|
1429
|
-
|
|
1430
|
-
const serialize = {
|
|
1431
|
-
clientProps: (props) => {
|
|
1432
|
-
const result = {};
|
|
1433
|
-
for (const [key, value] of Object.entries(props || {})) {
|
|
1434
|
-
if (typeof value === 'function') result[key] = { __rpc: true, name: value.name || 'anonymous' };
|
|
1435
|
-
else result[key] = value;
|
|
1436
|
-
}
|
|
1437
|
-
return result;
|
|
1438
|
-
},
|
|
1439
|
-
json: JSON.stringify,
|
|
1440
|
-
parse: JSON.parse
|
|
1441
|
-
};
|
|
1442
|
-
|
|
1443
|
-
Object.defineProperty(Function.prototype, 'isClient', {
|
|
1444
|
-
get: function() { return __isClientComponent__(this); },
|
|
1445
|
-
configurable: true,
|
|
1446
|
-
enumerable: false
|
|
1447
|
-
});
|
|
1448
|
-
|
|
1449
|
-
const parseUrl = (urlString) => {
|
|
1450
|
-
try {
|
|
1451
|
-
const url = new URL(urlString);
|
|
1452
|
-
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
1453
|
-
return {
|
|
1454
|
-
protocol: url.protocol.replace(':', ''),
|
|
1455
|
-
host: url.host,
|
|
1456
|
-
pathname: url.pathname,
|
|
1457
|
-
path: pathParts,
|
|
1458
|
-
search: url.search,
|
|
1459
|
-
hash: url.hash,
|
|
1460
|
-
origin: url.origin
|
|
1461
|
-
};
|
|
1462
|
-
} catch {
|
|
1463
|
-
return { pathname: urlString, path: urlString.split('/').filter(Boolean) };
|
|
1464
|
-
}
|
|
1465
|
-
};
|
|
1466
|
-
`
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
function getWorkflowSystemTemplate(): string {
|
|
1470
|
-
// This is a very long template - I'll include the essential parts
|
|
1471
|
-
return `
|
|
1472
|
-
// ============================================================
|
|
1473
|
-
// Workflow/Event System (aligned with ai-workflows)
|
|
1474
|
-
// ============================================================
|
|
1475
|
-
|
|
1476
|
-
const __event_handlers__ = new Map();
|
|
1477
|
-
const __schedule_handlers__ = [];
|
|
1478
|
-
const __workflow_history__ = [];
|
|
1479
|
-
|
|
1480
|
-
const __KNOWN_PATTERNS__ = {
|
|
1481
|
-
second: '* * * * * *', minute: '* * * * *', hour: '0 * * * *',
|
|
1482
|
-
day: '0 0 * * *', week: '0 0 * * 0', month: '0 0 1 * *', year: '0 0 1 1 *',
|
|
1483
|
-
Monday: '0 0 * * 1', Tuesday: '0 0 * * 2', Wednesday: '0 0 * * 3',
|
|
1484
|
-
Thursday: '0 0 * * 4', Friday: '0 0 * * 5', Saturday: '0 0 * * 6', Sunday: '0 0 * * 0',
|
|
1485
|
-
weekday: '0 0 * * 1-5', weekend: '0 0 * * 0,6', midnight: '0 0 * * *', noon: '0 12 * * *'
|
|
1486
|
-
};
|
|
1487
|
-
|
|
1488
|
-
const __TIME_PATTERNS__ = {
|
|
1489
|
-
at6am: { hour: 6, minute: 0 }, at7am: { hour: 7, minute: 0 }, at8am: { hour: 8, minute: 0 },
|
|
1490
|
-
at9am: { hour: 9, minute: 0 }, at10am: { hour: 10, minute: 0 }, at11am: { hour: 11, minute: 0 },
|
|
1491
|
-
at12pm: { hour: 12, minute: 0 }, atnoon: { hour: 12, minute: 0 }, at1pm: { hour: 13, minute: 0 },
|
|
1492
|
-
at2pm: { hour: 14, minute: 0 }, at3pm: { hour: 15, minute: 0 }, at4pm: { hour: 16, minute: 0 },
|
|
1493
|
-
at5pm: { hour: 17, minute: 0 }, at6pm: { hour: 18, minute: 0 }, at7pm: { hour: 19, minute: 0 },
|
|
1494
|
-
at8pm: { hour: 20, minute: 0 }, at9pm: { hour: 21, minute: 0 }, atmidnight: { hour: 0, minute: 0 }
|
|
1495
|
-
};
|
|
1496
|
-
|
|
1497
|
-
const __parseEvent__ = (event) => {
|
|
1498
|
-
const parts = event.split('.');
|
|
1499
|
-
if (parts.length !== 2) return null;
|
|
1500
|
-
return { noun: parts[0], event: parts[1] };
|
|
1501
|
-
};
|
|
1502
|
-
|
|
1503
|
-
const __registerEventHandler__ = (noun, event, handler) => {
|
|
1504
|
-
const key = noun + '.' + event;
|
|
1505
|
-
if (!__event_handlers__.has(key)) __event_handlers__.set(key, []);
|
|
1506
|
-
__event_handlers__.get(key).push({ handler, source: handler.toString() });
|
|
1507
|
-
};
|
|
1508
|
-
|
|
1509
|
-
const __registerScheduleHandler__ = (interval, handler) => {
|
|
1510
|
-
__schedule_handlers__.push({ interval, handler, source: handler.toString() });
|
|
1511
|
-
};
|
|
1512
|
-
|
|
1513
|
-
const on = new Proxy(function(event, filterOrHandler, handler) {
|
|
1514
|
-
if (typeof event === 'string') {
|
|
1515
|
-
const actualHandler = typeof filterOrHandler === 'function' ? filterOrHandler : handler;
|
|
1516
|
-
const filter = typeof filterOrHandler === 'object' ? filterOrHandler : null;
|
|
1517
|
-
const key = event;
|
|
1518
|
-
if (!__event_handlers__.has(key)) __event_handlers__.set(key, []);
|
|
1519
|
-
__event_handlers__.get(key).push({ handler: actualHandler, filter, source: actualHandler.toString() });
|
|
1520
|
-
return {
|
|
1521
|
-
off: () => {
|
|
1522
|
-
const handlers = __event_handlers__.get(key);
|
|
1523
|
-
if (handlers) {
|
|
1524
|
-
const idx = handlers.findIndex(h => h.handler === actualHandler);
|
|
1525
|
-
if (idx > -1) handlers.splice(idx, 1);
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
};
|
|
1529
|
-
}
|
|
1530
|
-
}, {
|
|
1531
|
-
get: (target, prop) => {
|
|
1532
|
-
if (prop === 'once') {
|
|
1533
|
-
return (event, handler) => {
|
|
1534
|
-
const wrapper = async (data, $) => {
|
|
1535
|
-
const key = event;
|
|
1536
|
-
const handlers = __event_handlers__.get(key);
|
|
1537
|
-
if (handlers) {
|
|
1538
|
-
const idx = handlers.findIndex(h => h.handler === wrapper);
|
|
1539
|
-
if (idx > -1) handlers.splice(idx, 1);
|
|
1540
|
-
}
|
|
1541
|
-
return handler(data, $);
|
|
1542
|
-
};
|
|
1543
|
-
const key = event;
|
|
1544
|
-
if (!__event_handlers__.has(key)) __event_handlers__.set(key, []);
|
|
1545
|
-
__event_handlers__.get(key).push({ handler: wrapper, source: handler.toString() });
|
|
1546
|
-
return { off: () => {} };
|
|
1547
|
-
};
|
|
1548
|
-
}
|
|
1549
|
-
const noun = String(prop);
|
|
1550
|
-
return new Proxy({}, {
|
|
1551
|
-
get: (_, eventName) => (handler) => {
|
|
1552
|
-
const key = noun + '.' + String(eventName);
|
|
1553
|
-
__registerEventHandler__(noun, String(eventName), handler);
|
|
1554
|
-
return {
|
|
1555
|
-
off: () => {
|
|
1556
|
-
const handlers = __event_handlers__.get(key);
|
|
1557
|
-
if (handlers) {
|
|
1558
|
-
const idx = handlers.findIndex(h => h.handler === handler);
|
|
1559
|
-
if (idx > -1) handlers.splice(idx, 1);
|
|
1560
|
-
}
|
|
1561
|
-
},
|
|
1562
|
-
unsubscribe: () => {
|
|
1563
|
-
const handlers = __event_handlers__.get(key);
|
|
1564
|
-
if (handlers) {
|
|
1565
|
-
const idx = handlers.findIndex(h => h.handler === handler);
|
|
1566
|
-
if (idx > -1) handlers.splice(idx, 1);
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
};
|
|
1570
|
-
}
|
|
1571
|
-
});
|
|
1572
|
-
},
|
|
1573
|
-
apply: (target, thisArg, args) => target(...args)
|
|
1574
|
-
});
|
|
1575
|
-
|
|
1576
|
-
const __parseDuration__ = (str) => {
|
|
1577
|
-
if (!str || typeof str !== 'string') return null;
|
|
1578
|
-
const match = str.match(/^(\\d+)(ms|s|m|h|d|w)?$/);
|
|
1579
|
-
if (!match) return null;
|
|
1580
|
-
const value = parseInt(match[1], 10);
|
|
1581
|
-
const unit = match[2] || 'ms';
|
|
1582
|
-
switch (unit) {
|
|
1583
|
-
case 'ms': return value;
|
|
1584
|
-
case 's': return value * 1000;
|
|
1585
|
-
case 'm': return value * 60 * 1000;
|
|
1586
|
-
case 'h': return value * 60 * 60 * 1000;
|
|
1587
|
-
case 'd': return value * 24 * 60 * 60 * 1000;
|
|
1588
|
-
case 'w': return value * 7 * 24 * 60 * 60 * 1000;
|
|
1589
|
-
default: return value;
|
|
1590
|
-
}
|
|
1591
|
-
};
|
|
1592
|
-
|
|
1593
|
-
const __isCronExpression__ = (str) => {
|
|
1594
|
-
if (!str || typeof str !== 'string') return false;
|
|
1595
|
-
const parts = str.trim().split(/\\s+/);
|
|
1596
|
-
return parts.length >= 5 && parts.length <= 6;
|
|
1597
|
-
};
|
|
1598
|
-
|
|
1599
|
-
const __schedule_timers__ = [];
|
|
1600
|
-
|
|
1601
|
-
const every = new Proxy(function(intervalOrName, handlerOrCronOrOptions, handlerArg) {
|
|
1602
|
-
let name = null;
|
|
1603
|
-
let interval = null;
|
|
1604
|
-
let handler = null;
|
|
1605
|
-
let options = {};
|
|
1606
|
-
|
|
1607
|
-
if (typeof handlerOrCronOrOptions === 'function') {
|
|
1608
|
-
interval = intervalOrName;
|
|
1609
|
-
handler = handlerOrCronOrOptions;
|
|
1610
|
-
} else if (typeof handlerArg === 'function') {
|
|
1611
|
-
name = intervalOrName;
|
|
1612
|
-
if (typeof handlerOrCronOrOptions === 'string') {
|
|
1613
|
-
interval = handlerOrCronOrOptions;
|
|
1614
|
-
} else {
|
|
1615
|
-
options = handlerOrCronOrOptions || {};
|
|
1616
|
-
interval = options.interval;
|
|
1617
|
-
}
|
|
1618
|
-
handler = handlerArg;
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
const isCron = __isCronExpression__(interval);
|
|
1622
|
-
const durationMs = isCron ? null : __parseDuration__(interval);
|
|
1623
|
-
|
|
1624
|
-
let stopped = false;
|
|
1625
|
-
let timer = null;
|
|
1626
|
-
|
|
1627
|
-
const job = {
|
|
1628
|
-
name: name || interval,
|
|
1629
|
-
cron: isCron ? interval : null,
|
|
1630
|
-
stopped: false,
|
|
1631
|
-
stop: () => {
|
|
1632
|
-
stopped = true;
|
|
1633
|
-
job.stopped = true;
|
|
1634
|
-
if (timer) clearInterval(timer);
|
|
1635
|
-
},
|
|
1636
|
-
next: () => new Date(Date.now() + (durationMs || 60000))
|
|
1637
|
-
};
|
|
1638
|
-
|
|
1639
|
-
if (durationMs) {
|
|
1640
|
-
if (options.immediate) Promise.resolve().then(() => handler());
|
|
1641
|
-
timer = setInterval(async () => {
|
|
1642
|
-
if (stopped) return;
|
|
1643
|
-
if (options.until && options.until()) { job.stop(); return; }
|
|
1644
|
-
try { await handler(); } catch (e) { console.error('[every] Handler error:', e); }
|
|
1645
|
-
}, durationMs);
|
|
1646
|
-
__schedule_timers__.push(timer);
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
__registerScheduleHandler__({
|
|
1650
|
-
type: isCron ? 'cron' : 'duration',
|
|
1651
|
-
expression: isCron ? interval : null,
|
|
1652
|
-
ms: durationMs,
|
|
1653
|
-
natural: name || interval
|
|
1654
|
-
}, handler);
|
|
1655
|
-
|
|
1656
|
-
return job;
|
|
1657
|
-
}, {
|
|
1658
|
-
get: (target, prop) => {
|
|
1659
|
-
const propStr = String(prop);
|
|
1660
|
-
const pattern = __KNOWN_PATTERNS__[propStr];
|
|
1661
|
-
if (pattern) {
|
|
1662
|
-
const result = (handler) => {
|
|
1663
|
-
__registerScheduleHandler__({ type: 'cron', expression: pattern, natural: propStr }, handler);
|
|
1664
|
-
return { stop: () => {}, cancel: () => {}, name: propStr, cron: pattern, stopped: false };
|
|
1665
|
-
};
|
|
1666
|
-
return new Proxy(result, {
|
|
1667
|
-
get: (_, timeKey) => {
|
|
1668
|
-
const time = __TIME_PATTERNS__[String(timeKey)];
|
|
1669
|
-
if (time) {
|
|
1670
|
-
const parts = pattern.split(' ');
|
|
1671
|
-
parts[0] = String(time.minute);
|
|
1672
|
-
parts[1] = String(time.hour);
|
|
1673
|
-
const cron = parts.join(' ');
|
|
1674
|
-
return (handler) => {
|
|
1675
|
-
__registerScheduleHandler__({ type: 'cron', expression: cron, natural: propStr + '.' + String(timeKey) }, handler);
|
|
1676
|
-
return { stop: () => {}, cancel: () => {}, name: propStr + '.' + String(timeKey), cron, stopped: false };
|
|
1677
|
-
};
|
|
1678
|
-
}
|
|
1679
|
-
return undefined;
|
|
1680
|
-
},
|
|
1681
|
-
apply: (_, __, args) => {
|
|
1682
|
-
__registerScheduleHandler__({ type: 'cron', expression: pattern, natural: propStr }, args[0]);
|
|
1683
|
-
return { stop: () => {}, cancel: () => {}, name: propStr, cron: pattern, stopped: false };
|
|
1684
|
-
}
|
|
1685
|
-
});
|
|
1686
|
-
}
|
|
1687
|
-
const pluralUnits = { seconds: 'second', minutes: 'minute', hours: 'hour', days: 'day', weeks: 'week' };
|
|
1688
|
-
if (pluralUnits[propStr]) {
|
|
1689
|
-
return (value) => (handler) => {
|
|
1690
|
-
__registerScheduleHandler__({ type: pluralUnits[propStr], value, natural: value + ' ' + propStr }, handler);
|
|
1691
|
-
return { stop: () => {}, cancel: () => {}, name: value + ' ' + propStr, stopped: false };
|
|
1692
|
-
};
|
|
1693
|
-
}
|
|
1694
|
-
return undefined;
|
|
1695
|
-
},
|
|
1696
|
-
apply: (target, thisArg, args) => target(...args)
|
|
1697
|
-
});
|
|
1698
|
-
|
|
1699
|
-
const send = async (event, data, options = {}) => {
|
|
1700
|
-
const eventId = __generateId__();
|
|
1701
|
-
const timestamp = Date.now();
|
|
1702
|
-
const eventObj = { id: eventId, type: event, data, timestamp, correlationId: options.correlationId };
|
|
1703
|
-
__workflow_history__.push({ type: 'event', name: event, data, timestamp });
|
|
1704
|
-
|
|
1705
|
-
if (options.delay) {
|
|
1706
|
-
const delayMs = typeof options.delay === 'string' ? __parseDuration__(options.delay) : options.delay;
|
|
1707
|
-
if (delayMs) await new Promise(r => setTimeout(r, delayMs));
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
const matchingHandlers = [];
|
|
1711
|
-
for (const [key, handlers] of __event_handlers__) {
|
|
1712
|
-
const keyPattern = key.replace(/\\.\\*/g, '\\\\.[^.]+');
|
|
1713
|
-
const regex = new RegExp('^' + keyPattern + '$');
|
|
1714
|
-
if (key === event || regex.test(event) || (key.endsWith('.*') && event.startsWith(key.slice(0, -1)))) {
|
|
1715
|
-
for (const h of handlers) {
|
|
1716
|
-
if (h.filter?.channel && h.filter.channel !== options.channel) continue;
|
|
1717
|
-
if (h.filter?.where) {
|
|
1718
|
-
let match = true;
|
|
1719
|
-
for (const [k, v] of Object.entries(h.filter.where)) {
|
|
1720
|
-
if (data[k] !== v) { match = false; break; }
|
|
1721
|
-
}
|
|
1722
|
-
if (!match) continue;
|
|
1723
|
-
}
|
|
1724
|
-
matchingHandlers.push(h);
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
if (options.wait && matchingHandlers.length > 0) {
|
|
1730
|
-
let response = null;
|
|
1731
|
-
const reply = (data) => { response = { data }; };
|
|
1732
|
-
await matchingHandlers[0].handler({ type: event, data, correlationId: options.correlationId }, reply);
|
|
1733
|
-
return response || eventObj;
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
await Promise.all(matchingHandlers.map(async ({ handler }) => {
|
|
1737
|
-
try { await handler({ type: event, data, correlationId: options.correlationId }, $); }
|
|
1738
|
-
catch (error) { console.error('Error in handler for ' + event + ':', error); }
|
|
1739
|
-
}));
|
|
1740
|
-
|
|
1741
|
-
return eventObj;
|
|
1742
|
-
};
|
|
1743
|
-
|
|
1744
|
-
send.broadcast = async (event, data) => send(event, data);
|
|
1745
|
-
|
|
1746
|
-
const delay = (ms) => {
|
|
1747
|
-
if (typeof ms === 'string') ms = __parseDuration__(ms) || 0;
|
|
1748
|
-
return new Promise(r => setTimeout(r, ms));
|
|
1749
|
-
};
|
|
1750
|
-
|
|
1751
|
-
const decide = (subject) => {
|
|
1752
|
-
const conditions = [];
|
|
1753
|
-
let defaultValue = null;
|
|
1754
|
-
|
|
1755
|
-
const chain = {
|
|
1756
|
-
when: (conditionOrFn, result) => {
|
|
1757
|
-
conditions.push({ condition: conditionOrFn, result });
|
|
1758
|
-
return chain;
|
|
1759
|
-
},
|
|
1760
|
-
otherwise: (result) => {
|
|
1761
|
-
defaultValue = result;
|
|
1762
|
-
for (const { condition, result } of conditions) {
|
|
1763
|
-
let matches = false;
|
|
1764
|
-
if (typeof condition === 'function') matches = condition(subject);
|
|
1765
|
-
else if (typeof condition === 'object') {
|
|
1766
|
-
matches = true;
|
|
1767
|
-
for (const [key, value] of Object.entries(condition)) {
|
|
1768
|
-
const subjectValue = subject[key];
|
|
1769
|
-
if (typeof value === 'object' && value !== null) {
|
|
1770
|
-
for (const [op, opVal] of Object.entries(value)) {
|
|
1771
|
-
switch (op) {
|
|
1772
|
-
case '$gte': if (!(subjectValue >= opVal)) matches = false; break;
|
|
1773
|
-
case '$gt': if (!(subjectValue > opVal)) matches = false; break;
|
|
1774
|
-
case '$lte': if (!(subjectValue <= opVal)) matches = false; break;
|
|
1775
|
-
case '$lt': if (!(subjectValue < opVal)) matches = false; break;
|
|
1776
|
-
case '$ne': if (subjectValue === opVal) matches = false; break;
|
|
1777
|
-
default: if (subjectValue !== value) matches = false;
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
} else if (subjectValue !== value) matches = false;
|
|
1781
|
-
if (!matches) break;
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
if (matches) return typeof result === 'function' ? result() : result;
|
|
1785
|
-
}
|
|
1786
|
-
return typeof defaultValue === 'function' ? defaultValue() : defaultValue;
|
|
1787
|
-
}
|
|
1788
|
-
};
|
|
1789
|
-
return chain;
|
|
1790
|
-
};
|
|
1791
|
-
|
|
1792
|
-
decide.async = (subject) => {
|
|
1793
|
-
const conditions = [];
|
|
1794
|
-
const chain = {
|
|
1795
|
-
when: (conditionFn, result) => {
|
|
1796
|
-
conditions.push({ condition: conditionFn, result });
|
|
1797
|
-
return chain;
|
|
1798
|
-
},
|
|
1799
|
-
otherwise: async (defaultResult) => {
|
|
1800
|
-
for (const { condition, result } of conditions) {
|
|
1801
|
-
const matches = await condition(subject);
|
|
1802
|
-
if (matches) return result;
|
|
1803
|
-
}
|
|
1804
|
-
return defaultResult;
|
|
1805
|
-
}
|
|
1806
|
-
};
|
|
1807
|
-
return chain;
|
|
1808
|
-
};
|
|
1809
|
-
|
|
1810
|
-
const __tracked_events__ = [];
|
|
1811
|
-
const track = async (event, data, metadata = {}) => {
|
|
1812
|
-
const entry = { type: event, data, metadata, timestamp: Date.now(), userId: metadata.userId };
|
|
1813
|
-
__tracked_events__.push(entry);
|
|
1814
|
-
return entry;
|
|
1815
|
-
};
|
|
1816
|
-
|
|
1817
|
-
track.user = async (userId, event, data) => {
|
|
1818
|
-
const entry = { type: event, data, userId, timestamp: Date.now() };
|
|
1819
|
-
__tracked_events__.push(entry);
|
|
1820
|
-
return entry;
|
|
1821
|
-
};
|
|
1822
|
-
|
|
1823
|
-
track.query = async (options = {}) => {
|
|
1824
|
-
let results = [...__tracked_events__];
|
|
1825
|
-
if (options.type) results = results.filter(e => e.type === options.type);
|
|
1826
|
-
if (options.userId) results = results.filter(e => e.userId === options.userId);
|
|
1827
|
-
if (options['data.buttonId']) results = results.filter(e => e.data?.buttonId === options['data.buttonId']);
|
|
1828
|
-
if (options.orderBy) {
|
|
1829
|
-
results.sort((a, b) => {
|
|
1830
|
-
const aVal = a[options.orderBy];
|
|
1831
|
-
const bVal = b[options.orderBy];
|
|
1832
|
-
return options.order === 'desc' ? bVal - aVal : aVal - bVal;
|
|
1833
|
-
});
|
|
1834
|
-
}
|
|
1835
|
-
if (options.limit) results = results.slice(0, options.limit);
|
|
1836
|
-
return results;
|
|
1837
|
-
};
|
|
1838
|
-
|
|
1839
|
-
track.funnel = async (steps, options = {}) => {
|
|
1840
|
-
const events = await track.query(options);
|
|
1841
|
-
const completed = steps.every(step => events.some(e => e.type === step));
|
|
1842
|
-
return {
|
|
1843
|
-
steps: steps.map(step => ({ step, completed: events.some(e => e.type === step) })),
|
|
1844
|
-
completed,
|
|
1845
|
-
conversionRate: completed ? 1 : 0
|
|
1846
|
-
};
|
|
1847
|
-
};
|
|
1848
|
-
|
|
1849
|
-
track.aggregate = async (event, options = {}) => {
|
|
1850
|
-
const events = __tracked_events__.filter(e => e.type === event);
|
|
1851
|
-
const groups = {};
|
|
1852
|
-
for (const e of events) {
|
|
1853
|
-
const key = options.groupBy ? e.data[options.groupBy] : '_all';
|
|
1854
|
-
if (!groups[key]) groups[key] = { sum: 0, count: 0, values: [] };
|
|
1855
|
-
groups[key].count++;
|
|
1856
|
-
if (options.sum) groups[key].sum += e.data[options.sum] || 0;
|
|
1857
|
-
groups[key].values.push(e);
|
|
1858
|
-
}
|
|
1859
|
-
return groups;
|
|
1860
|
-
};
|
|
1861
|
-
|
|
1862
|
-
track.timeseries = async (event, options = {}) => {
|
|
1863
|
-
const events = __tracked_events__.filter(e => e.type === event);
|
|
1864
|
-
return events.map(e => ({
|
|
1865
|
-
timestamp: e.timestamp,
|
|
1866
|
-
value: options.field ? e.data[options.field] : 1
|
|
1867
|
-
}));
|
|
1868
|
-
};
|
|
1869
|
-
|
|
1870
|
-
const experiment = (name) => {
|
|
1871
|
-
const variants = [];
|
|
1872
|
-
let eligibilityFn = null;
|
|
1873
|
-
const overrides = new Map();
|
|
1874
|
-
let rolloutConfig = null;
|
|
1875
|
-
|
|
1876
|
-
const exp = {
|
|
1877
|
-
variant: (variantName, config, options = {}) => {
|
|
1878
|
-
variants.push({ name: variantName, config, weight: options.weight || 1 });
|
|
1879
|
-
return exp;
|
|
1880
|
-
},
|
|
1881
|
-
eligible: (fn) => { eligibilityFn = fn; return exp; },
|
|
1882
|
-
override: (userId, variantName) => { overrides.set(userId, variantName); return exp; },
|
|
1883
|
-
rollout: (variantName, percentage) => { rolloutConfig = { variant: variantName, percentage }; return exp; },
|
|
1884
|
-
assign: (userId) => {
|
|
1885
|
-
if (overrides.has(userId)) {
|
|
1886
|
-
const overrideVariant = variants.find(v => v.name === overrides.get(userId));
|
|
1887
|
-
if (overrideVariant) return { name: overrideVariant.name, ...overrideVariant.config };
|
|
1888
|
-
}
|
|
1889
|
-
if (eligibilityFn && typeof userId === 'object' && !eligibilityFn(userId)) {
|
|
1890
|
-
return variants[0] ? { name: variants[0].name, ...variants[0].config } : {};
|
|
1891
|
-
}
|
|
1892
|
-
const userIdStr = typeof userId === 'object' ? userId.id || JSON.stringify(userId) : String(userId);
|
|
1893
|
-
let hash = 0;
|
|
1894
|
-
for (let i = 0; i < userIdStr.length; i++) {
|
|
1895
|
-
hash = ((hash << 5) - hash) + userIdStr.charCodeAt(i);
|
|
1896
|
-
hash = hash & hash;
|
|
1897
|
-
}
|
|
1898
|
-
const normalized = Math.abs(hash % 100) / 100;
|
|
1899
|
-
if (rolloutConfig && normalized < rolloutConfig.percentage) {
|
|
1900
|
-
const rolloutVariant = variants.find(v => v.name === rolloutConfig.variant);
|
|
1901
|
-
if (rolloutVariant) return { name: rolloutVariant.name, ...rolloutVariant.config };
|
|
1902
|
-
}
|
|
1903
|
-
const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
|
|
1904
|
-
let cumulative = 0;
|
|
1905
|
-
for (const v of variants) {
|
|
1906
|
-
cumulative += v.weight / totalWeight;
|
|
1907
|
-
if (normalized < cumulative) return { name: v.name, ...v.config };
|
|
1908
|
-
}
|
|
1909
|
-
return variants[0] ? { name: variants[0].name, ...variants[0].config } : {};
|
|
1910
|
-
},
|
|
1911
|
-
track: {
|
|
1912
|
-
exposure: async (userId) => { await track('experiment.exposure', { experiment: name, userId }); },
|
|
1913
|
-
conversion: async (userId, data = {}) => { await track('experiment.conversion', { experiment: name, userId, ...data }); }
|
|
1914
|
-
},
|
|
1915
|
-
results: async () => {
|
|
1916
|
-
const exposures = __tracked_events__.filter(e => e.type === 'experiment.exposure' && e.data.experiment === name);
|
|
1917
|
-
const conversions = __tracked_events__.filter(e => e.type === 'experiment.conversion' && e.data.experiment === name);
|
|
1918
|
-
const result = {};
|
|
1919
|
-
for (const v of variants) result[v.name] = { exposures: 0, conversions: 0 };
|
|
1920
|
-
for (const e of exposures) {
|
|
1921
|
-
const variantName = exp.assign(e.data.userId).name;
|
|
1922
|
-
if (result[variantName]) result[variantName].exposures++;
|
|
1923
|
-
}
|
|
1924
|
-
for (const e of conversions) {
|
|
1925
|
-
const variantName = exp.assign(e.data.userId).name;
|
|
1926
|
-
if (result[variantName]) result[variantName].conversions++;
|
|
1927
|
-
}
|
|
1928
|
-
return result;
|
|
1929
|
-
}
|
|
1930
|
-
};
|
|
1931
|
-
return exp;
|
|
1932
|
-
};
|
|
1933
|
-
|
|
1934
|
-
const __doEvent__ = async (event, data) => {
|
|
1935
|
-
__workflow_history__.push({ type: 'action', name: 'do:' + event, data, timestamp: Date.now() });
|
|
1936
|
-
const parsed = __parseEvent__(event);
|
|
1937
|
-
if (!parsed) throw new Error('Invalid event format: ' + event + '. Expected Noun.event');
|
|
1938
|
-
const key = parsed.noun + '.' + parsed.event;
|
|
1939
|
-
const handlers = __event_handlers__.get(key) || [];
|
|
1940
|
-
if (handlers.length === 0) throw new Error('No handler registered for ' + event);
|
|
1941
|
-
return await handlers[0].handler(data, $);
|
|
1942
|
-
};
|
|
1943
|
-
|
|
1944
|
-
const __tryEvent__ = async (event, data) => {
|
|
1945
|
-
__workflow_history__.push({ type: 'action', name: 'try:' + event, data, timestamp: Date.now() });
|
|
1946
|
-
const parsed = __parseEvent__(event);
|
|
1947
|
-
if (!parsed) throw new Error('Invalid event format: ' + event + '. Expected Noun.event');
|
|
1948
|
-
const key = parsed.noun + '.' + parsed.event;
|
|
1949
|
-
const handlers = __event_handlers__.get(key) || [];
|
|
1950
|
-
if (handlers.length === 0) throw new Error('No handler registered for ' + event);
|
|
1951
|
-
return await handlers[0].handler(data, $);
|
|
1952
|
-
};
|
|
1953
|
-
|
|
1954
|
-
const __queues__ = new Map();
|
|
1955
|
-
const __queue_stats__ = new Map();
|
|
1956
|
-
const queue = (name) => {
|
|
1957
|
-
if (!__queues__.has(name)) {
|
|
1958
|
-
__queues__.set(name, []);
|
|
1959
|
-
__queue_stats__.set(name, { added: 0, processed: 0, failed: 0, retried: 0 });
|
|
1960
|
-
}
|
|
1961
|
-
const q = __queues__.get(name);
|
|
1962
|
-
const stats = __queue_stats__.get(name);
|
|
1963
|
-
return {
|
|
1964
|
-
add: async (item, options = {}) => {
|
|
1965
|
-
const job = { id: __generateId__(), item, options, addedAt: new Date(), priority: options.priority || 'normal', attempts: 0 };
|
|
1966
|
-
q.push(job);
|
|
1967
|
-
stats.added++;
|
|
1968
|
-
return job;
|
|
1969
|
-
},
|
|
1970
|
-
process: async (handler) => {
|
|
1971
|
-
while (q.length) {
|
|
1972
|
-
const job = q.shift();
|
|
1973
|
-
try {
|
|
1974
|
-
job.attempts++;
|
|
1975
|
-
await handler(job.item);
|
|
1976
|
-
stats.processed++;
|
|
1977
|
-
} catch (e) {
|
|
1978
|
-
stats.failed++;
|
|
1979
|
-
if (job.attempts < (job.options.maxRetries || 3)) {
|
|
1980
|
-
q.push(job);
|
|
1981
|
-
stats.retried++;
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
},
|
|
1986
|
-
processBatch: async (handler, batchSize = 10) => {
|
|
1987
|
-
const batch = q.splice(0, batchSize);
|
|
1988
|
-
if (batch.length === 0) return;
|
|
1989
|
-
try {
|
|
1990
|
-
await handler(batch.map(j => j.item));
|
|
1991
|
-
stats.processed += batch.length;
|
|
1992
|
-
} catch (e) {
|
|
1993
|
-
stats.failed += batch.length;
|
|
1994
|
-
for (const job of batch) {
|
|
1995
|
-
if (job.attempts < (job.options.maxRetries || 3)) {
|
|
1996
|
-
job.attempts++;
|
|
1997
|
-
q.push(job);
|
|
1998
|
-
stats.retried++;
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
},
|
|
2003
|
-
size: () => q.length,
|
|
2004
|
-
clear: () => { q.length = 0; },
|
|
2005
|
-
stats: () => ({ ...stats, pending: q.length }),
|
|
2006
|
-
peek: (n = 1) => q.slice(0, n).map(j => j.item),
|
|
2007
|
-
getByPriority: (priority) => q.filter(j => j.priority === priority).map(j => j.item)
|
|
2008
|
-
};
|
|
2009
|
-
};
|
|
2010
|
-
|
|
2011
|
-
const __actors__ = new Map();
|
|
2012
|
-
const actor = (type) => ({
|
|
2013
|
-
register: (id, state = {}) => {
|
|
2014
|
-
const key = type + ':' + id;
|
|
2015
|
-
__actors__.set(key, { id, type, state, createdAt: new Date() });
|
|
2016
|
-
return __actors__.get(key);
|
|
2017
|
-
},
|
|
2018
|
-
get: (id) => __actors__.get(type + ':' + id),
|
|
2019
|
-
send: async (id, message) => {
|
|
2020
|
-
const a = __actors__.get(type + ':' + id);
|
|
2021
|
-
if (!a) throw new Error('Actor not found: ' + type + ':' + id);
|
|
2022
|
-
return { delivered: true };
|
|
2023
|
-
}
|
|
2024
|
-
});
|
|
2025
|
-
|
|
2026
|
-
const __actions__ = new Map();
|
|
2027
|
-
const actions = {
|
|
2028
|
-
async create(options) {
|
|
2029
|
-
const id = __generateId__();
|
|
2030
|
-
const action = {
|
|
2031
|
-
id, actor: options.actor, object: options.object, action: options.action,
|
|
2032
|
-
metadata: options.metadata || {}, status: 'pending',
|
|
2033
|
-
createdAt: new Date(), updatedAt: new Date()
|
|
2034
|
-
};
|
|
2035
|
-
__actions__.set(id, action);
|
|
2036
|
-
return action;
|
|
2037
|
-
},
|
|
2038
|
-
async get(id) { return __actions__.get(id) || null; },
|
|
2039
|
-
async start(id) {
|
|
2040
|
-
const action = __actions__.get(id);
|
|
2041
|
-
if (!action) throw new Error('Action not found: ' + id);
|
|
2042
|
-
action.status = 'active';
|
|
2043
|
-
action.startedAt = new Date();
|
|
2044
|
-
action.updatedAt = new Date();
|
|
2045
|
-
return action;
|
|
2046
|
-
},
|
|
2047
|
-
async complete(id, result = {}) {
|
|
2048
|
-
const action = __actions__.get(id);
|
|
2049
|
-
if (!action) throw new Error('Action not found: ' + id);
|
|
2050
|
-
action.status = 'completed';
|
|
2051
|
-
action.result = result;
|
|
2052
|
-
action.completedAt = new Date();
|
|
2053
|
-
action.updatedAt = new Date();
|
|
2054
|
-
return action;
|
|
2055
|
-
},
|
|
2056
|
-
async fail(id, error) {
|
|
2057
|
-
const action = __actions__.get(id);
|
|
2058
|
-
if (!action) throw new Error('Action not found: ' + id);
|
|
2059
|
-
action.status = 'failed';
|
|
2060
|
-
action.error = error;
|
|
2061
|
-
action.failedAt = new Date();
|
|
2062
|
-
action.updatedAt = new Date();
|
|
2063
|
-
return action;
|
|
2064
|
-
},
|
|
2065
|
-
async list(options = {}) {
|
|
2066
|
-
let results = Array.from(__actions__.values());
|
|
2067
|
-
if (options.status) results = results.filter(a => a.status === options.status);
|
|
2068
|
-
if (options.actor) results = results.filter(a => a.actor === options.actor);
|
|
2069
|
-
if (options.limit) results = results.slice(0, options.limit);
|
|
2070
|
-
return results;
|
|
2071
|
-
},
|
|
2072
|
-
async retry(id) {
|
|
2073
|
-
const action = __actions__.get(id);
|
|
2074
|
-
if (!action) throw new Error('Action not found: ' + id);
|
|
2075
|
-
action.status = 'pending';
|
|
2076
|
-
action.retryCount = (action.retryCount || 0) + 1;
|
|
2077
|
-
action.updatedAt = new Date();
|
|
2078
|
-
return action;
|
|
2079
|
-
},
|
|
2080
|
-
async cancel(id) {
|
|
2081
|
-
const action = __actions__.get(id);
|
|
2082
|
-
if (!action) throw new Error('Action not found: ' + id);
|
|
2083
|
-
action.status = 'cancelled';
|
|
2084
|
-
action.cancelledAt = new Date();
|
|
2085
|
-
action.updatedAt = new Date();
|
|
2086
|
-
return action;
|
|
2087
|
-
}
|
|
2088
|
-
};
|
|
2089
|
-
|
|
2090
|
-
const __artifacts__ = new Map();
|
|
2091
|
-
const artifacts = {
|
|
2092
|
-
async store(options) {
|
|
2093
|
-
const id = __generateId__();
|
|
2094
|
-
const artifact = {
|
|
2095
|
-
id, type: options.type, data: options.data,
|
|
2096
|
-
metadata: options.metadata || {}, createdAt: new Date()
|
|
2097
|
-
};
|
|
2098
|
-
__artifacts__.set(id, artifact);
|
|
2099
|
-
return artifact;
|
|
2100
|
-
},
|
|
2101
|
-
async get(id) { return __artifacts__.get(id) || null; },
|
|
2102
|
-
async list(options = {}) {
|
|
2103
|
-
let results = Array.from(__artifacts__.values());
|
|
2104
|
-
if (options.type) results = results.filter(a => a.type === options.type);
|
|
2105
|
-
if (options.limit) results = results.slice(0, options.limit);
|
|
2106
|
-
return results;
|
|
2107
|
-
}
|
|
2108
|
-
};
|
|
2109
|
-
|
|
2110
|
-
const __events__ = [];
|
|
2111
|
-
const events = {
|
|
2112
|
-
async record(options) {
|
|
2113
|
-
const event = {
|
|
2114
|
-
id: __generateId__(), type: options.type, subject: options.subject,
|
|
2115
|
-
data: options.data, metadata: options.metadata || {}, timestamp: new Date()
|
|
2116
|
-
};
|
|
2117
|
-
__events__.push(event);
|
|
2118
|
-
return event;
|
|
2119
|
-
},
|
|
2120
|
-
async list(options = {}) {
|
|
2121
|
-
let results = [...__events__];
|
|
2122
|
-
if (options.type) results = results.filter(e => e.type === options.type);
|
|
2123
|
-
if (options.subject) results = results.filter(e => e.subject === options.subject);
|
|
2124
|
-
if (options.limit) results = results.slice(0, options.limit);
|
|
2125
|
-
return results;
|
|
2126
|
-
},
|
|
2127
|
-
async query(options = {}) {
|
|
2128
|
-
let results = [...__events__];
|
|
2129
|
-
if (options.type) results = results.filter(e => e.type === options.type);
|
|
2130
|
-
if (options.subject) results = results.filter(e => e.subject === options.subject);
|
|
2131
|
-
if (options.actor) results = results.filter(e => e.metadata?.actor === options.actor);
|
|
2132
|
-
if (options.from) results = results.filter(e => new Date(e.timestamp) >= new Date(options.from));
|
|
2133
|
-
if (options.to) results = results.filter(e => new Date(e.timestamp) <= new Date(options.to));
|
|
2134
|
-
if (options.orderBy === 'timestamp') {
|
|
2135
|
-
results.sort((a, b) => options.order === 'desc' ? b.timestamp - a.timestamp : a.timestamp - b.timestamp);
|
|
2136
|
-
}
|
|
2137
|
-
if (options.limit) results = results.slice(0, options.limit);
|
|
2138
|
-
return results;
|
|
2139
|
-
},
|
|
2140
|
-
async replay(handler, options = {}) {
|
|
2141
|
-
const evts = await events.list(options);
|
|
2142
|
-
for (const evt of evts) await handler(evt);
|
|
2143
|
-
},
|
|
2144
|
-
subscribe(type, handler) {
|
|
2145
|
-
on(type, handler);
|
|
2146
|
-
return { unsubscribe: () => {} };
|
|
2147
|
-
},
|
|
2148
|
-
async emit(type, data, metadata = {}) {
|
|
2149
|
-
const event = await events.record({ type, data, metadata });
|
|
2150
|
-
await send(type, data);
|
|
2151
|
-
return event;
|
|
2152
|
-
}
|
|
2153
|
-
};
|
|
2154
|
-
|
|
2155
|
-
const parse = (content) => {
|
|
2156
|
-
const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);
|
|
2157
|
-
if (!frontmatterMatch) return { data: {}, content: content.trim(), type: null, id: null };
|
|
2158
|
-
const [, frontmatter, body] = frontmatterMatch;
|
|
2159
|
-
const data = {};
|
|
2160
|
-
let currentKey = null;
|
|
2161
|
-
let inArray = false;
|
|
2162
|
-
const lines = frontmatter.split('\\n');
|
|
2163
|
-
for (const line of lines) {
|
|
2164
|
-
const keyMatch = line.match(/^(\\w+):\\s*(.*)$/);
|
|
2165
|
-
if (keyMatch) {
|
|
2166
|
-
currentKey = keyMatch[1];
|
|
2167
|
-
const value = keyMatch[2].trim();
|
|
2168
|
-
if (value === '') { data[currentKey] = []; inArray = true; }
|
|
2169
|
-
else { data[currentKey] = value; inArray = false; }
|
|
2170
|
-
} else if (inArray && currentKey && line.match(/^\\s+-\\s+(.+)$/)) {
|
|
2171
|
-
const itemMatch = line.match(/^\\s+-\\s+(.+)$/);
|
|
2172
|
-
if (itemMatch) {
|
|
2173
|
-
if (!Array.isArray(data[currentKey])) data[currentKey] = [];
|
|
2174
|
-
data[currentKey].push(itemMatch[1].trim());
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
return { data, content: body.trim(), type: data.$type || data['@type'] || null, id: data.$id || data['@id'] || null };
|
|
2179
|
-
};
|
|
2180
|
-
|
|
2181
|
-
const stringify = (doc) => {
|
|
2182
|
-
const lines = ['---'];
|
|
2183
|
-
if (doc.type) lines.push('$type: ' + doc.type);
|
|
2184
|
-
if (doc.id) lines.push('$id: ' + doc.id);
|
|
2185
|
-
for (const [key, value] of Object.entries(doc.data || {})) {
|
|
2186
|
-
if (Array.isArray(value)) {
|
|
2187
|
-
lines.push(key + ':');
|
|
2188
|
-
for (const item of value) lines.push(' - ' + item);
|
|
2189
|
-
} else lines.push(key + ': ' + value);
|
|
2190
|
-
}
|
|
2191
|
-
lines.push('---');
|
|
2192
|
-
if (doc.content) lines.push('', doc.content);
|
|
2193
|
-
return lines.join('\\n');
|
|
2194
|
-
};
|
|
2195
|
-
|
|
2196
|
-
const toAst = (doc) => {
|
|
2197
|
-
const children = [];
|
|
2198
|
-
const lines = (doc.content || '').split('\\n');
|
|
2199
|
-
for (const line of lines) {
|
|
2200
|
-
if (line.startsWith('# ')) children.push({ type: 'heading', depth: 1, text: line.slice(2) });
|
|
2201
|
-
else if (line.startsWith('## ')) children.push({ type: 'heading', depth: 2, text: line.slice(3) });
|
|
2202
|
-
else if (line.startsWith('- ')) {
|
|
2203
|
-
const lastList = children[children.length - 1];
|
|
2204
|
-
if (lastList?.type === 'list') lastList.items.push(line.slice(2));
|
|
2205
|
-
else children.push({ type: 'list', items: [line.slice(2)] });
|
|
2206
|
-
} else if (line.startsWith('\`\`\`')) {
|
|
2207
|
-
const lang = line.slice(3).trim();
|
|
2208
|
-
children.push({ type: 'code', lang: lang || null, value: '' });
|
|
2209
|
-
} else if (line.trim()) children.push({ type: 'paragraph', text: line });
|
|
2210
|
-
}
|
|
2211
|
-
return { type: 'root', children };
|
|
2212
|
-
};
|
|
2213
|
-
|
|
2214
|
-
const renderMarkdown = (doc, options = {}) => {
|
|
2215
|
-
let result = '';
|
|
2216
|
-
if (options.includeFrontmatter && doc.data && Object.keys(doc.data).length > 0) {
|
|
2217
|
-
result += '---\\n';
|
|
2218
|
-
if (doc.type) result += '$type: ' + doc.type + '\\n';
|
|
2219
|
-
for (const [key, value] of Object.entries(doc.data)) {
|
|
2220
|
-
if (Array.isArray(value)) {
|
|
2221
|
-
result += key + ':\\n';
|
|
2222
|
-
for (const item of value) result += ' - ' + item + '\\n';
|
|
2223
|
-
} else result += key + ': ' + value + '\\n';
|
|
2224
|
-
}
|
|
2225
|
-
result += '---\\n';
|
|
2226
|
-
}
|
|
2227
|
-
result += doc.content || '';
|
|
2228
|
-
return result;
|
|
2229
|
-
};
|
|
2230
|
-
|
|
2231
|
-
const createComponents = (createElement) => {
|
|
2232
|
-
const components = {};
|
|
2233
|
-
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'];
|
|
2234
|
-
for (const name of componentNames) {
|
|
2235
|
-
components[name] = (props) => createElement(name.toLowerCase() === 'hero' ? 'header' : 'section', { 'data-component': name, ...props });
|
|
2236
|
-
}
|
|
2237
|
-
return components;
|
|
2238
|
-
};
|
|
2239
|
-
|
|
2240
|
-
const getComponentNames = () => ['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'];
|
|
2241
|
-
|
|
2242
|
-
const getComponentMeta = (name) => ({
|
|
2243
|
-
category: ['Hero', 'Features', 'Pricing', 'CTA', 'Testimonials'].includes(name) ? 'landing' : 'ui',
|
|
2244
|
-
requiredProps: name === 'Hero' ? ['title'] : [],
|
|
2245
|
-
related: name === 'Hero' ? ['CTA', 'Features'] : []
|
|
2246
|
-
});
|
|
2247
|
-
|
|
2248
|
-
const getComponentsByCategory = (category) => {
|
|
2249
|
-
if (category === 'landing') return ['Hero', 'Features', 'Pricing', 'CTA', 'Testimonials'];
|
|
2250
|
-
return ['Card', 'Button', 'Input', 'Form'];
|
|
2251
|
-
};
|
|
2252
|
-
|
|
2253
|
-
const extractTests = (content) => {
|
|
2254
|
-
const tests = [];
|
|
2255
|
-
const regex = /\`\`\`(?:ts|js)\\s+test[^\\n]*\\n([\\s\\S]*?)\`\`\`/g;
|
|
2256
|
-
let match;
|
|
2257
|
-
while ((match = regex.exec(content)) !== null) tests.push({ code: match[1].trim() });
|
|
2258
|
-
return tests;
|
|
2259
|
-
};
|
|
2260
|
-
|
|
2261
|
-
const parseMeta = (meta) => {
|
|
2262
|
-
const result = {};
|
|
2263
|
-
const parts = meta.split(/\\s+/);
|
|
2264
|
-
for (const part of parts) {
|
|
2265
|
-
if (part.includes('=')) {
|
|
2266
|
-
const [key, value] = part.split('=');
|
|
2267
|
-
result[key] = value;
|
|
2268
|
-
} else result[part] = true;
|
|
2269
|
-
}
|
|
2270
|
-
return result;
|
|
2271
|
-
};
|
|
2272
|
-
|
|
2273
|
-
const createElement = (type, props, ...children) => ({ type, props: props || {}, children });
|
|
2274
|
-
|
|
2275
|
-
const extractLinks = (content) => {
|
|
2276
|
-
const links = [];
|
|
2277
|
-
const regex = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;
|
|
2278
|
-
let match;
|
|
2279
|
-
while ((match = regex.exec(content)) !== null) links.push({ text: match[1], url: match[2] });
|
|
2280
|
-
return links;
|
|
2281
|
-
};
|
|
2282
|
-
|
|
2283
|
-
const extractRelationships = (doc) => {
|
|
2284
|
-
const relationships = [];
|
|
2285
|
-
const url = doc.id || doc.url;
|
|
2286
|
-
if (!url) return relationships;
|
|
2287
|
-
const links = extractLinks(doc.content || '');
|
|
2288
|
-
for (const link of links) relationships.push({ from: url, to: link.url, type: 'links', label: link.text });
|
|
2289
|
-
for (const [key, value] of Object.entries(doc.data || {})) {
|
|
2290
|
-
if (typeof value === 'string' && (value.startsWith('http') || value.startsWith('/'))) {
|
|
2291
|
-
relationships.push({ from: url, to: value, type: key });
|
|
2292
|
-
}
|
|
2293
|
-
if (Array.isArray(value)) {
|
|
2294
|
-
for (const item of value) {
|
|
2295
|
-
if (typeof item === 'string' && (item.startsWith('http') || item.startsWith('/'))) {
|
|
2296
|
-
relationships.push({ from: url, to: item, type: key });
|
|
2297
|
-
}
|
|
2298
|
-
}
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
return relationships;
|
|
2302
|
-
};
|
|
2303
|
-
|
|
2304
|
-
const withRelationships = (doc) => ({ ...doc, relationships: extractRelationships(doc) });
|
|
2305
|
-
|
|
2306
|
-
const resolveUrl = (entity) => {
|
|
2307
|
-
if (entity.url) return entity.url;
|
|
2308
|
-
if (entity.ns && entity.type && entity.id) return 'https://' + entity.ns + '/' + entity.type + '/' + entity.id;
|
|
2309
|
-
if (entity.ns && entity.id) return 'https://' + entity.ns + '/' + entity.id;
|
|
2310
|
-
return null;
|
|
2311
|
-
};
|
|
2312
|
-
`
|
|
2313
|
-
}
|
|
2314
|
-
|
|
2315
|
-
function getContextObjectTemplate(): string {
|
|
2316
|
-
return `
|
|
2317
|
-
// Context object ($) - unified SDK context (aligned with ai-workflows WorkflowContext)
|
|
2318
|
-
const $ = {
|
|
2319
|
-
ns: __SDK_CONFIG__.ns,
|
|
2320
|
-
db,
|
|
2321
|
-
ai,
|
|
2322
|
-
on,
|
|
2323
|
-
every,
|
|
2324
|
-
send,
|
|
2325
|
-
do: __doEvent__,
|
|
2326
|
-
try: __tryEvent__,
|
|
2327
|
-
queue,
|
|
2328
|
-
actor,
|
|
2329
|
-
actions,
|
|
2330
|
-
artifacts,
|
|
2331
|
-
events,
|
|
2332
|
-
decide,
|
|
2333
|
-
track,
|
|
2334
|
-
experiment,
|
|
2335
|
-
delay,
|
|
2336
|
-
state: {},
|
|
2337
|
-
history: __workflow_history__,
|
|
2338
|
-
user: { id: 'test-user', name: 'Test User', role: 'admin' },
|
|
2339
|
-
request: { method: 'GET', path: '/', headers: {}, body: null },
|
|
2340
|
-
env: { NODE_ENV: 'test' },
|
|
2341
|
-
config: {},
|
|
2342
|
-
context: {},
|
|
2343
|
-
meta: {},
|
|
2344
|
-
log: (message, data) => {
|
|
2345
|
-
__workflow_history__.push({ type: 'action', name: 'log', data: { message, data }, timestamp: Date.now() });
|
|
2346
|
-
console.log('[sdk] ' + message, data ?? '');
|
|
2347
|
-
},
|
|
2348
|
-
error: console.error,
|
|
2349
|
-
warn: console.warn,
|
|
2350
|
-
async scope(overrides, fn) {
|
|
2351
|
-
const prev = { ns: $.ns, user: $.user, state: { ...$.state } };
|
|
2352
|
-
Object.assign($, overrides);
|
|
2353
|
-
try { return await fn(); }
|
|
2354
|
-
finally { Object.assign($, prev); }
|
|
2355
|
-
},
|
|
2356
|
-
getHandlers() { return { events: Array.from(__event_handlers__.keys()), schedules: __schedule_handlers__.length }; },
|
|
2357
|
-
clearHandlers() { __event_handlers__.clear(); __schedule_handlers__.length = 0; },
|
|
2358
|
-
getHistory() { return [...__workflow_history__]; },
|
|
2359
|
-
clearHistory() { __workflow_history__.length = 0; }
|
|
2360
|
-
};
|
|
2361
|
-
|
|
2362
|
-
// Standalone exports
|
|
2363
|
-
const api = {};
|
|
2364
|
-
const search = __db_core__.search.bind(__db_core__);
|
|
2365
|
-
`
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
|
-
// Helper to get the remote SDK template
|
|
2369
|
-
function getRemoteSDKTemplate(rpcUrl: string, token: string, ns: string): string {
|
|
2370
|
-
return `
|
|
2371
|
-
// ============================================================
|
|
2372
|
-
// SDK - Thin RPC Proxy ($, db, ai, api, on, send)
|
|
2373
|
-
// ============================================================
|
|
2374
|
-
|
|
2375
|
-
const __SDK_CONFIG__ = {
|
|
2376
|
-
rpcUrl: '${rpcUrl}',
|
|
2377
|
-
token: '${token}',
|
|
2378
|
-
ns: '${ns}'
|
|
2379
|
-
};
|
|
2380
|
-
|
|
2381
|
-
// HTTP RPC client
|
|
2382
|
-
const __rpc__ = {
|
|
2383
|
-
async do(path, ...args) {
|
|
2384
|
-
const headers = { 'Content-Type': 'application/json' };
|
|
2385
|
-
if (__SDK_CONFIG__.token) {
|
|
2386
|
-
headers['Authorization'] = 'Bearer ' + __SDK_CONFIG__.token;
|
|
2387
|
-
}
|
|
2388
|
-
|
|
2389
|
-
const serializedArgs = args.map(arg => {
|
|
2390
|
-
if (typeof arg === 'function') {
|
|
2391
|
-
return { __fn: arg.toString().replace(/"/g, "'") };
|
|
2392
|
-
}
|
|
2393
|
-
return arg;
|
|
2394
|
-
});
|
|
2395
|
-
|
|
2396
|
-
const response = await fetch(__SDK_CONFIG__.rpcUrl + '/rpc', {
|
|
2397
|
-
method: 'POST',
|
|
2398
|
-
headers,
|
|
2399
|
-
body: JSON.stringify({ method: 'do', path, args: serializedArgs })
|
|
2400
|
-
});
|
|
2401
|
-
|
|
2402
|
-
if (!response.ok) {
|
|
2403
|
-
const errorData = await response.json().catch(() => ({}));
|
|
2404
|
-
throw new Error(errorData.error || 'RPC error: ' + response.statusText);
|
|
2405
|
-
}
|
|
2406
|
-
|
|
2407
|
-
return response.json();
|
|
2408
|
-
}
|
|
2409
|
-
};
|
|
2410
|
-
|
|
2411
|
-
const __userDefinitions__ = new Map();
|
|
2412
|
-
|
|
2413
|
-
const __createProxy__ = (path = '') => {
|
|
2414
|
-
const localStore = new Map();
|
|
2415
|
-
|
|
2416
|
-
const proxy = new Proxy(() => {}, {
|
|
2417
|
-
get: (target, prop, receiver) => {
|
|
2418
|
-
if (prop === 'toJSON') {
|
|
2419
|
-
const obj = { __rpcPath: path };
|
|
2420
|
-
for (const [key, value] of localStore) obj[key] = value;
|
|
2421
|
-
for (const [key, value] of __userDefinitions__) {
|
|
2422
|
-
if (key.startsWith(path ? path + '.' : '') && !key.slice(path ? path.length + 1 : 0).includes('.')) {
|
|
2423
|
-
const localKey = key.slice(path ? path.length + 1 : 0);
|
|
2424
|
-
if (localKey) obj[localKey] = value;
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
return () => obj;
|
|
2428
|
-
}
|
|
2429
|
-
if (prop === Symbol.toPrimitive || prop === 'valueOf' || prop === 'toString') {
|
|
2430
|
-
return () => path || '[SDK Proxy]';
|
|
2431
|
-
}
|
|
2432
|
-
if (prop === Symbol.toStringTag) return 'SDKProxy';
|
|
2433
|
-
if (prop === 'then' || prop === 'catch' || prop === 'finally') return undefined;
|
|
2434
|
-
if (prop === 'should') {
|
|
2435
|
-
const obj = {};
|
|
2436
|
-
for (const [key, value] of localStore) obj[key] = value;
|
|
2437
|
-
for (const [key, value] of __userDefinitions__) {
|
|
2438
|
-
if (key.startsWith(path ? path + '.' : '') && !key.slice(path ? path.length + 1 : 0).includes('.')) {
|
|
2439
|
-
const localKey = key.slice(path ? path.length + 1 : 0);
|
|
2440
|
-
if (localKey) obj[localKey] = value;
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
if (Object.keys(obj).length > 0) return __createShouldChain__(obj);
|
|
2444
|
-
return __createShouldChain__(path ? { __path: path } : { __sdk: true, ns: __SDK_CONFIG__.ns });
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
|
-
const fullPath = path ? path + '.' + String(prop) : String(prop);
|
|
2448
|
-
|
|
2449
|
-
if (localStore.has(String(prop))) return localStore.get(String(prop));
|
|
2450
|
-
if (__userDefinitions__.has(fullPath)) return __userDefinitions__.get(fullPath);
|
|
2451
|
-
|
|
2452
|
-
return __createProxy__(fullPath);
|
|
2453
|
-
},
|
|
2454
|
-
|
|
2455
|
-
set: (_, prop, value) => {
|
|
2456
|
-
const fullPath = path ? path + '.' + String(prop) : String(prop);
|
|
2457
|
-
localStore.set(String(prop), value);
|
|
2458
|
-
__userDefinitions__.set(fullPath, value);
|
|
2459
|
-
return true;
|
|
2460
|
-
},
|
|
2461
|
-
|
|
2462
|
-
apply: (_, __, args) => {
|
|
2463
|
-
if (Array.isArray(args[0]) && 'raw' in args[0]) {
|
|
2464
|
-
const strings = args[0];
|
|
2465
|
-
const values = args.slice(1);
|
|
2466
|
-
const text = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '');
|
|
2467
|
-
return __rpc__.do(path, text);
|
|
2468
|
-
}
|
|
2469
|
-
return __rpc__.do(path, ...args);
|
|
2470
|
-
},
|
|
2471
|
-
|
|
2472
|
-
ownKeys: () => {
|
|
2473
|
-
const keys = [];
|
|
2474
|
-
for (const [key, value] of localStore) keys.push(key);
|
|
2475
|
-
for (const [key, value] of __userDefinitions__) {
|
|
2476
|
-
if (key.startsWith(path ? path + '.' : '') && !key.slice(path ? path.length + 1 : 0).includes('.')) {
|
|
2477
|
-
const localKey = key.slice(path ? path.length + 1 : 0);
|
|
2478
|
-
if (localKey && !keys.includes(localKey)) keys.push(localKey);
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
return keys;
|
|
2482
|
-
},
|
|
2483
|
-
|
|
2484
|
-
getOwnPropertyDescriptor: (_, prop) => {
|
|
2485
|
-
const fullPath = path ? path + '.' + String(prop) : String(prop);
|
|
2486
|
-
if (localStore.has(String(prop)) || __userDefinitions__.has(fullPath)) {
|
|
2487
|
-
return { configurable: true, enumerable: true, writable: true };
|
|
2488
|
-
}
|
|
2489
|
-
return undefined;
|
|
2490
|
-
}
|
|
2491
|
-
});
|
|
2492
|
-
|
|
2493
|
-
return proxy;
|
|
2494
|
-
};
|
|
2495
|
-
|
|
2496
|
-
const $ = __createProxy__();
|
|
2497
|
-
const db = $.db;
|
|
2498
|
-
const ai = $.ai;
|
|
2499
|
-
const api = $.api;
|
|
2500
|
-
const on = $.on;
|
|
2501
|
-
const send = $.send;
|
|
2502
|
-
const search = $.search;
|
|
2503
|
-
const track = $.track;
|
|
2504
|
-
const every = $.every;
|
|
2505
|
-
const decide = $.decide;
|
|
2506
|
-
|
|
2507
|
-
$.ns = __SDK_CONFIG__.ns;
|
|
2508
|
-
$.user = { id: 'anonymous', name: 'Anonymous', role: 'guest' };
|
|
2509
|
-
$.request = { method: 'GET', path: '/', headers: {}, body: null };
|
|
2510
|
-
$.env = typeof process !== 'undefined' ? (process.env || {}) : {};
|
|
2511
|
-
$.config = {};
|
|
2512
|
-
$.context = {};
|
|
2513
|
-
$.meta = {};
|
|
2514
|
-
`
|
|
2515
|
-
}
|