@dotit/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/dist/aliases.d.ts +1 -0
  4. package/dist/aliases.js +8 -0
  5. package/dist/ask.d.ts +7 -0
  6. package/dist/ask.js +55 -0
  7. package/dist/browser.d.ts +12 -0
  8. package/dist/browser.js +32 -0
  9. package/dist/diff.d.ts +17 -0
  10. package/dist/diff.js +179 -0
  11. package/dist/document-css.d.ts +1 -0
  12. package/dist/document-css.js +290 -0
  13. package/dist/executor.d.ts +40 -0
  14. package/dist/executor.js +501 -0
  15. package/dist/history.d.ts +10 -0
  16. package/dist/history.js +297 -0
  17. package/dist/html-to-it.d.ts +1 -0
  18. package/dist/html-to-it.js +288 -0
  19. package/dist/index-builder.d.ts +62 -0
  20. package/dist/index-builder.js +228 -0
  21. package/dist/index.d.ts +39 -0
  22. package/dist/index.js +94 -0
  23. package/dist/language-registry.d.ts +39 -0
  24. package/dist/language-registry.js +530 -0
  25. package/dist/markdown.d.ts +1 -0
  26. package/dist/markdown.js +123 -0
  27. package/dist/merge.d.ts +6 -0
  28. package/dist/merge.js +255 -0
  29. package/dist/parser.d.ts +29 -0
  30. package/dist/parser.js +1562 -0
  31. package/dist/query.d.ts +32 -0
  32. package/dist/query.js +293 -0
  33. package/dist/renderer.d.ts +16 -0
  34. package/dist/renderer.js +1286 -0
  35. package/dist/schema.d.ts +47 -0
  36. package/dist/schema.js +574 -0
  37. package/dist/source.d.ts +3 -0
  38. package/dist/source.js +223 -0
  39. package/dist/theme.d.ts +49 -0
  40. package/dist/theme.js +113 -0
  41. package/dist/themes/corporate.json +86 -0
  42. package/dist/themes/dark.json +64 -0
  43. package/dist/themes/editorial.json +54 -0
  44. package/dist/themes/legal.json +57 -0
  45. package/dist/themes/minimal.json +50 -0
  46. package/dist/themes/print.json +54 -0
  47. package/dist/themes/technical.json +59 -0
  48. package/dist/themes/warm.json +53 -0
  49. package/dist/trust.d.ts +66 -0
  50. package/dist/trust.js +200 -0
  51. package/dist/types.d.ts +234 -0
  52. package/dist/types.js +19 -0
  53. package/dist/utils.d.ts +2 -0
  54. package/dist/utils.js +13 -0
  55. package/dist/validate.d.ts +13 -0
  56. package/dist/validate.js +711 -0
  57. package/dist/workflow.d.ts +18 -0
  58. package/dist/workflow.js +160 -0
  59. package/package.json +51 -0
