@markbrutx/promptbook-core 0.2.0 → 0.4.1
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/bundle.d.ts.map +1 -1
- package/dist/bundle.js +10 -2
- package/dist/bundle.js.map +1 -1
- package/dist/edge/index.js +389 -0
- package/dist/edge.d.ts +12 -7
- package/dist/edge.d.ts.map +1 -1
- package/dist/edge.js +9 -6
- package/dist/edge.js.map +1 -1
- package/package.json +1 -1
- package/src/bundle.ts +10 -2
- package/src/edge.ts +23 -7
package/dist/bundle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuD,UAAU,EAAQ,MAAM,YAAY,CAAC;AAExG,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,wGAAwG;IACxG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuD,UAAU,EAAQ,MAAM,YAAY,CAAC;AAExG,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,wGAAwG;IACxG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAuGD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAMhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAY1F;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAK1D"}
|
package/dist/bundle.js
CHANGED
|
@@ -71,10 +71,18 @@ function canonicalCodePrompt(codePrompt) {
|
|
|
71
71
|
};
|
|
72
72
|
return compact(ordered);
|
|
73
73
|
}
|
|
74
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Emit `new Map([...])` with one `[key, value]` entry per line (entries pre-sorted).
|
|
76
|
+
*
|
|
77
|
+
* An empty map serializes to `new Map()` (not `new Map([])`): without entries to
|
|
78
|
+
* infer from, `new Map([])` widens to `Map<unknown, unknown>`, which is not
|
|
79
|
+
* assignable to `Map<string, T>` in a plain (annotation-free) module. `new Map()`
|
|
80
|
+
* infers `Map<any, any>`, which stays assignable, so plain bundles type-check
|
|
81
|
+
* against `PromptBook` even when a map is empty.
|
|
82
|
+
*/
|
|
75
83
|
function serializeMap(entries, canonical) {
|
|
76
84
|
if (entries.length === 0) {
|
|
77
|
-
return "new Map(
|
|
85
|
+
return "new Map()";
|
|
78
86
|
}
|
|
79
87
|
const lines = entries.map(([key, value]) => ` [${JSON.stringify(key)}, ${JSON.stringify(canonical(value))}],`);
|
|
80
88
|
return `new Map([\n${lines.join("\n")}\n ])`;
|
package/dist/bundle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
|
package/dist/edge/index.js
CHANGED
|
@@ -19,6 +19,103 @@ function interpolate(body, context, onMissing) {
|
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// dist/lint/rules/banned-tokens.js
|
|
23
|
+
var EM_DASH = "\u2014";
|
|
24
|
+
var DEFAULT_BANNED = [EM_DASH];
|
|
25
|
+
function bannedTokens(options = {}) {
|
|
26
|
+
const tokens = options.tokens ?? DEFAULT_BANNED;
|
|
27
|
+
const severity = options.severity ?? "error";
|
|
28
|
+
return {
|
|
29
|
+
id: "banned-tokens",
|
|
30
|
+
description: "Resolved prompt must not contain banned substrings or patterns.",
|
|
31
|
+
scope: "resolved",
|
|
32
|
+
check(input) {
|
|
33
|
+
const result = input.result;
|
|
34
|
+
if (result === void 0) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const text = result.text;
|
|
38
|
+
const findings = [];
|
|
39
|
+
for (const token of tokens) {
|
|
40
|
+
const matched = typeof token === "string" ? text.includes(token) : token.test(text);
|
|
41
|
+
if (matched) {
|
|
42
|
+
const label = typeof token === "string" ? JSON.stringify(token) : String(token);
|
|
43
|
+
findings.push({
|
|
44
|
+
ruleId: "banned-tokens",
|
|
45
|
+
severity,
|
|
46
|
+
message: `text contains banned token ${label}`
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return findings;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// dist/lint/references.js
|
|
56
|
+
function* iterateReferences(book) {
|
|
57
|
+
for (const composition of book.compositions.values()) {
|
|
58
|
+
const composed = composition.name;
|
|
59
|
+
for (const id of composition.base) {
|
|
60
|
+
yield { id, composition: composed, role: "base" };
|
|
61
|
+
}
|
|
62
|
+
if (composition.order) {
|
|
63
|
+
for (const id of composition.order) {
|
|
64
|
+
yield { id, composition: composed, role: "order" };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const rule of composition.rules) {
|
|
68
|
+
const ruleIndex = rule.index;
|
|
69
|
+
for (const id of rule.add ?? []) {
|
|
70
|
+
yield { id, composition: composed, role: "add", ruleIndex };
|
|
71
|
+
}
|
|
72
|
+
if (rule.after !== void 0) {
|
|
73
|
+
yield { id: rule.after, composition: composed, role: "after", ruleIndex };
|
|
74
|
+
}
|
|
75
|
+
for (const [from, to] of Object.entries(rule.replace ?? {})) {
|
|
76
|
+
yield { id: from, composition: composed, role: "replace-from", ruleIndex };
|
|
77
|
+
yield { id: to, composition: composed, role: "replace-to", ruleIndex };
|
|
78
|
+
}
|
|
79
|
+
for (const id of rule.forbid ?? []) {
|
|
80
|
+
yield { id, composition: composed, role: "forbid", ruleIndex };
|
|
81
|
+
}
|
|
82
|
+
for (const id of rule.order ?? []) {
|
|
83
|
+
yield { id, composition: composed, role: "rule-order", ruleIndex };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// dist/lint/rules/dangling-reference.js
|
|
90
|
+
function danglingReference(options = {}) {
|
|
91
|
+
const severity = options.severity ?? "error";
|
|
92
|
+
return {
|
|
93
|
+
id: "dangling-reference",
|
|
94
|
+
description: "Every referenced fragment id must exist.",
|
|
95
|
+
scope: "book",
|
|
96
|
+
check(input) {
|
|
97
|
+
const findings = [];
|
|
98
|
+
for (const reference of iterateReferences(input.book)) {
|
|
99
|
+
if (input.book.fragments.has(reference.id)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const where = reference.ruleIndex !== void 0 ? `rule #${reference.ruleIndex} (${reference.role})` : reference.role;
|
|
103
|
+
const finding = {
|
|
104
|
+
ruleId: "dangling-reference",
|
|
105
|
+
severity,
|
|
106
|
+
message: `composition "${reference.composition}" references unknown fragment "${reference.id}" via ${where}`,
|
|
107
|
+
fragmentId: reference.id
|
|
108
|
+
};
|
|
109
|
+
if (reference.ruleIndex !== void 0) {
|
|
110
|
+
finding.ruleIndex = reference.ruleIndex;
|
|
111
|
+
}
|
|
112
|
+
findings.push(finding);
|
|
113
|
+
}
|
|
114
|
+
return findings;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
22
119
|
// dist/resolve-book.js
|
|
23
120
|
function resolveBook(book, prompt, context) {
|
|
24
121
|
const composition = book.compositions.get(prompt);
|
|
@@ -188,7 +285,299 @@ function computeUnmatchedAxes(composition, context) {
|
|
|
188
285
|
}
|
|
189
286
|
return result;
|
|
190
287
|
}
|
|
288
|
+
|
|
289
|
+
// dist/lint/rules/dead-rule.js
|
|
290
|
+
function deadRule(options = {}) {
|
|
291
|
+
const severity = options.severity ?? "warning";
|
|
292
|
+
return {
|
|
293
|
+
id: "dead-rule",
|
|
294
|
+
description: "Rules should affect fragments that can be present (static check).",
|
|
295
|
+
scope: "book",
|
|
296
|
+
check(input) {
|
|
297
|
+
const findings = [];
|
|
298
|
+
for (const composition of input.book.compositions.values()) {
|
|
299
|
+
const everPresent = everPresentSet(composition);
|
|
300
|
+
composition.rules.forEach((rule, position) => {
|
|
301
|
+
const present = presentSetFor(composition.base, composition.rules, position, rule.when);
|
|
302
|
+
switch (rule.action) {
|
|
303
|
+
case "add":
|
|
304
|
+
for (const id of rule.add ?? []) {
|
|
305
|
+
if (present.has(id)) {
|
|
306
|
+
findings.push({
|
|
307
|
+
ruleId: "dead-rule",
|
|
308
|
+
severity,
|
|
309
|
+
message: `rule #${rule.index} in "${composition.name}" adds "${id}", which is already present`,
|
|
310
|
+
fragmentId: id,
|
|
311
|
+
ruleIndex: rule.index
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
break;
|
|
316
|
+
case "replace":
|
|
317
|
+
for (const from of Object.keys(rule.replace ?? {})) {
|
|
318
|
+
if (!present.has(from)) {
|
|
319
|
+
findings.push({
|
|
320
|
+
ruleId: "dead-rule",
|
|
321
|
+
severity,
|
|
322
|
+
message: `rule #${rule.index} in "${composition.name}" replaces "${from}", which is never present`,
|
|
323
|
+
fragmentId: from,
|
|
324
|
+
ruleIndex: rule.index
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
case "order":
|
|
330
|
+
for (const id of rule.order ?? []) {
|
|
331
|
+
if (!everPresent.has(id)) {
|
|
332
|
+
findings.push({
|
|
333
|
+
ruleId: "dead-rule",
|
|
334
|
+
severity,
|
|
335
|
+
message: `rule #${rule.index} in "${composition.name}" orders unknown id "${id}"`,
|
|
336
|
+
fragmentId: id,
|
|
337
|
+
ruleIndex: rule.index
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
case "forbid":
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return findings;
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function presentSetFor(base, rules, position, currentWhen) {
|
|
352
|
+
const present = new Set(base);
|
|
353
|
+
for (let i = 0; i < position; i++) {
|
|
354
|
+
const prior = rules[i];
|
|
355
|
+
if (!prior || !firesWhenever(prior.when, currentWhen)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (prior.action === "add") {
|
|
359
|
+
for (const id of prior.add ?? []) {
|
|
360
|
+
present.add(id);
|
|
361
|
+
}
|
|
362
|
+
} else if (prior.action === "replace") {
|
|
363
|
+
for (const [from, to] of Object.entries(prior.replace ?? {})) {
|
|
364
|
+
if (present.has(from)) {
|
|
365
|
+
present.delete(from);
|
|
366
|
+
present.add(to);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return present;
|
|
372
|
+
}
|
|
373
|
+
function everPresentSet(composition) {
|
|
374
|
+
const present = new Set(composition.base);
|
|
375
|
+
for (const rule of composition.rules) {
|
|
376
|
+
if (rule.action === "add") {
|
|
377
|
+
for (const id of rule.add ?? []) {
|
|
378
|
+
present.add(id);
|
|
379
|
+
}
|
|
380
|
+
} else if (rule.action === "replace") {
|
|
381
|
+
for (const to of Object.values(rule.replace ?? {})) {
|
|
382
|
+
present.add(to);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return present;
|
|
387
|
+
}
|
|
388
|
+
function firesWhenever(prior, current) {
|
|
389
|
+
for (const [key, value] of Object.entries(prior)) {
|
|
390
|
+
const actual = current[key];
|
|
391
|
+
if (actual === void 0 || !valuesEqual(actual, value)) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// dist/lint/rules/example-balance.js
|
|
399
|
+
function exampleBalance(options = {}) {
|
|
400
|
+
const kind = options.kind ?? "example";
|
|
401
|
+
const groupTagPrefix = options.groupTagPrefix ?? "case:";
|
|
402
|
+
const maxSkew = options.maxSkew ?? 1;
|
|
403
|
+
const severity = options.severity ?? "warning";
|
|
404
|
+
return {
|
|
405
|
+
id: "example-balance",
|
|
406
|
+
description: `Example groups (kind "${kind}", tag "${groupTagPrefix}*") should differ by at most ${maxSkew}.`,
|
|
407
|
+
scope: "resolved",
|
|
408
|
+
check(input) {
|
|
409
|
+
const result = input.result;
|
|
410
|
+
if (result === void 0) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
const counts = /* @__PURE__ */ new Map();
|
|
414
|
+
for (const id of result.trace.finalOrder) {
|
|
415
|
+
const fragment = input.book.fragments.get(id);
|
|
416
|
+
if (fragment?.kind !== kind) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
for (const tag of fragment.tags ?? []) {
|
|
420
|
+
if (tag.startsWith(groupTagPrefix)) {
|
|
421
|
+
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (counts.size < 2) {
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
const values = [...counts.values()];
|
|
429
|
+
const max = Math.max(...values);
|
|
430
|
+
const min = Math.min(...values);
|
|
431
|
+
if (max - min <= maxSkew) {
|
|
432
|
+
return [];
|
|
433
|
+
}
|
|
434
|
+
const summary = [...counts.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([group, count]) => `${group}=${count}`).join(", ");
|
|
435
|
+
return [
|
|
436
|
+
{
|
|
437
|
+
ruleId: "example-balance",
|
|
438
|
+
severity,
|
|
439
|
+
message: `example groups are imbalanced (${summary}); skew ${max - min} exceeds ${maxSkew}`
|
|
440
|
+
}
|
|
441
|
+
];
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// dist/lint/rules/language-directive-position.js
|
|
447
|
+
function languageDirectivePosition(options = {}) {
|
|
448
|
+
const kind = options.kind ?? "language-directive";
|
|
449
|
+
const edgeWindow = options.edgeWindow ?? 2;
|
|
450
|
+
const severity = options.severity ?? "warning";
|
|
451
|
+
return {
|
|
452
|
+
id: "language-directive-position",
|
|
453
|
+
description: `Fragments of kind "${kind}" should sit within the first/last ${edgeWindow} positions.`,
|
|
454
|
+
scope: "resolved",
|
|
455
|
+
check(input) {
|
|
456
|
+
const result = input.result;
|
|
457
|
+
if (result === void 0) {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
const order = result.trace.finalOrder;
|
|
461
|
+
const total = order.length;
|
|
462
|
+
const findings = [];
|
|
463
|
+
order.forEach((id, position) => {
|
|
464
|
+
if (input.book.fragments.get(id)?.kind !== kind) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const atStart = position < edgeWindow;
|
|
468
|
+
const atEnd = position >= total - edgeWindow;
|
|
469
|
+
if (!atStart && !atEnd) {
|
|
470
|
+
findings.push({
|
|
471
|
+
ruleId: "language-directive-position",
|
|
472
|
+
severity,
|
|
473
|
+
message: `fragment "${id}" (kind "${kind}") is at position ${position + 1} of ${total}, not within the first/last ${edgeWindow}`,
|
|
474
|
+
fragmentId: id
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
return findings;
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// dist/lint/rules/token-budget.js
|
|
484
|
+
function estimateTokensByChars(text) {
|
|
485
|
+
return Math.ceil(text.length / 4);
|
|
486
|
+
}
|
|
487
|
+
var DEFAULT_MAX_TOKENS = 8e3;
|
|
488
|
+
function tokenBudget(options = {}) {
|
|
489
|
+
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
490
|
+
const estimate = options.estimateTokens ?? estimateTokensByChars;
|
|
491
|
+
const severity = options.severity ?? "warning";
|
|
492
|
+
return {
|
|
493
|
+
id: "token-budget",
|
|
494
|
+
description: `Resolved prompt should fit within ~${maxTokens} estimated tokens.`,
|
|
495
|
+
scope: "resolved",
|
|
496
|
+
check(input) {
|
|
497
|
+
const result = input.result;
|
|
498
|
+
if (result === void 0) {
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
const tokens = estimate(result.text);
|
|
502
|
+
if (tokens <= maxTokens) {
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
return [
|
|
506
|
+
{
|
|
507
|
+
ruleId: "token-budget",
|
|
508
|
+
severity,
|
|
509
|
+
message: `estimated ${tokens} tokens exceeds budget of ${maxTokens}`
|
|
510
|
+
}
|
|
511
|
+
];
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// dist/lint/rules/unused-fragment.js
|
|
517
|
+
function unusedFragment(options = {}) {
|
|
518
|
+
const severity = options.severity ?? "warning";
|
|
519
|
+
return {
|
|
520
|
+
id: "unused-fragment",
|
|
521
|
+
description: "Every fragment should be referenced by at least one composition or rule.",
|
|
522
|
+
scope: "book",
|
|
523
|
+
check(input) {
|
|
524
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
525
|
+
for (const reference of iterateReferences(input.book)) {
|
|
526
|
+
referenced.add(reference.id);
|
|
527
|
+
}
|
|
528
|
+
const findings = [];
|
|
529
|
+
for (const id of [...input.book.fragments.keys()].sort()) {
|
|
530
|
+
if (!referenced.has(id)) {
|
|
531
|
+
findings.push({
|
|
532
|
+
ruleId: "unused-fragment",
|
|
533
|
+
severity,
|
|
534
|
+
message: `fragment "${id}" is not referenced by any composition or rule`,
|
|
535
|
+
fragmentId: id
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return findings;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// dist/lint/rules/index.js
|
|
545
|
+
function defaultRules(options = {}) {
|
|
546
|
+
return [
|
|
547
|
+
tokenBudget(options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {}),
|
|
548
|
+
languageDirectivePosition(),
|
|
549
|
+
exampleBalance(),
|
|
550
|
+
bannedTokens(options.bannedTokens !== void 0 ? { tokens: options.bannedTokens } : {}),
|
|
551
|
+
unusedFragment(),
|
|
552
|
+
danglingReference(),
|
|
553
|
+
deadRule()
|
|
554
|
+
];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// dist/lint/lint.js
|
|
558
|
+
function lint(input, rules = defaultRules()) {
|
|
559
|
+
const findings = [];
|
|
560
|
+
for (const rule of rules) {
|
|
561
|
+
if (rule.scope === "resolved" && input.result === void 0) {
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
findings.push(...rule.check(input));
|
|
565
|
+
}
|
|
566
|
+
let errorCount = 0;
|
|
567
|
+
let warningCount = 0;
|
|
568
|
+
for (const finding of findings) {
|
|
569
|
+
if (finding.severity === "error") {
|
|
570
|
+
errorCount += 1;
|
|
571
|
+
} else if (finding.severity === "warning") {
|
|
572
|
+
warningCount += 1;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return { findings, errorCount, warningCount };
|
|
576
|
+
}
|
|
191
577
|
export {
|
|
578
|
+
estimateTokensByChars,
|
|
192
579
|
interpolate,
|
|
580
|
+
iterateReferences,
|
|
581
|
+
lint,
|
|
193
582
|
resolveBook
|
|
194
583
|
};
|
package/dist/edge.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Self-contained entrypoint for edge / Deno runtimes that only need
|
|
3
|
-
* an already-bundled book at request time (no folder
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* that file and pair it with a `promptbook bundle` book.
|
|
2
|
+
* Self-contained entrypoint for edge / Deno / browser runtimes that only need
|
|
3
|
+
* to resolve and lint an already-bundled book at request time (no folder
|
|
4
|
+
* loading). Its module graph touches no filesystem, YAML, or Node builtins, so
|
|
5
|
+
* `scripts/build-edge.mjs` bundles it to one portable ESM file with no
|
|
6
|
+
* external imports. Consumers (e.g. a Supabase edge function or a static
|
|
7
|
+
* Next.js demo) vendor that file and pair it with a `promptbook bundle` book.
|
|
8
8
|
*/
|
|
9
9
|
export { interpolate } from "./interpolate.js";
|
|
10
|
+
export { lint } from "./lint/lint.js";
|
|
11
|
+
export type { FragmentReference } from "./lint/references.js";
|
|
12
|
+
export { iterateReferences } from "./lint/references.js";
|
|
13
|
+
export { estimateTokensByChars } from "./lint/rules/token-budget.js";
|
|
14
|
+
export type { LintFinding, LintInput, LintReport, LintRule, LintScope, Severity } from "./lint/types.js";
|
|
10
15
|
export { resolveBook } from "./resolve-book.js";
|
|
11
|
-
export type { Context, PromptBook, ResolveResult, Trace } from "./types.js";
|
|
16
|
+
export type { Composition, Context, ContextValue, Fragment, PromptBook, ResolveResult, Rule, RuleAction, Trace, When, } from "./types.js";
|
|
12
17
|
//# sourceMappingURL=edge.d.ts.map
|
package/dist/edge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACzG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,WAAW,EACX,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,aAAa,EACb,IAAI,EACJ,UAAU,EACV,KAAK,EACL,IAAI,GACL,MAAM,YAAY,CAAC"}
|
package/dist/edge.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Self-contained entrypoint for edge / Deno runtimes that only need
|
|
3
|
-
* an already-bundled book at request time (no folder
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* that file and pair it with a `promptbook bundle` book.
|
|
2
|
+
* Self-contained entrypoint for edge / Deno / browser runtimes that only need
|
|
3
|
+
* to resolve and lint an already-bundled book at request time (no folder
|
|
4
|
+
* loading). Its module graph touches no filesystem, YAML, or Node builtins, so
|
|
5
|
+
* `scripts/build-edge.mjs` bundles it to one portable ESM file with no
|
|
6
|
+
* external imports. Consumers (e.g. a Supabase edge function or a static
|
|
7
|
+
* Next.js demo) vendor that file and pair it with a `promptbook bundle` book.
|
|
8
8
|
*/
|
|
9
9
|
export { interpolate } from "./interpolate.js";
|
|
10
|
+
export { lint } from "./lint/lint.js";
|
|
11
|
+
export { iterateReferences } from "./lint/references.js";
|
|
12
|
+
export { estimateTokensByChars } from "./lint/rules/token-budget.js";
|
|
10
13
|
export { resolveBook } from "./resolve-book.js";
|
|
11
14
|
//# sourceMappingURL=edge.js.map
|
package/dist/edge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
package/package.json
CHANGED
package/src/bundle.ts
CHANGED
|
@@ -95,10 +95,18 @@ function canonicalCodePrompt(codePrompt: CodePrompt): Record<string, unknown> {
|
|
|
95
95
|
return compact(ordered);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Emit `new Map([...])` with one `[key, value]` entry per line (entries pre-sorted).
|
|
100
|
+
*
|
|
101
|
+
* An empty map serializes to `new Map()` (not `new Map([])`): without entries to
|
|
102
|
+
* infer from, `new Map([])` widens to `Map<unknown, unknown>`, which is not
|
|
103
|
+
* assignable to `Map<string, T>` in a plain (annotation-free) module. `new Map()`
|
|
104
|
+
* infers `Map<any, any>`, which stays assignable, so plain bundles type-check
|
|
105
|
+
* against `PromptBook` even when a map is empty.
|
|
106
|
+
*/
|
|
99
107
|
function serializeMap<T>(entries: [string, T][], canonical: (value: T) => unknown): string {
|
|
100
108
|
if (entries.length === 0) {
|
|
101
|
-
return "new Map(
|
|
109
|
+
return "new Map()";
|
|
102
110
|
}
|
|
103
111
|
const lines = entries.map(
|
|
104
112
|
([key, value]) => ` [${JSON.stringify(key)}, ${JSON.stringify(canonical(value))}],`,
|
package/src/edge.ts
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Self-contained entrypoint for edge / Deno runtimes that only need
|
|
3
|
-
* an already-bundled book at request time (no folder
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* that file and pair it with a `promptbook bundle` book.
|
|
2
|
+
* Self-contained entrypoint for edge / Deno / browser runtimes that only need
|
|
3
|
+
* to resolve and lint an already-bundled book at request time (no folder
|
|
4
|
+
* loading). Its module graph touches no filesystem, YAML, or Node builtins, so
|
|
5
|
+
* `scripts/build-edge.mjs` bundles it to one portable ESM file with no
|
|
6
|
+
* external imports. Consumers (e.g. a Supabase edge function or a static
|
|
7
|
+
* Next.js demo) vendor that file and pair it with a `promptbook bundle` book.
|
|
8
8
|
*/
|
|
9
9
|
export { interpolate } from "./interpolate.js";
|
|
10
|
+
export { lint } from "./lint/lint.js";
|
|
11
|
+
export type { FragmentReference } from "./lint/references.js";
|
|
12
|
+
export { iterateReferences } from "./lint/references.js";
|
|
13
|
+
export { estimateTokensByChars } from "./lint/rules/token-budget.js";
|
|
14
|
+
export type { LintFinding, LintInput, LintReport, LintRule, LintScope, Severity } from "./lint/types.js";
|
|
10
15
|
export { resolveBook } from "./resolve-book.js";
|
|
11
|
-
export type {
|
|
16
|
+
export type {
|
|
17
|
+
Composition,
|
|
18
|
+
Context,
|
|
19
|
+
ContextValue,
|
|
20
|
+
Fragment,
|
|
21
|
+
PromptBook,
|
|
22
|
+
ResolveResult,
|
|
23
|
+
Rule,
|
|
24
|
+
RuleAction,
|
|
25
|
+
Trace,
|
|
26
|
+
When,
|
|
27
|
+
} from "./types.js";
|