@renderify/core 0.1.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/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/core.cjs.js +1583 -0
- package/dist/core.cjs.js.map +1 -0
- package/dist/core.d.mts +335 -0
- package/dist/core.d.ts +335 -0
- package/dist/core.esm.js +1609 -0
- package/dist/core.esm.js.map +1 -0
- package/package.json +61 -0
package/dist/core.cjs.js
ADDED
|
@@ -0,0 +1,1583 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var src_exports = {};
|
|
23
|
+
__export(src_exports, {
|
|
24
|
+
DefaultApiIntegration: () => DefaultApiIntegration,
|
|
25
|
+
DefaultCodeGenerator: () => DefaultCodeGenerator,
|
|
26
|
+
DefaultContextManager: () => DefaultContextManager,
|
|
27
|
+
DefaultCustomizationEngine: () => DefaultCustomizationEngine,
|
|
28
|
+
DefaultPerformanceOptimizer: () => DefaultPerformanceOptimizer,
|
|
29
|
+
DefaultRenderifyConfig: () => DefaultRenderifyConfig,
|
|
30
|
+
DefaultUIRenderer: () => import_runtime.DefaultUIRenderer,
|
|
31
|
+
PolicyRejectionError: () => PolicyRejectionError,
|
|
32
|
+
RenderifyApp: () => RenderifyApp,
|
|
33
|
+
createJspmOnlyStrictModeConfig: () => createJspmOnlyStrictModeConfig,
|
|
34
|
+
createRenderifyApp: () => createRenderifyApp
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(src_exports);
|
|
37
|
+
|
|
38
|
+
// src/api-integration.ts
|
|
39
|
+
var DefaultApiIntegration = class {
|
|
40
|
+
apis = /* @__PURE__ */ new Map();
|
|
41
|
+
registerApi(api) {
|
|
42
|
+
this.apis.set(api.name, api);
|
|
43
|
+
}
|
|
44
|
+
listApis() {
|
|
45
|
+
return [...this.apis.values()];
|
|
46
|
+
}
|
|
47
|
+
async callApi(name, params) {
|
|
48
|
+
const api = this.apis.get(name);
|
|
49
|
+
if (!api) {
|
|
50
|
+
throw new Error(`API not found: ${name}`);
|
|
51
|
+
}
|
|
52
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
53
|
+
const timeout = this.createTimeout(controller, api.timeoutMs ?? 1e4);
|
|
54
|
+
try {
|
|
55
|
+
const url = this.buildUrl(api, params);
|
|
56
|
+
const init = {
|
|
57
|
+
method: api.method,
|
|
58
|
+
headers: {
|
|
59
|
+
"content-type": "application/json",
|
|
60
|
+
...api.headers ?? {}
|
|
61
|
+
},
|
|
62
|
+
signal: controller?.signal
|
|
63
|
+
};
|
|
64
|
+
if (api.method !== "GET" && api.method !== "DELETE" && params) {
|
|
65
|
+
init.body = JSON.stringify(params);
|
|
66
|
+
}
|
|
67
|
+
const response = await fetch(url, init);
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const bodyText = await response.text();
|
|
70
|
+
throw new Error(
|
|
71
|
+
`API ${name} failed with ${response.status}: ${bodyText}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
75
|
+
if (contentType.includes("application/json")) {
|
|
76
|
+
return await response.json();
|
|
77
|
+
}
|
|
78
|
+
return await response.text();
|
|
79
|
+
} finally {
|
|
80
|
+
if (timeout) {
|
|
81
|
+
clearTimeout(timeout);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
buildUrl(api, params) {
|
|
86
|
+
if (!params || api.method !== "GET" && api.method !== "DELETE") {
|
|
87
|
+
return api.endpoint;
|
|
88
|
+
}
|
|
89
|
+
const url = new URL(api.endpoint);
|
|
90
|
+
for (const [key, value] of Object.entries(params)) {
|
|
91
|
+
if (value === void 0 || value === null) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
url.searchParams.set(key, String(value));
|
|
95
|
+
}
|
|
96
|
+
return url.toString();
|
|
97
|
+
}
|
|
98
|
+
createTimeout(controller, timeoutMs) {
|
|
99
|
+
if (!controller || timeoutMs <= 0) {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
return setTimeout(() => controller.abort(), timeoutMs);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// src/codegen.ts
|
|
107
|
+
var import_ir = require("@renderify/ir");
|
|
108
|
+
var DefaultCodeGenerator = class {
|
|
109
|
+
createIncrementalSession(input) {
|
|
110
|
+
let buffer = "";
|
|
111
|
+
let lastSignature = "";
|
|
112
|
+
const tryGenerate = async () => {
|
|
113
|
+
const candidate = await this.tryBuildIncrementalCandidate(
|
|
114
|
+
input.prompt,
|
|
115
|
+
buffer
|
|
116
|
+
);
|
|
117
|
+
if (!candidate) {
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
const signature = this.createIncrementalPlanSignature(candidate.plan);
|
|
121
|
+
if (signature === lastSignature) {
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
lastSignature = signature;
|
|
125
|
+
return candidate;
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
pushDelta: async (delta) => {
|
|
129
|
+
if (delta.length > 0) {
|
|
130
|
+
buffer += delta;
|
|
131
|
+
}
|
|
132
|
+
return await tryGenerate();
|
|
133
|
+
},
|
|
134
|
+
finalize: async (finalText) => {
|
|
135
|
+
if (typeof finalText === "string" && finalText.length > 0) {
|
|
136
|
+
buffer = finalText;
|
|
137
|
+
}
|
|
138
|
+
const candidate = await tryGenerate();
|
|
139
|
+
return candidate?.plan;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async generatePlan(input) {
|
|
144
|
+
const parsedPlan = await this.tryParseRuntimePlan(
|
|
145
|
+
input.llmText,
|
|
146
|
+
input.prompt
|
|
147
|
+
);
|
|
148
|
+
if (parsedPlan) {
|
|
149
|
+
return parsedPlan;
|
|
150
|
+
}
|
|
151
|
+
const source = this.tryExtractRuntimeSource(input.llmText);
|
|
152
|
+
if (source) {
|
|
153
|
+
return await this.createSourcePlan(input.prompt, source);
|
|
154
|
+
}
|
|
155
|
+
const parsedRoot = this.tryParseRuntimeNode(input.llmText);
|
|
156
|
+
const root = parsedRoot ?? this.createFallbackRoot(input.prompt, input.llmText);
|
|
157
|
+
const imports = (0, import_ir.collectComponentModules)(root);
|
|
158
|
+
return this.createPlanFromRoot(root, {
|
|
159
|
+
prompt: input.prompt,
|
|
160
|
+
imports,
|
|
161
|
+
capabilities: {
|
|
162
|
+
domWrite: true,
|
|
163
|
+
allowedModules: imports
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
async validatePlan(plan) {
|
|
168
|
+
if (!plan.id || !Number.isInteger(plan.version) || plan.version <= 0) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (!(0, import_ir.isRuntimeNode)(plan.root)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
return typeof plan.capabilities === "object" && plan.capabilities !== null;
|
|
175
|
+
}
|
|
176
|
+
async transformPlan(plan, transforms) {
|
|
177
|
+
return transforms.reduce((current, transform) => transform(current), plan);
|
|
178
|
+
}
|
|
179
|
+
createFallbackRoot(prompt, llmText) {
|
|
180
|
+
const title = prompt.trim().length > 0 ? prompt.trim() : "Untitled prompt";
|
|
181
|
+
const summary = llmText.trim().length > 0 ? llmText.trim() : "No model output";
|
|
182
|
+
return (0, import_ir.createElementNode)("section", { class: "renderify-runtime-output" }, [
|
|
183
|
+
(0, import_ir.createElementNode)("h1", void 0, [(0, import_ir.createTextNode)(title)]),
|
|
184
|
+
(0, import_ir.createElementNode)("p", void 0, [(0, import_ir.createTextNode)(summary)])
|
|
185
|
+
]);
|
|
186
|
+
}
|
|
187
|
+
async tryBuildIncrementalCandidate(prompt, llmText) {
|
|
188
|
+
const parsedPlan = await this.tryParseRuntimePlan(llmText, prompt);
|
|
189
|
+
if (parsedPlan) {
|
|
190
|
+
return {
|
|
191
|
+
plan: parsedPlan,
|
|
192
|
+
complete: this.isLikelyCompleteJsonPayload(llmText),
|
|
193
|
+
mode: "runtime-plan-json"
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const parsedNode = this.tryParseRuntimeNode(llmText);
|
|
197
|
+
if (parsedNode) {
|
|
198
|
+
return {
|
|
199
|
+
plan: this.createPlanFromRoot(parsedNode, {
|
|
200
|
+
prompt,
|
|
201
|
+
imports: (0, import_ir.collectComponentModules)(parsedNode),
|
|
202
|
+
capabilities: {
|
|
203
|
+
domWrite: true,
|
|
204
|
+
allowedModules: (0, import_ir.collectComponentModules)(parsedNode)
|
|
205
|
+
}
|
|
206
|
+
}),
|
|
207
|
+
complete: this.isLikelyCompleteJsonPayload(llmText),
|
|
208
|
+
mode: "runtime-node-json"
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const source = this.tryExtractRuntimeSource(llmText);
|
|
212
|
+
if (source) {
|
|
213
|
+
return {
|
|
214
|
+
plan: await this.createSourcePlan(prompt, source),
|
|
215
|
+
complete: this.hasClosedCodeFence(llmText),
|
|
216
|
+
mode: "runtime-source"
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
const fallbackRoot = this.createFallbackRoot(prompt, llmText);
|
|
220
|
+
const fallbackImports = (0, import_ir.collectComponentModules)(fallbackRoot);
|
|
221
|
+
return {
|
|
222
|
+
plan: this.createPlanFromRoot(fallbackRoot, {
|
|
223
|
+
prompt,
|
|
224
|
+
imports: fallbackImports,
|
|
225
|
+
capabilities: {
|
|
226
|
+
domWrite: true,
|
|
227
|
+
allowedModules: fallbackImports
|
|
228
|
+
}
|
|
229
|
+
}),
|
|
230
|
+
complete: false,
|
|
231
|
+
mode: "runtime-text-fallback"
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
createIncrementalPlanSignature(plan) {
|
|
235
|
+
return this.hashIncrementalSignatureValue({
|
|
236
|
+
root: plan.root,
|
|
237
|
+
imports: plan.imports ?? [],
|
|
238
|
+
source: plan.source,
|
|
239
|
+
capabilities: plan.capabilities,
|
|
240
|
+
state: plan.state
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
hashIncrementalSignatureValue(value) {
|
|
244
|
+
const hasher = (0, import_ir.createFnv1a64Hasher)();
|
|
245
|
+
const update = (chunk) => {
|
|
246
|
+
hasher.update(chunk);
|
|
247
|
+
};
|
|
248
|
+
const visit = (input) => {
|
|
249
|
+
if (input === null) {
|
|
250
|
+
update("null;");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (input === void 0) {
|
|
254
|
+
update("undefined;");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (typeof input === "string") {
|
|
258
|
+
update(`s:${input};`);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (typeof input === "number") {
|
|
262
|
+
update(Number.isFinite(input) ? `n:${String(input)};` : "n:null;");
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (typeof input === "boolean") {
|
|
266
|
+
update(input ? "b:1;" : "b:0;");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (Array.isArray(input)) {
|
|
270
|
+
update("a:[");
|
|
271
|
+
for (const entry of input) {
|
|
272
|
+
visit(entry);
|
|
273
|
+
}
|
|
274
|
+
update("];");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (typeof input === "object") {
|
|
278
|
+
const entries = Object.entries(input).filter(([, entryValue]) => entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right));
|
|
279
|
+
update("o:{");
|
|
280
|
+
for (const [key, entryValue] of entries) {
|
|
281
|
+
update(`k:${key}=`);
|
|
282
|
+
visit(entryValue);
|
|
283
|
+
}
|
|
284
|
+
update("};");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
update(`x:${String(input)};`);
|
|
288
|
+
};
|
|
289
|
+
visit(value);
|
|
290
|
+
return hasher.digestHex();
|
|
291
|
+
}
|
|
292
|
+
createPlanFromRoot(root, input) {
|
|
293
|
+
const imports = this.normalizeImports(input.imports) ?? (0, import_ir.collectComponentModules)(root);
|
|
294
|
+
const source = this.normalizeSourceModule(input.source);
|
|
295
|
+
const moduleManifest = this.normalizeModuleManifest(input.moduleManifest) ?? this.createModuleManifestFromImports(imports);
|
|
296
|
+
const capabilities = this.normalizeCapabilities(
|
|
297
|
+
input.capabilities,
|
|
298
|
+
imports
|
|
299
|
+
);
|
|
300
|
+
const metadata = this.normalizeMetadata(input.prompt, input.metadata);
|
|
301
|
+
const id = this.normalizePlanId(input.id);
|
|
302
|
+
const version = this.normalizePlanVersion(input.version);
|
|
303
|
+
return {
|
|
304
|
+
specVersion: (0, import_ir.resolveRuntimePlanSpecVersion)(
|
|
305
|
+
input.specVersion ?? import_ir.DEFAULT_RUNTIME_PLAN_SPEC_VERSION
|
|
306
|
+
),
|
|
307
|
+
id,
|
|
308
|
+
version,
|
|
309
|
+
root,
|
|
310
|
+
imports,
|
|
311
|
+
...moduleManifest ? { moduleManifest } : {},
|
|
312
|
+
capabilities,
|
|
313
|
+
...metadata ? { metadata } : {},
|
|
314
|
+
...input.state ? { state: input.state } : {},
|
|
315
|
+
...source ? { source } : {}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
async tryParseRuntimePlan(text, prompt) {
|
|
319
|
+
const parsed = this.tryParseJsonPayload(text);
|
|
320
|
+
if (!this.isRecord(parsed) || !("root" in parsed)) {
|
|
321
|
+
return void 0;
|
|
322
|
+
}
|
|
323
|
+
const root = parsed.root;
|
|
324
|
+
if (!(0, import_ir.isRuntimeNode)(root)) {
|
|
325
|
+
return void 0;
|
|
326
|
+
}
|
|
327
|
+
const metadata = this.isRecord(parsed.metadata) ? (0, import_ir.isRuntimePlanMetadata)(parsed.metadata) ? parsed.metadata : void 0 : void 0;
|
|
328
|
+
const moduleManifest = this.isRecord(parsed.moduleManifest) && (0, import_ir.isRuntimeModuleManifest)(parsed.moduleManifest) ? parsed.moduleManifest : void 0;
|
|
329
|
+
const state = this.isRecord(parsed.state) && (0, import_ir.isRuntimeStateModel)(parsed.state) ? parsed.state : void 0;
|
|
330
|
+
const source = this.isRecord(parsed.source) && (0, import_ir.isRuntimeSourceModule)(parsed.source) ? parsed.source : void 0;
|
|
331
|
+
const importsFromPayload = Array.isArray(parsed.imports) ? parsed.imports.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0) : void 0;
|
|
332
|
+
const importsFromManifest = moduleManifest ? Object.keys(moduleManifest) : void 0;
|
|
333
|
+
const imports = importsFromPayload ?? importsFromManifest ?? (source ? await this.parseImportsFromSource(source.code) : void 0);
|
|
334
|
+
return this.createPlanFromRoot(root, {
|
|
335
|
+
prompt,
|
|
336
|
+
specVersion: typeof parsed.specVersion === "string" ? parsed.specVersion : void 0,
|
|
337
|
+
id: typeof parsed.id === "string" ? parsed.id : void 0,
|
|
338
|
+
version: typeof parsed.version === "number" ? parsed.version : void 0,
|
|
339
|
+
imports,
|
|
340
|
+
moduleManifest,
|
|
341
|
+
capabilities: this.isRecord(parsed.capabilities) && (0, import_ir.isRuntimeCapabilities)(parsed.capabilities) ? parsed.capabilities : void 0,
|
|
342
|
+
metadata,
|
|
343
|
+
state,
|
|
344
|
+
source
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
tryParseRuntimeNode(text) {
|
|
348
|
+
const parsed = this.tryParseJsonPayload(text);
|
|
349
|
+
if ((0, import_ir.isRuntimeNode)(parsed)) {
|
|
350
|
+
return parsed;
|
|
351
|
+
}
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
tryParseJsonPayload(text) {
|
|
355
|
+
const codeBlockMatch = text.match(/```json\s*([\s\S]*?)\s*```/i);
|
|
356
|
+
const payload = codeBlockMatch ? codeBlockMatch[1] : text;
|
|
357
|
+
const trimmed = payload.trim();
|
|
358
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
return JSON.parse(trimmed);
|
|
363
|
+
} catch {
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
isLikelyCompleteJsonPayload(text) {
|
|
368
|
+
const codeBlockMatch = text.match(/```json\s*([\s\S]*?)\s*```/i);
|
|
369
|
+
const payload = codeBlockMatch ? codeBlockMatch[1] : text;
|
|
370
|
+
const trimmed = payload.trim();
|
|
371
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
const stack = [];
|
|
375
|
+
let inString = false;
|
|
376
|
+
let escaped = false;
|
|
377
|
+
for (const char of trimmed) {
|
|
378
|
+
if (inString) {
|
|
379
|
+
if (escaped) {
|
|
380
|
+
escaped = false;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (char === "\\") {
|
|
384
|
+
escaped = true;
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
if (char === '"') {
|
|
388
|
+
inString = false;
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (char === '"') {
|
|
393
|
+
inString = true;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (char === "{" || char === "[") {
|
|
397
|
+
stack.push(char);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (char === "}" || char === "]") {
|
|
401
|
+
const top = stack.pop();
|
|
402
|
+
if (!top) {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
if (char === "}" && top !== "{" || char === "]" && top !== "[") {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return !inString && stack.length === 0;
|
|
411
|
+
}
|
|
412
|
+
hasClosedCodeFence(text) {
|
|
413
|
+
const fences = text.match(/```/g);
|
|
414
|
+
return Boolean(fences && fences.length >= 2 && fences.length % 2 === 0);
|
|
415
|
+
}
|
|
416
|
+
normalizePlanId(id) {
|
|
417
|
+
if (typeof id === "string" && id.trim().length > 0) {
|
|
418
|
+
return id.trim();
|
|
419
|
+
}
|
|
420
|
+
return `plan_${Date.now().toString(36)}`;
|
|
421
|
+
}
|
|
422
|
+
normalizePlanVersion(version) {
|
|
423
|
+
if (Number.isInteger(version) && version > 0) {
|
|
424
|
+
return version;
|
|
425
|
+
}
|
|
426
|
+
return 1;
|
|
427
|
+
}
|
|
428
|
+
normalizeImports(imports) {
|
|
429
|
+
if (!imports || imports.length === 0) {
|
|
430
|
+
return void 0;
|
|
431
|
+
}
|
|
432
|
+
return [...new Set(imports)];
|
|
433
|
+
}
|
|
434
|
+
normalizeModuleManifest(manifest) {
|
|
435
|
+
if (!manifest || Object.keys(manifest).length === 0) {
|
|
436
|
+
return void 0;
|
|
437
|
+
}
|
|
438
|
+
return manifest;
|
|
439
|
+
}
|
|
440
|
+
normalizeCapabilities(capabilities, imports) {
|
|
441
|
+
const normalized = {
|
|
442
|
+
domWrite: true,
|
|
443
|
+
allowedModules: imports,
|
|
444
|
+
...capabilities ?? {}
|
|
445
|
+
};
|
|
446
|
+
if (!Array.isArray(normalized.allowedModules)) {
|
|
447
|
+
normalized.allowedModules = imports;
|
|
448
|
+
}
|
|
449
|
+
if (typeof normalized.maxImports === "number" && (!Number.isFinite(normalized.maxImports) || normalized.maxImports < 0)) {
|
|
450
|
+
delete normalized.maxImports;
|
|
451
|
+
}
|
|
452
|
+
if (typeof normalized.maxExecutionMs === "number" && (!Number.isFinite(normalized.maxExecutionMs) || normalized.maxExecutionMs < 1)) {
|
|
453
|
+
delete normalized.maxExecutionMs;
|
|
454
|
+
}
|
|
455
|
+
if (typeof normalized.maxComponentInvocations === "number" && (!Number.isFinite(normalized.maxComponentInvocations) || normalized.maxComponentInvocations < 0)) {
|
|
456
|
+
delete normalized.maxComponentInvocations;
|
|
457
|
+
}
|
|
458
|
+
return normalized;
|
|
459
|
+
}
|
|
460
|
+
createModuleManifestFromImports(imports) {
|
|
461
|
+
if (imports.length === 0) {
|
|
462
|
+
return void 0;
|
|
463
|
+
}
|
|
464
|
+
const manifest = {};
|
|
465
|
+
for (const specifier of imports) {
|
|
466
|
+
const resolvedUrl = this.resolveImportToUrl(specifier);
|
|
467
|
+
const version = this.extractVersionFromSpecifier(specifier);
|
|
468
|
+
manifest[specifier] = {
|
|
469
|
+
resolvedUrl,
|
|
470
|
+
...version ? { version } : {},
|
|
471
|
+
signer: "renderify-codegen"
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
return manifest;
|
|
475
|
+
}
|
|
476
|
+
resolveImportToUrl(specifier) {
|
|
477
|
+
if (this.isHttpUrl(specifier)) {
|
|
478
|
+
return specifier;
|
|
479
|
+
}
|
|
480
|
+
if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
|
|
481
|
+
return specifier;
|
|
482
|
+
}
|
|
483
|
+
if (specifier.startsWith("npm:")) {
|
|
484
|
+
return this.resolveJspmSpecifier(specifier.slice(4));
|
|
485
|
+
}
|
|
486
|
+
return this.resolveJspmSpecifier(specifier);
|
|
487
|
+
}
|
|
488
|
+
extractVersionFromSpecifier(specifier) {
|
|
489
|
+
if (!specifier.startsWith("npm:")) {
|
|
490
|
+
return void 0;
|
|
491
|
+
}
|
|
492
|
+
const body = specifier.slice(4);
|
|
493
|
+
const atIndex = body.lastIndexOf("@");
|
|
494
|
+
if (atIndex <= 0) {
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
const version = body.slice(atIndex + 1).trim();
|
|
498
|
+
if (version.length === 0 || version.includes("/")) {
|
|
499
|
+
return void 0;
|
|
500
|
+
}
|
|
501
|
+
return version;
|
|
502
|
+
}
|
|
503
|
+
normalizeMetadata(prompt, metadata) {
|
|
504
|
+
const sourcePrompt = prompt.trim().length > 0 ? prompt.trim() : void 0;
|
|
505
|
+
const merged = {
|
|
506
|
+
...metadata ?? {},
|
|
507
|
+
...sourcePrompt ? { sourcePrompt } : {}
|
|
508
|
+
};
|
|
509
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
510
|
+
}
|
|
511
|
+
tryExtractRuntimeSource(text) {
|
|
512
|
+
const match = text.match(/```(tsx|jsx|ts|js)\s*([\s\S]*?)\s*```/i);
|
|
513
|
+
if (!match) {
|
|
514
|
+
return void 0;
|
|
515
|
+
}
|
|
516
|
+
const language = match[1].toLowerCase();
|
|
517
|
+
const code = match[2].trim();
|
|
518
|
+
if (code.length === 0) {
|
|
519
|
+
return void 0;
|
|
520
|
+
}
|
|
521
|
+
return this.normalizeSourceModule({
|
|
522
|
+
language,
|
|
523
|
+
code,
|
|
524
|
+
exportName: "default"
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
async createSourcePlan(prompt, source) {
|
|
528
|
+
const imports = await this.parseImportsFromSource(source.code);
|
|
529
|
+
const normalizedSource = this.normalizeSourceModule(source);
|
|
530
|
+
const sourceRuntime = normalizedSource?.runtime ?? "renderify";
|
|
531
|
+
return this.createPlanFromRoot(
|
|
532
|
+
(0, import_ir.createElementNode)("section", { class: "renderify-runtime-source-plan" }, [
|
|
533
|
+
(0, import_ir.createElementNode)("h2", void 0, [(0, import_ir.createTextNode)(prompt)]),
|
|
534
|
+
(0, import_ir.createElementNode)("p", void 0, [
|
|
535
|
+
(0, import_ir.createTextNode)(`Runtime source module (${source.language}) prepared`)
|
|
536
|
+
])
|
|
537
|
+
]),
|
|
538
|
+
{
|
|
539
|
+
prompt,
|
|
540
|
+
imports,
|
|
541
|
+
capabilities: {
|
|
542
|
+
domWrite: true,
|
|
543
|
+
allowedModules: imports
|
|
544
|
+
},
|
|
545
|
+
metadata: {
|
|
546
|
+
sourcePrompt: prompt,
|
|
547
|
+
tags: ["source-module", source.language, `runtime:${sourceRuntime}`]
|
|
548
|
+
},
|
|
549
|
+
source: normalizedSource
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
async parseImportsFromSource(code) {
|
|
554
|
+
return (0, import_ir.collectRuntimeSourceImports)(code);
|
|
555
|
+
}
|
|
556
|
+
isHttpUrl(value) {
|
|
557
|
+
try {
|
|
558
|
+
const parsed = new URL(value);
|
|
559
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
560
|
+
} catch {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
isRecord(value) {
|
|
565
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
566
|
+
}
|
|
567
|
+
normalizeSourceModule(source) {
|
|
568
|
+
if (!source) {
|
|
569
|
+
return void 0;
|
|
570
|
+
}
|
|
571
|
+
const runtime = source.runtime ?? this.inferSourceRuntimeFromLanguage(source.language, source.code);
|
|
572
|
+
return {
|
|
573
|
+
...source,
|
|
574
|
+
runtime
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
inferSourceRuntimeFromLanguage(language, code) {
|
|
578
|
+
if (language === "jsx" || language === "tsx") {
|
|
579
|
+
return "preact";
|
|
580
|
+
}
|
|
581
|
+
if ((language === "js" || language === "ts") && /from\s+["'](?:preact|react|recharts)["']/.test(code)) {
|
|
582
|
+
return "preact";
|
|
583
|
+
}
|
|
584
|
+
return "renderify";
|
|
585
|
+
}
|
|
586
|
+
resolveJspmSpecifier(specifier) {
|
|
587
|
+
const normalized = specifier.trim();
|
|
588
|
+
if (normalized.length === 0) {
|
|
589
|
+
return "https://ga.jspm.io/npm:missing";
|
|
590
|
+
}
|
|
591
|
+
const override = import_ir.DEFAULT_JSPM_SPECIFIER_OVERRIDES[normalized];
|
|
592
|
+
if (override) {
|
|
593
|
+
return override;
|
|
594
|
+
}
|
|
595
|
+
return `https://ga.jspm.io/npm:${normalized}`;
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// src/config.ts
|
|
600
|
+
var DEFAULT_RUNTIME_SPEC_VERSIONS = ["runtime-plan/v1"];
|
|
601
|
+
var DEFAULT_RUNTIME_REMOTE_FALLBACK_CDNS = ["https://esm.sh"];
|
|
602
|
+
var DEFAULT_JSPM_ALLOWED_NETWORK_HOSTS = ["ga.jspm.io", "cdn.jspm.io"];
|
|
603
|
+
function createJspmOnlyStrictModeConfig(options = {}) {
|
|
604
|
+
const allowedNetworkHosts = normalizeJspmAllowedNetworkHosts(
|
|
605
|
+
options.allowedNetworkHosts
|
|
606
|
+
);
|
|
607
|
+
return {
|
|
608
|
+
runtimeJspmOnlyStrictMode: true,
|
|
609
|
+
strictSecurity: true,
|
|
610
|
+
securityProfile: "strict",
|
|
611
|
+
runtimeEnforceModuleManifest: true,
|
|
612
|
+
runtimeAllowIsolationFallback: false,
|
|
613
|
+
runtimeEnableDependencyPreflight: true,
|
|
614
|
+
runtimeFailOnDependencyPreflightError: true,
|
|
615
|
+
runtimeRemoteFallbackCdnBases: [],
|
|
616
|
+
securityPolicy: {
|
|
617
|
+
allowArbitraryNetwork: false,
|
|
618
|
+
allowedNetworkHosts,
|
|
619
|
+
requireModuleManifestForBareSpecifiers: true,
|
|
620
|
+
requireModuleIntegrity: true,
|
|
621
|
+
allowDynamicSourceImports: false
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
var DefaultRenderifyConfig = class {
|
|
626
|
+
config = {};
|
|
627
|
+
async load(overrides) {
|
|
628
|
+
const env = getEnvironmentValues();
|
|
629
|
+
const defaultValues = {
|
|
630
|
+
llmProvider: "openai",
|
|
631
|
+
llmModel: "gpt-4.1-mini",
|
|
632
|
+
llmBaseUrl: "https://api.openai.com/v1",
|
|
633
|
+
llmRequestTimeoutMs: 3e4,
|
|
634
|
+
jspmCdnUrl: "https://ga.jspm.io/npm",
|
|
635
|
+
strictSecurity: true,
|
|
636
|
+
llmUseStructuredOutput: true,
|
|
637
|
+
securityProfile: "balanced",
|
|
638
|
+
runtimeJspmOnlyStrictMode: false,
|
|
639
|
+
runtimeEnforceModuleManifest: true,
|
|
640
|
+
runtimeAllowIsolationFallback: false,
|
|
641
|
+
runtimeSupportedSpecVersions: [...DEFAULT_RUNTIME_SPEC_VERSIONS],
|
|
642
|
+
runtimeEnableDependencyPreflight: true,
|
|
643
|
+
runtimeFailOnDependencyPreflightError: false,
|
|
644
|
+
runtimeRemoteFetchTimeoutMs: 12e3,
|
|
645
|
+
runtimeRemoteFetchRetries: 2,
|
|
646
|
+
runtimeRemoteFetchBackoffMs: 150,
|
|
647
|
+
runtimeRemoteFallbackCdnBases: [...DEFAULT_RUNTIME_REMOTE_FALLBACK_CDNS],
|
|
648
|
+
runtimeBrowserSourceSandboxMode: "worker",
|
|
649
|
+
runtimeBrowserSourceSandboxTimeoutMs: 4e3,
|
|
650
|
+
runtimeBrowserSourceSandboxFailClosed: true
|
|
651
|
+
};
|
|
652
|
+
const merged = {
|
|
653
|
+
...defaultValues,
|
|
654
|
+
...env,
|
|
655
|
+
...overrides ?? {}
|
|
656
|
+
};
|
|
657
|
+
this.config = applyDerivedConfig(merged);
|
|
658
|
+
}
|
|
659
|
+
get(key) {
|
|
660
|
+
return this.config[key];
|
|
661
|
+
}
|
|
662
|
+
set(key, value) {
|
|
663
|
+
this.config[key] = value;
|
|
664
|
+
}
|
|
665
|
+
snapshot() {
|
|
666
|
+
return { ...this.config };
|
|
667
|
+
}
|
|
668
|
+
async save() {
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
function applyDerivedConfig(input) {
|
|
672
|
+
if (input.runtimeJspmOnlyStrictMode !== true) {
|
|
673
|
+
return input;
|
|
674
|
+
}
|
|
675
|
+
const existingPolicy = toSecurityPolicyOverrides(input.securityPolicy);
|
|
676
|
+
const strictPreset = createJspmOnlyStrictModeConfig({
|
|
677
|
+
allowedNetworkHosts: normalizeJspmAllowedNetworkHosts(
|
|
678
|
+
existingPolicy.allowedNetworkHosts
|
|
679
|
+
)
|
|
680
|
+
});
|
|
681
|
+
return {
|
|
682
|
+
...input,
|
|
683
|
+
...strictPreset,
|
|
684
|
+
securityPolicy: {
|
|
685
|
+
...existingPolicy,
|
|
686
|
+
...toSecurityPolicyOverrides(strictPreset.securityPolicy)
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
function getEnvironmentValues() {
|
|
691
|
+
if (typeof process === "undefined" || typeof process.env === "undefined" || process.env === null) {
|
|
692
|
+
return {};
|
|
693
|
+
}
|
|
694
|
+
const values = {
|
|
695
|
+
llmUseStructuredOutput: process.env.RENDERIFY_LLM_USE_STRUCTURED_OUTPUT !== "false",
|
|
696
|
+
strictSecurity: process.env.RENDERIFY_STRICT_SECURITY !== "false",
|
|
697
|
+
securityProfile: parseSecurityProfile(
|
|
698
|
+
process.env.RENDERIFY_SECURITY_PROFILE
|
|
699
|
+
),
|
|
700
|
+
runtimeJspmOnlyStrictMode: process.env.RENDERIFY_RUNTIME_JSPM_ONLY_STRICT_MODE === "true",
|
|
701
|
+
runtimeEnforceModuleManifest: process.env.RENDERIFY_RUNTIME_ENFORCE_MANIFEST !== "false",
|
|
702
|
+
runtimeAllowIsolationFallback: process.env.RENDERIFY_RUNTIME_ALLOW_ISOLATION_FALLBACK === "true",
|
|
703
|
+
runtimeSupportedSpecVersions: parseSpecVersions(
|
|
704
|
+
process.env.RENDERIFY_RUNTIME_SPEC_VERSIONS
|
|
705
|
+
),
|
|
706
|
+
runtimeEnableDependencyPreflight: process.env.RENDERIFY_RUNTIME_PREFLIGHT !== "false",
|
|
707
|
+
runtimeFailOnDependencyPreflightError: process.env.RENDERIFY_RUNTIME_PREFLIGHT_FAIL_FAST === "true",
|
|
708
|
+
runtimeRemoteFetchTimeoutMs: parsePositiveInt(process.env.RENDERIFY_RUNTIME_REMOTE_FETCH_TIMEOUT_MS) ?? 12e3,
|
|
709
|
+
runtimeRemoteFetchRetries: parseNonNegativeInt(process.env.RENDERIFY_RUNTIME_REMOTE_FETCH_RETRIES) ?? 2,
|
|
710
|
+
runtimeRemoteFetchBackoffMs: parseNonNegativeInt(
|
|
711
|
+
process.env.RENDERIFY_RUNTIME_REMOTE_FETCH_BACKOFF_MS
|
|
712
|
+
) ?? 150,
|
|
713
|
+
runtimeRemoteFallbackCdnBases: parseCsvValues(
|
|
714
|
+
process.env.RENDERIFY_RUNTIME_REMOTE_FALLBACK_CDNS,
|
|
715
|
+
DEFAULT_RUNTIME_REMOTE_FALLBACK_CDNS
|
|
716
|
+
),
|
|
717
|
+
runtimeBrowserSourceSandboxMode: parseSourceSandboxMode(
|
|
718
|
+
process.env.RENDERIFY_RUNTIME_BROWSER_SANDBOX_MODE
|
|
719
|
+
),
|
|
720
|
+
runtimeBrowserSourceSandboxFailClosed: process.env.RENDERIFY_RUNTIME_BROWSER_SANDBOX_FAIL_CLOSED !== "false"
|
|
721
|
+
};
|
|
722
|
+
if (process.env.RENDERIFY_LLM_API_KEY) {
|
|
723
|
+
values.llmApiKey = process.env.RENDERIFY_LLM_API_KEY;
|
|
724
|
+
}
|
|
725
|
+
if (process.env.RENDERIFY_LLM_PROVIDER) {
|
|
726
|
+
values.llmProvider = parseLlmProvider(process.env.RENDERIFY_LLM_PROVIDER);
|
|
727
|
+
}
|
|
728
|
+
if (process.env.RENDERIFY_LLM_MODEL) {
|
|
729
|
+
values.llmModel = process.env.RENDERIFY_LLM_MODEL;
|
|
730
|
+
}
|
|
731
|
+
if (process.env.RENDERIFY_LLM_BASE_URL) {
|
|
732
|
+
values.llmBaseUrl = process.env.RENDERIFY_LLM_BASE_URL;
|
|
733
|
+
}
|
|
734
|
+
const timeoutMs = parsePositiveInt(process.env.RENDERIFY_LLM_TIMEOUT_MS);
|
|
735
|
+
if (timeoutMs !== void 0) {
|
|
736
|
+
values.llmRequestTimeoutMs = timeoutMs;
|
|
737
|
+
}
|
|
738
|
+
if (process.env.RENDERIFY_JSPM_CDN_URL) {
|
|
739
|
+
values.jspmCdnUrl = process.env.RENDERIFY_JSPM_CDN_URL;
|
|
740
|
+
}
|
|
741
|
+
const browserSandboxTimeout = parsePositiveInt(
|
|
742
|
+
process.env.RENDERIFY_RUNTIME_BROWSER_SANDBOX_TIMEOUT_MS
|
|
743
|
+
);
|
|
744
|
+
if (browserSandboxTimeout !== void 0) {
|
|
745
|
+
values.runtimeBrowserSourceSandboxTimeoutMs = browserSandboxTimeout;
|
|
746
|
+
}
|
|
747
|
+
return values;
|
|
748
|
+
}
|
|
749
|
+
function parsePositiveInt(value) {
|
|
750
|
+
if (!value) {
|
|
751
|
+
return void 0;
|
|
752
|
+
}
|
|
753
|
+
const parsed = Number(value);
|
|
754
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
755
|
+
return void 0;
|
|
756
|
+
}
|
|
757
|
+
return parsed;
|
|
758
|
+
}
|
|
759
|
+
function parseNonNegativeInt(value) {
|
|
760
|
+
if (!value) {
|
|
761
|
+
return void 0;
|
|
762
|
+
}
|
|
763
|
+
const parsed = Number(value);
|
|
764
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
765
|
+
return void 0;
|
|
766
|
+
}
|
|
767
|
+
return parsed;
|
|
768
|
+
}
|
|
769
|
+
function parseSecurityProfile(value) {
|
|
770
|
+
if (value === "strict" || value === "balanced" || value === "relaxed") {
|
|
771
|
+
return value;
|
|
772
|
+
}
|
|
773
|
+
return "balanced";
|
|
774
|
+
}
|
|
775
|
+
function parseLlmProvider(value) {
|
|
776
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
777
|
+
return value.trim().toLowerCase();
|
|
778
|
+
}
|
|
779
|
+
return "openai";
|
|
780
|
+
}
|
|
781
|
+
function parseSpecVersions(value) {
|
|
782
|
+
if (!value || value.trim().length === 0) {
|
|
783
|
+
return [...DEFAULT_RUNTIME_SPEC_VERSIONS];
|
|
784
|
+
}
|
|
785
|
+
const parsed = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
786
|
+
return parsed.length > 0 ? parsed : [...DEFAULT_RUNTIME_SPEC_VERSIONS];
|
|
787
|
+
}
|
|
788
|
+
function parseCsvValues(value, fallback) {
|
|
789
|
+
if (!value || value.trim().length === 0) {
|
|
790
|
+
return [...fallback];
|
|
791
|
+
}
|
|
792
|
+
const parsed = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
793
|
+
return parsed.length > 0 ? parsed : [...fallback];
|
|
794
|
+
}
|
|
795
|
+
function parseSourceSandboxMode(value) {
|
|
796
|
+
if (value === "none" || value === "worker" || value === "iframe") {
|
|
797
|
+
return value;
|
|
798
|
+
}
|
|
799
|
+
return "worker";
|
|
800
|
+
}
|
|
801
|
+
function toSecurityPolicyOverrides(value) {
|
|
802
|
+
if (!isRecord(value)) {
|
|
803
|
+
return {};
|
|
804
|
+
}
|
|
805
|
+
return value;
|
|
806
|
+
}
|
|
807
|
+
function normalizeJspmAllowedNetworkHosts(input) {
|
|
808
|
+
const values = Array.isArray(input) ? input : DEFAULT_JSPM_ALLOWED_NETWORK_HOSTS;
|
|
809
|
+
const normalized = [];
|
|
810
|
+
for (const entry of values) {
|
|
811
|
+
if (typeof entry !== "string") {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const host = normalizeNetworkHostEntry(entry);
|
|
815
|
+
if (!host || !host.endsWith("jspm.io")) {
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
if (!normalized.includes(host)) {
|
|
819
|
+
normalized.push(host);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return normalized.length > 0 ? normalized : [...DEFAULT_JSPM_ALLOWED_NETWORK_HOSTS];
|
|
823
|
+
}
|
|
824
|
+
function normalizeNetworkHostEntry(value) {
|
|
825
|
+
const trimmed = value.trim().toLowerCase();
|
|
826
|
+
if (trimmed.length === 0) {
|
|
827
|
+
return void 0;
|
|
828
|
+
}
|
|
829
|
+
try {
|
|
830
|
+
if (trimmed.includes("://")) {
|
|
831
|
+
const parsed = new URL(trimmed);
|
|
832
|
+
return parsed.host;
|
|
833
|
+
}
|
|
834
|
+
} catch {
|
|
835
|
+
}
|
|
836
|
+
const hostLike = trimmed.replace(/^https?:\/\//, "").replace(/^\/+/, "").replace(/\/.*$/, "");
|
|
837
|
+
return hostLike.length > 0 ? hostLike : void 0;
|
|
838
|
+
}
|
|
839
|
+
function isRecord(value) {
|
|
840
|
+
return typeof value === "object" && value !== null;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/context.ts
|
|
844
|
+
var DefaultContextManager = class {
|
|
845
|
+
ctx = {};
|
|
846
|
+
listeners = /* @__PURE__ */ new Set();
|
|
847
|
+
async initialize() {
|
|
848
|
+
this.ctx = {
|
|
849
|
+
user: { id: "anonymous" },
|
|
850
|
+
app: { version: "0.1.0", environment: "development" }
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
getContext() {
|
|
854
|
+
return this.ctx;
|
|
855
|
+
}
|
|
856
|
+
updateContext(partialCtx) {
|
|
857
|
+
const nextUser = partialCtx.user || this.ctx.user ? {
|
|
858
|
+
id: partialCtx.user?.id ?? this.ctx.user?.id ?? "anonymous",
|
|
859
|
+
name: partialCtx.user?.name ?? this.ctx.user?.name,
|
|
860
|
+
role: partialCtx.user?.role ?? this.ctx.user?.role
|
|
861
|
+
} : void 0;
|
|
862
|
+
const nextApp = partialCtx.app || this.ctx.app ? {
|
|
863
|
+
version: partialCtx.app?.version ?? this.ctx.app?.version ?? "0.1.0",
|
|
864
|
+
environment: partialCtx.app?.environment ?? this.ctx.app?.environment
|
|
865
|
+
} : void 0;
|
|
866
|
+
this.ctx = {
|
|
867
|
+
...this.ctx,
|
|
868
|
+
...partialCtx,
|
|
869
|
+
user: nextUser,
|
|
870
|
+
app: nextApp
|
|
871
|
+
};
|
|
872
|
+
this.notify();
|
|
873
|
+
}
|
|
874
|
+
subscribe(listener) {
|
|
875
|
+
this.listeners.add(listener);
|
|
876
|
+
return () => {
|
|
877
|
+
this.listeners.delete(listener);
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
notify() {
|
|
881
|
+
for (const cb of this.listeners) {
|
|
882
|
+
cb(this.ctx);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
// src/customization.ts
|
|
888
|
+
var DefaultCustomizationEngine = class {
|
|
889
|
+
plugins = [];
|
|
890
|
+
registerPlugin(plugin) {
|
|
891
|
+
this.plugins.push(plugin);
|
|
892
|
+
}
|
|
893
|
+
getPlugins() {
|
|
894
|
+
return [...this.plugins];
|
|
895
|
+
}
|
|
896
|
+
async runHook(hookName, payload, context) {
|
|
897
|
+
let currentPayload = payload;
|
|
898
|
+
for (const plugin of this.plugins) {
|
|
899
|
+
const hookFn = plugin.hooks[hookName];
|
|
900
|
+
if (!hookFn) {
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
currentPayload = await hookFn(currentPayload, context);
|
|
904
|
+
}
|
|
905
|
+
return currentPayload;
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
|
|
909
|
+
// src/performance.ts
|
|
910
|
+
var DefaultPerformanceOptimizer = class {
|
|
911
|
+
timers = /* @__PURE__ */ new Map();
|
|
912
|
+
metrics = [];
|
|
913
|
+
startMeasurement(label) {
|
|
914
|
+
this.timers.set(label, {
|
|
915
|
+
label,
|
|
916
|
+
startedAt: nowMs()
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
endMeasurement(label) {
|
|
920
|
+
const timer = this.timers.get(label);
|
|
921
|
+
if (!timer) {
|
|
922
|
+
return void 0;
|
|
923
|
+
}
|
|
924
|
+
const endedAt = nowMs();
|
|
925
|
+
const metric = {
|
|
926
|
+
label,
|
|
927
|
+
startedAt: timer.startedAt,
|
|
928
|
+
endedAt,
|
|
929
|
+
durationMs: endedAt - timer.startedAt
|
|
930
|
+
};
|
|
931
|
+
this.metrics.push(metric);
|
|
932
|
+
this.timers.delete(label);
|
|
933
|
+
return metric;
|
|
934
|
+
}
|
|
935
|
+
getMetrics() {
|
|
936
|
+
return [...this.metrics];
|
|
937
|
+
}
|
|
938
|
+
reset() {
|
|
939
|
+
this.timers.clear();
|
|
940
|
+
this.metrics.length = 0;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
function nowMs() {
|
|
944
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
945
|
+
return performance.now();
|
|
946
|
+
}
|
|
947
|
+
return Date.now();
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// src/security.ts
|
|
951
|
+
var security_exports = {};
|
|
952
|
+
__reExport(security_exports, require("@renderify/security"));
|
|
953
|
+
|
|
954
|
+
// src/index.ts
|
|
955
|
+
__reExport(src_exports, security_exports, module.exports);
|
|
956
|
+
|
|
957
|
+
// src/ui.ts
|
|
958
|
+
var import_runtime = require("@renderify/runtime");
|
|
959
|
+
|
|
960
|
+
// src/index.ts
|
|
961
|
+
var PolicyRejectionError = class extends Error {
|
|
962
|
+
result;
|
|
963
|
+
constructor(result) {
|
|
964
|
+
super(`Security policy rejected runtime plan: ${result.issues.join("; ")}`);
|
|
965
|
+
this.name = "PolicyRejectionError";
|
|
966
|
+
this.result = result;
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
var RenderifyApp = class {
|
|
970
|
+
deps;
|
|
971
|
+
listeners = /* @__PURE__ */ new Map();
|
|
972
|
+
running = false;
|
|
973
|
+
constructor(deps) {
|
|
974
|
+
this.deps = deps;
|
|
975
|
+
}
|
|
976
|
+
async start() {
|
|
977
|
+
if (this.running) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
await this.deps.config.load();
|
|
981
|
+
this.deps.llm.configure(this.deps.config.snapshot());
|
|
982
|
+
await this.deps.context.initialize();
|
|
983
|
+
const policyOverrides = this.deps.config.get("securityPolicy");
|
|
984
|
+
const securityProfile = this.deps.config.get("securityProfile");
|
|
985
|
+
this.deps.security.initialize({
|
|
986
|
+
profile: securityProfile,
|
|
987
|
+
overrides: policyOverrides
|
|
988
|
+
});
|
|
989
|
+
await this.deps.runtime.initialize();
|
|
990
|
+
this.running = true;
|
|
991
|
+
this.emit("started");
|
|
992
|
+
}
|
|
993
|
+
async stop() {
|
|
994
|
+
if (!this.running) {
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
await this.deps.runtime.terminate();
|
|
998
|
+
this.running = false;
|
|
999
|
+
this.emit("stopped");
|
|
1000
|
+
}
|
|
1001
|
+
async renderPrompt(prompt, options = {}) {
|
|
1002
|
+
this.ensureRunning();
|
|
1003
|
+
this.throwIfAborted(options.signal);
|
|
1004
|
+
const traceId = options.traceId ?? this.createTraceId();
|
|
1005
|
+
const metricLabel = this.createMetricLabel(traceId);
|
|
1006
|
+
this.deps.performance.startMeasurement(metricLabel);
|
|
1007
|
+
let llmResponse;
|
|
1008
|
+
let promptAfterHook = prompt;
|
|
1009
|
+
let handoffToPlanFlow = false;
|
|
1010
|
+
try {
|
|
1011
|
+
const pluginContextFactory = (hookName) => ({
|
|
1012
|
+
traceId,
|
|
1013
|
+
hookName
|
|
1014
|
+
});
|
|
1015
|
+
promptAfterHook = await this.runHook(
|
|
1016
|
+
"beforeLLM",
|
|
1017
|
+
prompt,
|
|
1018
|
+
pluginContextFactory("beforeLLM")
|
|
1019
|
+
);
|
|
1020
|
+
const llmContext = this.toRecord(this.deps.context.getContext());
|
|
1021
|
+
const llmRequestBase = {
|
|
1022
|
+
prompt: promptAfterHook,
|
|
1023
|
+
context: llmContext,
|
|
1024
|
+
signal: options.signal
|
|
1025
|
+
};
|
|
1026
|
+
const llmUseStructuredOutput = this.deps.config.get("llmUseStructuredOutput") !== false;
|
|
1027
|
+
let llmStructuredResponse;
|
|
1028
|
+
let llmResponseRaw;
|
|
1029
|
+
if (llmUseStructuredOutput && typeof this.deps.llm.generateStructuredResponse === "function") {
|
|
1030
|
+
const structuredRequest = {
|
|
1031
|
+
...llmRequestBase,
|
|
1032
|
+
format: "runtime-plan",
|
|
1033
|
+
strict: true
|
|
1034
|
+
};
|
|
1035
|
+
llmStructuredResponse = await this.deps.llm.generateStructuredResponse(structuredRequest);
|
|
1036
|
+
if (llmStructuredResponse.valid && llmStructuredResponse.text.trim().length > 0) {
|
|
1037
|
+
llmResponseRaw = {
|
|
1038
|
+
text: llmStructuredResponse.text,
|
|
1039
|
+
tokensUsed: llmStructuredResponse.tokensUsed,
|
|
1040
|
+
model: llmStructuredResponse.model,
|
|
1041
|
+
raw: {
|
|
1042
|
+
mode: "structured",
|
|
1043
|
+
value: llmStructuredResponse.value,
|
|
1044
|
+
errors: llmStructuredResponse.errors,
|
|
1045
|
+
payload: llmStructuredResponse.raw
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
} else {
|
|
1049
|
+
const fallbackResponse = await this.deps.llm.generateResponse(llmRequestBase);
|
|
1050
|
+
llmResponseRaw = {
|
|
1051
|
+
...fallbackResponse,
|
|
1052
|
+
raw: {
|
|
1053
|
+
mode: "fallback-text",
|
|
1054
|
+
structuredErrors: llmStructuredResponse.errors ?? [],
|
|
1055
|
+
fallbackPayload: fallbackResponse.raw
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
} else {
|
|
1060
|
+
llmResponseRaw = await this.deps.llm.generateResponse(llmRequestBase);
|
|
1061
|
+
}
|
|
1062
|
+
llmResponse = await this.runHook(
|
|
1063
|
+
"afterLLM",
|
|
1064
|
+
llmResponseRaw,
|
|
1065
|
+
pluginContextFactory("afterLLM")
|
|
1066
|
+
);
|
|
1067
|
+
const codegenInputRaw = {
|
|
1068
|
+
prompt: promptAfterHook,
|
|
1069
|
+
llmText: llmResponse.text,
|
|
1070
|
+
context: llmContext
|
|
1071
|
+
};
|
|
1072
|
+
const codegenInput = await this.runHook(
|
|
1073
|
+
"beforeCodeGen",
|
|
1074
|
+
codegenInputRaw,
|
|
1075
|
+
pluginContextFactory("beforeCodeGen")
|
|
1076
|
+
);
|
|
1077
|
+
const planned = await this.deps.codegen.generatePlan(codegenInput);
|
|
1078
|
+
const planAfterCodegen = await this.runHook(
|
|
1079
|
+
"afterCodeGen",
|
|
1080
|
+
planned,
|
|
1081
|
+
pluginContextFactory("afterCodeGen")
|
|
1082
|
+
);
|
|
1083
|
+
handoffToPlanFlow = true;
|
|
1084
|
+
const planFlowResult = await this.executePlanFlow({
|
|
1085
|
+
traceId,
|
|
1086
|
+
metricLabel,
|
|
1087
|
+
prompt: promptAfterHook,
|
|
1088
|
+
plan: planAfterCodegen,
|
|
1089
|
+
target: options.target,
|
|
1090
|
+
signal: options.signal
|
|
1091
|
+
});
|
|
1092
|
+
return {
|
|
1093
|
+
...planFlowResult,
|
|
1094
|
+
prompt: promptAfterHook,
|
|
1095
|
+
llm: llmResponse
|
|
1096
|
+
};
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
if (!handoffToPlanFlow) {
|
|
1099
|
+
const metric = this.deps.performance.endMeasurement(metricLabel);
|
|
1100
|
+
this.emit("renderFailed", { traceId, metric, error });
|
|
1101
|
+
}
|
|
1102
|
+
throw error;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
async *renderPromptStream(prompt, options = {}) {
|
|
1106
|
+
this.ensureRunning();
|
|
1107
|
+
this.throwIfAborted(options.signal);
|
|
1108
|
+
const traceId = options.traceId ?? this.createTraceId();
|
|
1109
|
+
const metricLabel = this.createMetricLabel(traceId);
|
|
1110
|
+
this.deps.performance.startMeasurement(metricLabel);
|
|
1111
|
+
let promptAfterHook = prompt;
|
|
1112
|
+
let handoffToPlanFlow = false;
|
|
1113
|
+
let llmResponse;
|
|
1114
|
+
try {
|
|
1115
|
+
const pluginContextFactory = (hookName) => ({
|
|
1116
|
+
traceId,
|
|
1117
|
+
hookName
|
|
1118
|
+
});
|
|
1119
|
+
promptAfterHook = await this.runHook(
|
|
1120
|
+
"beforeLLM",
|
|
1121
|
+
prompt,
|
|
1122
|
+
pluginContextFactory("beforeLLM")
|
|
1123
|
+
);
|
|
1124
|
+
const llmContext = this.toRecord(this.deps.context.getContext());
|
|
1125
|
+
const llmRequestBase = {
|
|
1126
|
+
prompt: promptAfterHook,
|
|
1127
|
+
context: llmContext,
|
|
1128
|
+
signal: options.signal
|
|
1129
|
+
};
|
|
1130
|
+
const incrementalCodegenSession = typeof this.deps.codegen.createIncrementalSession === "function" ? this.deps.codegen.createIncrementalSession({
|
|
1131
|
+
prompt: promptAfterHook,
|
|
1132
|
+
context: llmContext
|
|
1133
|
+
}) : void 0;
|
|
1134
|
+
const llmUseStructuredOutput = this.deps.config.get("llmUseStructuredOutput") !== false;
|
|
1135
|
+
const streamPreviewInterval = Math.max(
|
|
1136
|
+
1,
|
|
1137
|
+
Math.floor(options.previewEveryChunks ?? 2)
|
|
1138
|
+
);
|
|
1139
|
+
const buildPreviewChunk = async (llmText, delta) => {
|
|
1140
|
+
const preview = await this.buildStreamingPreview(
|
|
1141
|
+
promptAfterHook,
|
|
1142
|
+
llmText,
|
|
1143
|
+
llmContext,
|
|
1144
|
+
options.target,
|
|
1145
|
+
incrementalCodegenSession,
|
|
1146
|
+
delta,
|
|
1147
|
+
options.signal
|
|
1148
|
+
);
|
|
1149
|
+
if (!preview) {
|
|
1150
|
+
return void 0;
|
|
1151
|
+
}
|
|
1152
|
+
return {
|
|
1153
|
+
type: "preview",
|
|
1154
|
+
traceId,
|
|
1155
|
+
prompt: promptAfterHook,
|
|
1156
|
+
llmText,
|
|
1157
|
+
html: preview.html,
|
|
1158
|
+
diagnostics: preview.execution.diagnostics,
|
|
1159
|
+
planId: preview.plan.id
|
|
1160
|
+
};
|
|
1161
|
+
};
|
|
1162
|
+
if (llmUseStructuredOutput && typeof this.deps.llm.generateStructuredResponse === "function") {
|
|
1163
|
+
const structuredRequest = {
|
|
1164
|
+
...llmRequestBase,
|
|
1165
|
+
format: "runtime-plan",
|
|
1166
|
+
strict: true
|
|
1167
|
+
};
|
|
1168
|
+
const structuredResponse = await this.deps.llm.generateStructuredResponse(structuredRequest);
|
|
1169
|
+
if (structuredResponse.valid && structuredResponse.text.trim().length > 0) {
|
|
1170
|
+
const fullText = structuredResponse.text;
|
|
1171
|
+
const chunkSize = Math.max(256, Math.floor(fullText.length / 4));
|
|
1172
|
+
let latestText = "";
|
|
1173
|
+
for (let offset = 0; offset < fullText.length; offset += chunkSize) {
|
|
1174
|
+
const delta = fullText.slice(offset, offset + chunkSize);
|
|
1175
|
+
latestText += delta;
|
|
1176
|
+
const done = latestText.length >= fullText.length;
|
|
1177
|
+
yield {
|
|
1178
|
+
type: "llm-delta",
|
|
1179
|
+
traceId,
|
|
1180
|
+
prompt: promptAfterHook,
|
|
1181
|
+
llmText: latestText,
|
|
1182
|
+
delta
|
|
1183
|
+
};
|
|
1184
|
+
if (done) {
|
|
1185
|
+
const previewChunk = await buildPreviewChunk(latestText, delta);
|
|
1186
|
+
if (previewChunk) {
|
|
1187
|
+
yield previewChunk;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
llmResponse = {
|
|
1192
|
+
text: fullText,
|
|
1193
|
+
tokensUsed: structuredResponse.tokensUsed ?? fullText.length,
|
|
1194
|
+
model: structuredResponse.model,
|
|
1195
|
+
raw: {
|
|
1196
|
+
mode: "structured",
|
|
1197
|
+
value: structuredResponse.value,
|
|
1198
|
+
errors: structuredResponse.errors,
|
|
1199
|
+
payload: structuredResponse.raw
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
} else {
|
|
1203
|
+
let fallbackRaw;
|
|
1204
|
+
if (typeof this.deps.llm.generateResponseStream === "function") {
|
|
1205
|
+
let latestText = "";
|
|
1206
|
+
let latestChunk;
|
|
1207
|
+
let chunkCount = 0;
|
|
1208
|
+
for await (const chunk of this.deps.llm.generateResponseStream(
|
|
1209
|
+
llmRequestBase
|
|
1210
|
+
)) {
|
|
1211
|
+
this.throwIfAborted(options.signal);
|
|
1212
|
+
chunkCount += 1;
|
|
1213
|
+
latestChunk = chunk;
|
|
1214
|
+
latestText = chunk.text;
|
|
1215
|
+
yield {
|
|
1216
|
+
type: "llm-delta",
|
|
1217
|
+
traceId,
|
|
1218
|
+
prompt: promptAfterHook,
|
|
1219
|
+
llmText: latestText,
|
|
1220
|
+
delta: chunk.delta
|
|
1221
|
+
};
|
|
1222
|
+
if (chunk.done || chunkCount % streamPreviewInterval === 0) {
|
|
1223
|
+
const previewChunk = await buildPreviewChunk(
|
|
1224
|
+
latestText,
|
|
1225
|
+
chunk.delta
|
|
1226
|
+
);
|
|
1227
|
+
if (previewChunk) {
|
|
1228
|
+
yield previewChunk;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
fallbackRaw = {
|
|
1233
|
+
text: latestText,
|
|
1234
|
+
tokensUsed: latestChunk?.tokensUsed ?? latestText.length,
|
|
1235
|
+
model: latestChunk?.model,
|
|
1236
|
+
raw: {
|
|
1237
|
+
mode: "stream",
|
|
1238
|
+
source: latestChunk?.raw
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
} else {
|
|
1242
|
+
fallbackRaw = await this.deps.llm.generateResponse(llmRequestBase);
|
|
1243
|
+
yield {
|
|
1244
|
+
type: "llm-delta",
|
|
1245
|
+
traceId,
|
|
1246
|
+
prompt: promptAfterHook,
|
|
1247
|
+
llmText: fallbackRaw.text,
|
|
1248
|
+
delta: fallbackRaw.text
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
llmResponse = {
|
|
1252
|
+
...fallbackRaw,
|
|
1253
|
+
raw: {
|
|
1254
|
+
mode: "fallback-text",
|
|
1255
|
+
structuredErrors: structuredResponse.errors ?? [],
|
|
1256
|
+
fallbackPayload: fallbackRaw.raw
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
} else if (typeof this.deps.llm.generateResponseStream === "function") {
|
|
1261
|
+
let latestText = "";
|
|
1262
|
+
let latestChunk;
|
|
1263
|
+
let chunkCount = 0;
|
|
1264
|
+
for await (const chunk of this.deps.llm.generateResponseStream(
|
|
1265
|
+
llmRequestBase
|
|
1266
|
+
)) {
|
|
1267
|
+
this.throwIfAborted(options.signal);
|
|
1268
|
+
chunkCount += 1;
|
|
1269
|
+
latestChunk = chunk;
|
|
1270
|
+
latestText = chunk.text;
|
|
1271
|
+
yield {
|
|
1272
|
+
type: "llm-delta",
|
|
1273
|
+
traceId,
|
|
1274
|
+
prompt: promptAfterHook,
|
|
1275
|
+
llmText: latestText,
|
|
1276
|
+
delta: chunk.delta
|
|
1277
|
+
};
|
|
1278
|
+
if (chunk.done || chunkCount % streamPreviewInterval === 0) {
|
|
1279
|
+
const previewChunk = await buildPreviewChunk(
|
|
1280
|
+
latestText,
|
|
1281
|
+
chunk.delta
|
|
1282
|
+
);
|
|
1283
|
+
if (previewChunk) {
|
|
1284
|
+
yield previewChunk;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
llmResponse = {
|
|
1289
|
+
text: latestText,
|
|
1290
|
+
tokensUsed: latestChunk?.tokensUsed ?? latestText.length,
|
|
1291
|
+
model: latestChunk?.model,
|
|
1292
|
+
raw: {
|
|
1293
|
+
mode: "stream",
|
|
1294
|
+
source: latestChunk?.raw
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
} else {
|
|
1298
|
+
llmResponse = await this.deps.llm.generateResponse(llmRequestBase);
|
|
1299
|
+
yield {
|
|
1300
|
+
type: "llm-delta",
|
|
1301
|
+
traceId,
|
|
1302
|
+
prompt: promptAfterHook,
|
|
1303
|
+
llmText: llmResponse.text,
|
|
1304
|
+
delta: llmResponse.text
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
llmResponse = await this.runHook(
|
|
1308
|
+
"afterLLM",
|
|
1309
|
+
llmResponse,
|
|
1310
|
+
pluginContextFactory("afterLLM")
|
|
1311
|
+
);
|
|
1312
|
+
const codegenInputRaw = {
|
|
1313
|
+
prompt: promptAfterHook,
|
|
1314
|
+
llmText: llmResponse.text,
|
|
1315
|
+
context: llmContext
|
|
1316
|
+
};
|
|
1317
|
+
const codegenInput = await this.runHook(
|
|
1318
|
+
"beforeCodeGen",
|
|
1319
|
+
codegenInputRaw,
|
|
1320
|
+
pluginContextFactory("beforeCodeGen")
|
|
1321
|
+
);
|
|
1322
|
+
const planned = await incrementalCodegenSession?.finalize(llmResponse.text) ?? await this.deps.codegen.generatePlan(codegenInput);
|
|
1323
|
+
const planAfterCodegen = await this.runHook(
|
|
1324
|
+
"afterCodeGen",
|
|
1325
|
+
planned,
|
|
1326
|
+
pluginContextFactory("afterCodeGen")
|
|
1327
|
+
);
|
|
1328
|
+
handoffToPlanFlow = true;
|
|
1329
|
+
const planFlowResult = await this.executePlanFlow({
|
|
1330
|
+
traceId,
|
|
1331
|
+
metricLabel,
|
|
1332
|
+
prompt: promptAfterHook,
|
|
1333
|
+
plan: planAfterCodegen,
|
|
1334
|
+
target: options.target,
|
|
1335
|
+
signal: options.signal
|
|
1336
|
+
});
|
|
1337
|
+
const final = {
|
|
1338
|
+
...planFlowResult,
|
|
1339
|
+
prompt: promptAfterHook,
|
|
1340
|
+
llm: llmResponse
|
|
1341
|
+
};
|
|
1342
|
+
yield {
|
|
1343
|
+
type: "final",
|
|
1344
|
+
traceId,
|
|
1345
|
+
prompt: promptAfterHook,
|
|
1346
|
+
llmText: llmResponse.text,
|
|
1347
|
+
html: final.html,
|
|
1348
|
+
diagnostics: final.execution.diagnostics,
|
|
1349
|
+
planId: final.plan.id,
|
|
1350
|
+
final
|
|
1351
|
+
};
|
|
1352
|
+
return final;
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1355
|
+
const errorName = error instanceof Error ? error.name : void 0;
|
|
1356
|
+
yield {
|
|
1357
|
+
type: "error",
|
|
1358
|
+
traceId,
|
|
1359
|
+
prompt: promptAfterHook,
|
|
1360
|
+
llmText: llmResponse?.text ?? "",
|
|
1361
|
+
error: {
|
|
1362
|
+
message: errorMessage,
|
|
1363
|
+
...errorName ? { name: errorName } : {}
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
if (!handoffToPlanFlow) {
|
|
1367
|
+
const metric = this.deps.performance.endMeasurement(metricLabel);
|
|
1368
|
+
this.emit("renderFailed", { traceId, metric, error });
|
|
1369
|
+
}
|
|
1370
|
+
throw error;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
async renderPlan(plan, options = {}) {
|
|
1374
|
+
this.ensureRunning();
|
|
1375
|
+
const traceId = options.traceId ?? this.createTraceId();
|
|
1376
|
+
const metricLabel = this.createMetricLabel(traceId);
|
|
1377
|
+
this.deps.performance.startMeasurement(metricLabel);
|
|
1378
|
+
return this.executePlanFlow({
|
|
1379
|
+
traceId,
|
|
1380
|
+
metricLabel,
|
|
1381
|
+
prompt: options.prompt,
|
|
1382
|
+
plan,
|
|
1383
|
+
target: options.target,
|
|
1384
|
+
signal: options.signal
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
getConfig() {
|
|
1388
|
+
return this.deps.config;
|
|
1389
|
+
}
|
|
1390
|
+
getContext() {
|
|
1391
|
+
return this.deps.context;
|
|
1392
|
+
}
|
|
1393
|
+
getLLM() {
|
|
1394
|
+
return this.deps.llm;
|
|
1395
|
+
}
|
|
1396
|
+
getCodeGenerator() {
|
|
1397
|
+
return this.deps.codegen;
|
|
1398
|
+
}
|
|
1399
|
+
getRuntimeManager() {
|
|
1400
|
+
return this.deps.runtime;
|
|
1401
|
+
}
|
|
1402
|
+
getSecurityChecker() {
|
|
1403
|
+
return this.deps.security;
|
|
1404
|
+
}
|
|
1405
|
+
on(eventName, callback) {
|
|
1406
|
+
if (!this.listeners.has(eventName)) {
|
|
1407
|
+
this.listeners.set(eventName, /* @__PURE__ */ new Set());
|
|
1408
|
+
}
|
|
1409
|
+
this.listeners.get(eventName)?.add(callback);
|
|
1410
|
+
return () => {
|
|
1411
|
+
this.listeners.get(eventName)?.delete(callback);
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
emit(eventName, payload) {
|
|
1415
|
+
const callbacks = this.listeners.get(eventName);
|
|
1416
|
+
if (!callbacks) {
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
for (const callback of callbacks) {
|
|
1420
|
+
callback(payload);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
async executePlanFlow(params) {
|
|
1424
|
+
const { traceId, metricLabel, prompt, plan, target, signal } = params;
|
|
1425
|
+
this.throwIfAborted(signal);
|
|
1426
|
+
try {
|
|
1427
|
+
const pluginContextFactory = (hookName) => ({
|
|
1428
|
+
traceId,
|
|
1429
|
+
hookName
|
|
1430
|
+
});
|
|
1431
|
+
const planBeforePolicy = await this.runHook(
|
|
1432
|
+
"beforePolicyCheck",
|
|
1433
|
+
plan,
|
|
1434
|
+
pluginContextFactory("beforePolicyCheck")
|
|
1435
|
+
);
|
|
1436
|
+
const securityResultRaw = await this.deps.security.checkPlan(planBeforePolicy);
|
|
1437
|
+
const securityResult = await this.runHook(
|
|
1438
|
+
"afterPolicyCheck",
|
|
1439
|
+
securityResultRaw,
|
|
1440
|
+
pluginContextFactory("afterPolicyCheck")
|
|
1441
|
+
);
|
|
1442
|
+
if (!securityResult.safe) {
|
|
1443
|
+
this.emit("policyRejected", securityResult);
|
|
1444
|
+
throw new PolicyRejectionError(securityResult);
|
|
1445
|
+
}
|
|
1446
|
+
const runtimeInputRaw = {
|
|
1447
|
+
plan: planBeforePolicy,
|
|
1448
|
+
context: {
|
|
1449
|
+
userId: this.resolveUserId(),
|
|
1450
|
+
variables: {}
|
|
1451
|
+
},
|
|
1452
|
+
signal
|
|
1453
|
+
};
|
|
1454
|
+
const runtimeInput = await this.runHook(
|
|
1455
|
+
"beforeRuntime",
|
|
1456
|
+
runtimeInputRaw,
|
|
1457
|
+
pluginContextFactory("beforeRuntime")
|
|
1458
|
+
);
|
|
1459
|
+
const runtimeExecutionRaw = await this.deps.runtime.execute(runtimeInput);
|
|
1460
|
+
const runtimeExecution = await this.runHook(
|
|
1461
|
+
"afterRuntime",
|
|
1462
|
+
runtimeExecutionRaw,
|
|
1463
|
+
pluginContextFactory("afterRuntime")
|
|
1464
|
+
);
|
|
1465
|
+
const renderInput = await this.runHook(
|
|
1466
|
+
"beforeRender",
|
|
1467
|
+
runtimeExecution,
|
|
1468
|
+
pluginContextFactory("beforeRender")
|
|
1469
|
+
);
|
|
1470
|
+
const htmlRaw = await this.deps.ui.render(renderInput, target);
|
|
1471
|
+
this.throwIfAborted(signal);
|
|
1472
|
+
const html = await this.runHook(
|
|
1473
|
+
"afterRender",
|
|
1474
|
+
htmlRaw,
|
|
1475
|
+
pluginContextFactory("afterRender")
|
|
1476
|
+
);
|
|
1477
|
+
const metric = this.deps.performance.endMeasurement(metricLabel);
|
|
1478
|
+
this.emit("rendered", {
|
|
1479
|
+
traceId,
|
|
1480
|
+
metric,
|
|
1481
|
+
prompt,
|
|
1482
|
+
planId: planBeforePolicy.id
|
|
1483
|
+
});
|
|
1484
|
+
return {
|
|
1485
|
+
traceId,
|
|
1486
|
+
plan: planBeforePolicy,
|
|
1487
|
+
security: securityResult,
|
|
1488
|
+
execution: runtimeExecution,
|
|
1489
|
+
html
|
|
1490
|
+
};
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
const metric = this.deps.performance.endMeasurement(metricLabel);
|
|
1493
|
+
this.emit("renderFailed", { traceId, metric, prompt, error });
|
|
1494
|
+
throw error;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
ensureRunning() {
|
|
1498
|
+
if (!this.running) {
|
|
1499
|
+
throw new Error("RenderifyApp is not started");
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
async runHook(hookName, payload, context) {
|
|
1503
|
+
if (!this.deps.customization) {
|
|
1504
|
+
return payload;
|
|
1505
|
+
}
|
|
1506
|
+
return this.deps.customization.runHook(hookName, payload, context);
|
|
1507
|
+
}
|
|
1508
|
+
toRecord(value) {
|
|
1509
|
+
if (typeof value !== "object" || value === null) {
|
|
1510
|
+
return {};
|
|
1511
|
+
}
|
|
1512
|
+
return value;
|
|
1513
|
+
}
|
|
1514
|
+
createTraceId() {
|
|
1515
|
+
return `trace_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1516
|
+
}
|
|
1517
|
+
createMetricLabel(traceId) {
|
|
1518
|
+
return `pipeline:${traceId}`;
|
|
1519
|
+
}
|
|
1520
|
+
resolveUserId() {
|
|
1521
|
+
const candidate = this.deps.context.getContext().user?.id;
|
|
1522
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1523
|
+
return candidate.trim();
|
|
1524
|
+
}
|
|
1525
|
+
return "anonymous";
|
|
1526
|
+
}
|
|
1527
|
+
async buildStreamingPreview(prompt, llmText, context, target, incrementalCodegenSession, delta, signal) {
|
|
1528
|
+
try {
|
|
1529
|
+
this.throwIfAborted(signal);
|
|
1530
|
+
let plan;
|
|
1531
|
+
if (incrementalCodegenSession) {
|
|
1532
|
+
if (typeof delta === "string" && delta.length > 0) {
|
|
1533
|
+
const incrementalUpdate = await incrementalCodegenSession.pushDelta(delta);
|
|
1534
|
+
plan = incrementalUpdate?.plan;
|
|
1535
|
+
}
|
|
1536
|
+
if (!plan) {
|
|
1537
|
+
return void 0;
|
|
1538
|
+
}
|
|
1539
|
+
} else {
|
|
1540
|
+
plan = await this.deps.codegen.generatePlan({
|
|
1541
|
+
prompt,
|
|
1542
|
+
llmText,
|
|
1543
|
+
context
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
const security = await this.deps.security.checkPlan(plan);
|
|
1547
|
+
if (!security.safe) {
|
|
1548
|
+
return void 0;
|
|
1549
|
+
}
|
|
1550
|
+
const execution = await this.deps.runtime.execute({
|
|
1551
|
+
plan,
|
|
1552
|
+
context: {
|
|
1553
|
+
userId: this.resolveUserId(),
|
|
1554
|
+
variables: {}
|
|
1555
|
+
},
|
|
1556
|
+
signal
|
|
1557
|
+
});
|
|
1558
|
+
const html = await this.deps.ui.render(execution, target);
|
|
1559
|
+
return {
|
|
1560
|
+
plan,
|
|
1561
|
+
execution,
|
|
1562
|
+
html
|
|
1563
|
+
};
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1566
|
+
throw error;
|
|
1567
|
+
}
|
|
1568
|
+
return void 0;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
throwIfAborted(signal) {
|
|
1572
|
+
if (!signal?.aborted) {
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
const error = new Error("Renderify request aborted");
|
|
1576
|
+
error.name = "AbortError";
|
|
1577
|
+
throw error;
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
function createRenderifyApp(deps) {
|
|
1581
|
+
return new RenderifyApp(deps);
|
|
1582
|
+
}
|
|
1583
|
+
//# sourceMappingURL=core.cjs.js.map
|