@@ -0,0 +1,501 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeWorkflow = executeWorkflow;
4
+ const workflow_1 = require("./workflow");
5
+ const utils_1 = require("./utils");
6
+ const DANGEROUS_PATH_KEYS = new Set(["__proto__", "constructor", "prototype"]);
7
+ const MAX_PATH_DEPTH = 20;
8
+ function getByPath(obj, path) {
9
+ const keys = path.split(".");
10
+ if (keys.length > MAX_PATH_DEPTH)
11
+ return undefined;
12
+ return keys.reduce((cur, key) => {
13
+ if (cur === null || cur === undefined)
14
+ return undefined;
15
+ if (DANGEROUS_PATH_KEYS.has(key))
16
+ return undefined;
17
+ if (Array.isArray(cur)) {
18
+ const idx = Number(key);
19
+ if (!isNaN(idx) && idx >= 0 && idx < cur.length)
20
+ return cur[idx];
21
+ return undefined;
22
+ }
23
+ if (typeof cur === "object") {
24
+ return Object.prototype.hasOwnProperty.call(cur, key)
25
+ ? cur[key]
26
+ : undefined;
27
+ }
28
+ return undefined;
29
+ }, obj);
30
+ }
31
+ function resolveValue(value, context) {
32
+ if (value === undefined || value === null)
33
+ return undefined;
34
+ const str = String(value);
35
+ const exactMatch = str.match(/^\{\{([^}]+)\}\}$/);
36
+ if (exactMatch) {
37
+ return getByPath(context, exactMatch[1].trim()) ?? str;
38
+ }
39
+ return resolveTemplate(str, context);
40
+ }
41
+ function resolveTemplate(template, context) {
42
+ if (!template.includes("{{"))
43
+ return template;
44
+ return template.replace(/\{\{([^}]+)\}\}/g, (_match, rawPath) => {
45
+ const path = rawPath.trim();
46
+ if (path.length > 200)
47
+ return _match;
48
+ const val = getByPath(context, path);
49
+ if (val !== undefined && val !== null)
50
+ return String(val);
51
+ return _match;
52
+ });
53
+ }
54
+ function tokenize(input) {
55
+ const tokens = [];
56
+ let i = 0;
57
+ while (i < input.length) {
58
+ if (input[i] === " " || input[i] === "\t") {
59
+ i++;
60
+ continue;
61
+ }
62
+ if (input[i] === "'" || input[i] === '"') {
63
+ const quote = input[i];
64
+ i++;
65
+ let val = "";
66
+ while (i < input.length && input[i] !== quote) {
67
+ if (input[i] === "\\" && i + 1 < input.length) {
68
+ i++;
69
+ val += input[i];
70
+ }
71
+ else {
72
+ val += input[i];
73
+ }
74
+ i++;
75
+ }
76
+ i++;
77
+ tokens.push({ type: "string", value: val });
78
+ continue;
79
+ }
80
+ if (input[i] === "{" && input[i + 1] === "{") {
81
+ i += 2;
82
+ let path = "";
83
+ while (i < input.length && !(input[i] === "}" && input[i + 1] === "}")) {
84
+ path += input[i];
85
+ i++;
86
+ }
87
+ i += 2;
88
+ tokens.push({ type: "var", path: path.trim() });
89
+ continue;
90
+ }
91
+ const two = input.slice(i, i + 2);
92
+ if (two === "==" ||
93
+ two === "!=" ||
94
+ two === "<=" ||
95
+ two === ">=" ||
96
+ two === "&&" ||
97
+ two === "||") {
98
+ tokens.push({ type: "op", value: two });
99
+ i += 2;
100
+ continue;
101
+ }
102
+ if (input[i] === "<" || input[i] === ">") {
103
+ tokens.push({ type: "op", value: input[i] });
104
+ i++;
105
+ continue;
106
+ }
107
+ if (input[i] === "(") {
108
+ tokens.push({ type: "lparen" });
109
+ i++;
110
+ continue;
111
+ }
112
+ if (input[i] === ")") {
113
+ tokens.push({ type: "rparen" });
114
+ i++;
115
+ continue;
116
+ }
117
+ if ((input[i] >= "0" && input[i] <= "9") ||
118
+ (input[i] === "-" &&
119
+ i + 1 < input.length &&
120
+ input[i + 1] >= "0" &&
121
+ input[i + 1] <= "9")) {
122
+ let num = "";
123
+ if (input[i] === "-") {
124
+ num += "-";
125
+ i++;
126
+ }
127
+ while (i < input.length &&
128
+ ((input[i] >= "0" && input[i] <= "9") || input[i] === ".")) {
129
+ num += input[i];
130
+ i++;
131
+ }
132
+ tokens.push({ type: "number", value: Number(num) });
133
+ continue;
134
+ }
135
+ const rest = input.slice(i);
136
+ if (rest.startsWith("true") &&
137
+ (i + 4 >= input.length || !/\w/.test(input[i + 4]))) {
138
+ tokens.push({ type: "boolean", value: true });
139
+ i += 4;
140
+ continue;
141
+ }
142
+ if (rest.startsWith("false") &&
143
+ (i + 5 >= input.length || !/\w/.test(input[i + 5]))) {
144
+ tokens.push({ type: "boolean", value: false });
145
+ i += 5;
146
+ continue;
147
+ }
148
+ if (rest.startsWith("null") &&
149
+ (i + 4 >= input.length || !/\w/.test(input[i + 4]))) {
150
+ tokens.push({ type: "null" });
151
+ i += 4;
152
+ continue;
153
+ }
154
+ i++;
155
+ }
156
+ return tokens;
157
+ }
158
+ function atomValue(token, context) {
159
+ switch (token.type) {
160
+ case "string":
161
+ return token.value;
162
+ case "number":
163
+ return token.value;
164
+ case "boolean":
165
+ return token.value;
166
+ case "null":
167
+ return null;
168
+ case "var":
169
+ return getByPath(context, token.path);
170
+ default:
171
+ return undefined;
172
+ }
173
+ }
174
+ function compare(op, left, right) {
175
+ const lNum = typeof left === "number" ? left : Number(left);
176
+ const rNum = typeof right === "number" ? right : Number(right);
177
+ switch (op) {
178
+ case "==":
179
+ return left == right;
180
+ case "!=":
181
+ return left != right;
182
+ case "<":
183
+ return lNum < rNum;
184
+ case ">":
185
+ return lNum > rNum;
186
+ case "<=":
187
+ return lNum <= rNum;
188
+ case ">=":
189
+ return lNum >= rNum;
190
+ default:
191
+ return false;
192
+ }
193
+ }
194
+ function evaluateCondition(condition, context) {
195
+ if (!condition)
196
+ return true;
197
+ try {
198
+ const tokens = tokenize(condition);
199
+ let pos = 0;
200
+ function peek() {
201
+ return tokens[pos];
202
+ }
203
+ function advance() {
204
+ return tokens[pos++];
205
+ }
206
+ function parseExpr() {
207
+ return parseOr();
208
+ }
209
+ function parseOr() {
210
+ let left = parseAnd();
211
+ while (peek()?.type === "op" &&
212
+ peek().value === "||") {
213
+ advance();
214
+ const right = parseAnd();
215
+ left = left || right;
216
+ }
217
+ return left;
218
+ }
219
+ function parseAnd() {
220
+ let left = parseCmp();
221
+ while (peek()?.type === "op" &&
222
+ peek().value === "&&") {
223
+ advance();
224
+ const right = parseCmp();
225
+ left = left && right;
226
+ }
227
+ return left;
228
+ }
229
+ function parseCmp() {
230
+ const leftVal = parseAtom();
231
+ const next = peek();
232
+ if (next?.type === "op" && next.value !== "&&" && next.value !== "||") {
233
+ const op = advance().value;
234
+ const rightVal = parseAtom();
235
+ return compare(op, leftVal, rightVal);
236
+ }
237
+ return !!leftVal;
238
+ }
239
+ function parseAtom() {
240
+ const token = peek();
241
+ if (!token)
242
+ return undefined;
243
+ if (token.type === "lparen") {
244
+ advance();
245
+ const val = parseExpr();
246
+ if (peek()?.type === "rparen")
247
+ advance();
248
+ return val;
249
+ }
250
+ advance();
251
+ return atomValue(token, context);
252
+ }
253
+ return parseExpr();
254
+ }
255
+ catch {
256
+ return false;
257
+ }
258
+ }
259
+ function findBlockById(doc, blockId) {
260
+ const all = (0, utils_1.flattenBlocks)(doc.blocks);
261
+ return all.find((b) => b.id === blockId || b.properties?.id === blockId);
262
+ }
263
+ function writeStatus(doc, blockId, status) {
264
+ const block = findBlockById(doc, blockId);
265
+ if (!block)
266
+ return;
267
+ if (!block.properties)
268
+ block.properties = {};
269
+ block.properties.status = status;
270
+ }
271
+ function updateBlockContent(doc, blockId, content) {
272
+ const block = findBlockById(doc, blockId);
273
+ if (!block)
274
+ return;
275
+ block.content = content;
276
+ }
277
+ function withTimeout(promise, ms, message) {
278
+ return new Promise((resolve, reject) => {
279
+ const timer = setTimeout(() => reject(new Error(message)), ms);
280
+ promise.then((val) => {
281
+ clearTimeout(timer);
282
+ resolve(val);
283
+ }, (err) => {
284
+ clearTimeout(timer);
285
+ reject(err);
286
+ });
287
+ });
288
+ }
289
+ async function executeWorkflow(document, runtime = {}) {
290
+ const options = {
291
+ maxSteps: 1000,
292
+ stepTimeout: 30000,
293
+ unknownTool: "warn",
294
+ dryRun: false,
295
+ ...runtime.options,
296
+ };
297
+ const workflow = (0, workflow_1.extractWorkflow)(document);
298
+ const context = {};
299
+ const allBlocks = (0, utils_1.flattenBlocks)(document.blocks);
300
+ for (const block of allBlocks) {
301
+ if (block.type === "context" && block.properties) {
302
+ for (const [k, v] of Object.entries(block.properties)) {
303
+ context[k] = v;
304
+ }
305
+ }
306
+ }
307
+ if (runtime.context) {
308
+ Object.assign(context, runtime.context);
309
+ }
310
+ const policies = [];
311
+ for (const block of allBlocks) {
312
+ if (block.type === "policy") {
313
+ policies.push(block);
314
+ }
315
+ }
316
+ if (policies.length > 0) {
317
+ context.__policies = policies;
318
+ }
319
+ for (const policy of policies) {
320
+ const requires = policy.properties?.requires;
321
+ if (requires !== "gate")
322
+ continue;
323
+ const condition = policy.properties?.if ||
324
+ (policy.properties?.always ? "true" : undefined);
325
+ if (!evaluateCondition(condition, context))
326
+ continue;
327
+ const hasApprovedGate = allBlocks.some((b) => b.type === "gate" && b.properties?.status === "approved");
328
+ if (!hasApprovedGate) {
329
+ return {
330
+ document,
331
+ context,
332
+ log: [],
333
+ status: "policy_blocked",
334
+ blockedByPolicy: policy,
335
+ error: new Error(`Policy "${policy.content}" requires an approved gate but none found`),
336
+ };
337
+ }
338
+ }
339
+ const log = [];
340
+ const resultDoc = structuredClone(document);
341
+ let stepCount = 0;
342
+ for (const batch of workflow.executionOrder) {
343
+ for (const blockId of batch) {
344
+ if (stepCount >= options.maxSteps) {
345
+ return {
346
+ document: resultDoc,
347
+ context,
348
+ log,
349
+ status: "error",
350
+ error: new Error(`Max steps (${options.maxSteps}) reached`),
351
+ };
352
+ }
353
+ const block = findBlockById(resultDoc, blockId);
354
+ if (!block)
355
+ continue;
356
+ const entry = {
357
+ blockId: block.id,
358
+ blockType: block.type,
359
+ content: block.content,
360
+ status: "running",
361
+ timestamp: new Date().toISOString(),
362
+ };
363
+ const start = Date.now();
364
+ try {
365
+ switch (block.type) {
366
+ case "step": {
367
+ const toolName = block.properties?.tool;
368
+ const inputKey = block.properties?.input;
369
+ const outputKey = block.properties?.output;
370
+ const input = resolveValue(inputKey, context);
371
+ entry.input = input;
372
+ if (options.dryRun) {
373
+ entry.status = "dry_run";
374
+ writeStatus(resultDoc, blockId, "dry_run");
375
+ break;
376
+ }
377
+ if (!toolName) {
378
+ entry.status = "skipped";
379
+ writeStatus(resultDoc, blockId, "skipped");
380
+ break;
381
+ }
382
+ const handler = runtime.tools?.[toolName];
383
+ if (!handler) {
384
+ if (options.unknownTool === "error") {
385
+ throw new Error(`No tool handler registered for: ${toolName}`);
386
+ }
387
+ entry.status = "skipped";
388
+ writeStatus(resultDoc, blockId, "skipped");
389
+ break;
390
+ }
391
+ runtime.onStepStart?.(block, context);
392
+ const output = await withTimeout(Promise.resolve(handler(input, context)), options.stepTimeout, `Step "${block.content}" timed out after ${options.stepTimeout}ms`);
393
+ if (outputKey) {
394
+ context[outputKey] = output;
395
+ }
396
+ runtime.onStepComplete?.(block, output, context);
397
+ entry.output = output;
398
+ entry.status = "completed";
399
+ writeStatus(resultDoc, blockId, "done");
400
+ stepCount++;
401
+ break;
402
+ }
403
+ case "decision": {
404
+ const condition = block.properties?.if;
405
+ const thenTarget = block.properties?.then;
406
+ const elseTarget = block.properties?.else;
407
+ const result = evaluateCondition(condition, context);
408
+ const nextTarget = result ? thenTarget : elseTarget;
409
+ context.__lastDecision = {
410
+ condition,
411
+ result,
412
+ took: nextTarget,
413
+ };
414
+ entry.status = "completed";
415
+ entry.output = { condition, result, branch: nextTarget };
416
+ writeStatus(resultDoc, blockId, "done");
417
+ break;
418
+ }
419
+ case "gate": {
420
+ if (options.dryRun) {
421
+ entry.status = "dry_run";
422
+ writeStatus(resultDoc, blockId, "dry_run");
423
+ break;
424
+ }
425
+ if (!runtime.onGate) {
426
+ writeStatus(resultDoc, blockId, "blocked");
427
+ entry.status = "blocked";
428
+ entry.durationMs = Date.now() - start;
429
+ log.push(entry);
430
+ return {
431
+ document: resultDoc,
432
+ context,
433
+ log,
434
+ status: "gate_blocked",
435
+ blockedAt: block,
436
+ };
437
+ }
438
+ const approved = await runtime.onGate(block, context);
439
+ if (!approved) {
440
+ writeStatus(resultDoc, blockId, "rejected");
441
+ entry.status = "blocked";
442
+ entry.durationMs = Date.now() - start;
443
+ log.push(entry);
444
+ return {
445
+ document: resultDoc,
446
+ context,
447
+ log,
448
+ status: "gate_blocked",
449
+ blockedAt: block,
450
+ };
451
+ }
452
+ writeStatus(resultDoc, blockId, "approved");
453
+ entry.status = "completed";
454
+ break;
455
+ }
456
+ case "audit": {
457
+ const resolved = resolveTemplate(block.content, context);
458
+ updateBlockContent(resultDoc, blockId, resolved);
459
+ runtime.onAudit?.(block, context);
460
+ entry.status = "completed";
461
+ writeStatus(resultDoc, blockId, "done");
462
+ break;
463
+ }
464
+ case "trigger": {
465
+ entry.status = "completed";
466
+ writeStatus(resultDoc, blockId, "done");
467
+ break;
468
+ }
469
+ case "result": {
470
+ const resolved = resolveTemplate(block.content, context);
471
+ updateBlockContent(resultDoc, blockId, resolved);
472
+ entry.status = "completed";
473
+ writeStatus(resultDoc, blockId, "done");
474
+ break;
475
+ }
476
+ default:
477
+ entry.status = "skipped";
478
+ break;
479
+ }
480
+ }
481
+ catch (err) {
482
+ const error = err instanceof Error ? err : new Error(String(err));
483
+ entry.status = "failed";
484
+ entry.error = error.message;
485
+ writeStatus(resultDoc, blockId, "failed");
486
+ runtime.onStepError?.(block, error, context);
487
+ entry.durationMs = Date.now() - start;
488
+ log.push(entry);
489
+ return { document: resultDoc, context, log, status: "error", error };
490
+ }
491
+ entry.durationMs = Date.now() - start;
492
+ log.push(entry);
493
+ }
494
+ }
495
+ return {
496
+ document: resultDoc,
497
+ context,
498
+ log,
499
+ status: options.dryRun ? "dry_run" : "completed",
500
+ };
501
+ }
@@ -0,0 +1,10 @@
1
+ import { RegistryEntry, RevisionEntry } from "./types";
2
+ export declare function parseHistorySection(raw: string): {
3
+ registry: RegistryEntry[];
4
+ revisions: RevisionEntry[];
5
+ registryIntact: boolean;
6
+ };
7
+ export interface SaveHistoryOptions {
8
+ by: string;
9
+ }
10
+ export declare function updateHistory(previousSource: string, currentSource: string, options: SaveHistoryOptions): string;