@promptscript/cli 1.0.0-alpha.9 → 1.0.0-rc.2
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/CHANGELOG.md +21 -0
- package/index.js +710 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.0-rc.2](https://github.com/mrwogu/promptscript/compare/v1.0.0-rc.1...v1.0.0-rc.2) (2026-02-01)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### chore
|
|
12
|
+
|
|
13
|
+
* prepare rc release ([87b9a04](https://github.com/mrwogu/promptscript/commit/87b9a0494e2c220c0c9cc226fc751c09613494ea))
|
|
14
|
+
|
|
15
|
+
## [1.0.0-rc.1](https://github.com/mrwogu/promptscript/compare/v1.0.0-alpha.10...v1.0.0-rc.1) (2026-02-01)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### chore
|
|
19
|
+
|
|
20
|
+
* prepare rc release ([756f820](https://github.com/mrwogu/promptscript/commit/756f82096a2a511e6a88aa3b45a56c92c3fab68c))
|
|
21
|
+
|
|
22
|
+
## [1.0.0-alpha.10](https://github.com/mrwogu/promptscript/compare/v1.0.0-alpha.9...v1.0.0-alpha.10) (2026-01-31)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### chore
|
|
26
|
+
|
|
27
|
+
* prepare alpha release ([c6595b6](https://github.com/mrwogu/promptscript/commit/c6595b6fd639f93f0093c503e8468fbbbf057cf9))
|
|
28
|
+
|
|
8
29
|
## [1.0.0-alpha.9](https://github.com/mrwogu/promptscript/compare/v1.0.0-alpha.8...v1.0.0-alpha.9) (2026-01-30)
|
|
9
30
|
|
|
10
31
|
|
package/index.js
CHANGED
|
@@ -171,6 +171,79 @@ var CircularDependencyError = class extends ResolveError {
|
|
|
171
171
|
}
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
+
// packages/core/src/errors/template.ts
|
|
175
|
+
var MissingParamError = class extends PSError {
|
|
176
|
+
/** Name of the missing parameter */
|
|
177
|
+
paramName;
|
|
178
|
+
/** Path of the template file */
|
|
179
|
+
templatePath;
|
|
180
|
+
constructor(paramName, templatePath, options) {
|
|
181
|
+
super(
|
|
182
|
+
`Missing required parameter '${paramName}' for template '${templatePath}'`,
|
|
183
|
+
"PS2010" /* MISSING_PARAM */,
|
|
184
|
+
options
|
|
185
|
+
);
|
|
186
|
+
this.name = "MissingParamError";
|
|
187
|
+
this.paramName = paramName;
|
|
188
|
+
this.templatePath = templatePath;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
var UnknownParamError = class extends PSError {
|
|
192
|
+
/** Name of the unknown parameter */
|
|
193
|
+
paramName;
|
|
194
|
+
/** Path of the template file */
|
|
195
|
+
templatePath;
|
|
196
|
+
/** Available parameter names */
|
|
197
|
+
availableParams;
|
|
198
|
+
constructor(paramName, templatePath, availableParams, options) {
|
|
199
|
+
const suggestion = availableParams.length > 0 ? `. Available parameters: ${availableParams.join(", ")}` : ". This template has no parameters.";
|
|
200
|
+
super(
|
|
201
|
+
`Unknown parameter '${paramName}' for template '${templatePath}'${suggestion}`,
|
|
202
|
+
"PS2011" /* UNKNOWN_PARAM */,
|
|
203
|
+
options
|
|
204
|
+
);
|
|
205
|
+
this.name = "UnknownParamError";
|
|
206
|
+
this.paramName = paramName;
|
|
207
|
+
this.templatePath = templatePath;
|
|
208
|
+
this.availableParams = availableParams;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
var ParamTypeMismatchError = class extends PSError {
|
|
212
|
+
/** Name of the parameter */
|
|
213
|
+
paramName;
|
|
214
|
+
/** Expected type */
|
|
215
|
+
expectedType;
|
|
216
|
+
/** Actual type */
|
|
217
|
+
actualType;
|
|
218
|
+
constructor(paramName, expectedType, actualType, options) {
|
|
219
|
+
super(
|
|
220
|
+
`Type mismatch for parameter '${paramName}': expected ${expectedType}, got ${actualType}`,
|
|
221
|
+
"PS2012" /* PARAM_TYPE_MISMATCH */,
|
|
222
|
+
options
|
|
223
|
+
);
|
|
224
|
+
this.name = "ParamTypeMismatchError";
|
|
225
|
+
this.paramName = paramName;
|
|
226
|
+
this.expectedType = expectedType;
|
|
227
|
+
this.actualType = actualType;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
var UndefinedVariableError = class extends PSError {
|
|
231
|
+
/** Name of the undefined variable */
|
|
232
|
+
variableName;
|
|
233
|
+
/** File where the variable was used */
|
|
234
|
+
sourceFile;
|
|
235
|
+
constructor(variableName, sourceFile, options) {
|
|
236
|
+
super(
|
|
237
|
+
`Undefined template variable '{{${variableName}}}' in '${sourceFile}'`,
|
|
238
|
+
"PS2013" /* UNDEFINED_VARIABLE */,
|
|
239
|
+
options
|
|
240
|
+
);
|
|
241
|
+
this.name = "UndefinedVariableError";
|
|
242
|
+
this.variableName = variableName;
|
|
243
|
+
this.sourceFile = sourceFile;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
174
247
|
// packages/core/src/utils/merge.ts
|
|
175
248
|
var DEFAULT_MERGE_OPTIONS = {
|
|
176
249
|
arrayStrategy: "unique",
|
|
@@ -304,6 +377,219 @@ var noopLogger = {
|
|
|
304
377
|
}
|
|
305
378
|
};
|
|
306
379
|
|
|
380
|
+
// packages/core/src/template.ts
|
|
381
|
+
function paramTypeToString(paramType) {
|
|
382
|
+
switch (paramType.kind) {
|
|
383
|
+
case "string":
|
|
384
|
+
return "string";
|
|
385
|
+
case "number":
|
|
386
|
+
return "number";
|
|
387
|
+
case "boolean":
|
|
388
|
+
return "boolean";
|
|
389
|
+
case "enum":
|
|
390
|
+
return `enum(${paramType.options.map((o) => `"${o}"`).join(", ")})`;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function getValueType(value) {
|
|
394
|
+
if (value === null) return "null";
|
|
395
|
+
if (typeof value === "string") return "string";
|
|
396
|
+
if (typeof value === "number") return "number";
|
|
397
|
+
if (typeof value === "boolean") return "boolean";
|
|
398
|
+
if (Array.isArray(value)) return "array";
|
|
399
|
+
if (typeof value === "object" && "type" in value) {
|
|
400
|
+
const typed = value;
|
|
401
|
+
if (typed.type === "TextContent") return "string";
|
|
402
|
+
if (typed.type === "TemplateExpression") return "template";
|
|
403
|
+
}
|
|
404
|
+
return "object";
|
|
405
|
+
}
|
|
406
|
+
function validateParamType(paramName, value, expectedType, location) {
|
|
407
|
+
const actualType = getValueType(value);
|
|
408
|
+
const expected = paramTypeToString(expectedType);
|
|
409
|
+
switch (expectedType.kind) {
|
|
410
|
+
case "string":
|
|
411
|
+
if (actualType !== "string") {
|
|
412
|
+
throw new ParamTypeMismatchError(paramName, expected, actualType, { location });
|
|
413
|
+
}
|
|
414
|
+
break;
|
|
415
|
+
case "number":
|
|
416
|
+
if (actualType !== "number") {
|
|
417
|
+
throw new ParamTypeMismatchError(paramName, expected, actualType, { location });
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
case "boolean":
|
|
421
|
+
if (actualType !== "boolean") {
|
|
422
|
+
throw new ParamTypeMismatchError(paramName, expected, actualType, { location });
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
case "enum":
|
|
426
|
+
if (actualType !== "string" || !expectedType.options.includes(value)) {
|
|
427
|
+
throw new ParamTypeMismatchError(paramName, expected, String(value), { location });
|
|
428
|
+
}
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function bindParams(args, defs, templatePath, callLocation) {
|
|
433
|
+
const result = /* @__PURE__ */ new Map();
|
|
434
|
+
if (!defs || defs.length === 0) {
|
|
435
|
+
if (args && args.length > 0 && args[0] !== void 0) {
|
|
436
|
+
throw new UnknownParamError(args[0].name, templatePath, [], { location: callLocation });
|
|
437
|
+
}
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
const defMap = /* @__PURE__ */ new Map();
|
|
441
|
+
for (const def of defs) {
|
|
442
|
+
defMap.set(def.name, def);
|
|
443
|
+
}
|
|
444
|
+
const providedNames = /* @__PURE__ */ new Set();
|
|
445
|
+
if (args) {
|
|
446
|
+
for (const arg of args) {
|
|
447
|
+
const def = defMap.get(arg.name);
|
|
448
|
+
if (!def) {
|
|
449
|
+
throw new UnknownParamError(
|
|
450
|
+
arg.name,
|
|
451
|
+
templatePath,
|
|
452
|
+
defs.map((d) => d.name),
|
|
453
|
+
{ location: arg.loc }
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
validateParamType(arg.name, arg.value, def.paramType, arg.loc);
|
|
457
|
+
result.set(arg.name, arg.value);
|
|
458
|
+
providedNames.add(arg.name);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
for (const def of defs) {
|
|
462
|
+
if (!providedNames.has(def.name)) {
|
|
463
|
+
if (def.defaultValue !== void 0) {
|
|
464
|
+
result.set(def.name, def.defaultValue);
|
|
465
|
+
} else if (!def.optional) {
|
|
466
|
+
throw new MissingParamError(def.name, templatePath, { location: callLocation });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
function valueToString(value) {
|
|
473
|
+
if (value === null) return "null";
|
|
474
|
+
if (typeof value === "string") return value;
|
|
475
|
+
if (typeof value === "number") return String(value);
|
|
476
|
+
if (typeof value === "boolean") return String(value);
|
|
477
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
478
|
+
if (typeof value === "object" && "type" in value) {
|
|
479
|
+
const typed = value;
|
|
480
|
+
if (typed.type === "TextContent") {
|
|
481
|
+
return value.value;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return JSON.stringify(value);
|
|
485
|
+
}
|
|
486
|
+
function interpolateText(text, ctx) {
|
|
487
|
+
const pattern = /\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g;
|
|
488
|
+
return text.replace(pattern, (_match, varName) => {
|
|
489
|
+
const value = ctx.params.get(varName);
|
|
490
|
+
if (value === void 0) {
|
|
491
|
+
throw new UndefinedVariableError(varName, ctx.sourceFile);
|
|
492
|
+
}
|
|
493
|
+
return valueToString(value);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
function interpolateValue(value, ctx) {
|
|
497
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
498
|
+
return value;
|
|
499
|
+
}
|
|
500
|
+
if (Array.isArray(value)) {
|
|
501
|
+
return value.map((v) => interpolateValue(v, ctx));
|
|
502
|
+
}
|
|
503
|
+
if (typeof value === "object" && "type" in value) {
|
|
504
|
+
const typed = value;
|
|
505
|
+
if (typed.type === "TemplateExpression") {
|
|
506
|
+
const expr = value;
|
|
507
|
+
const resolved = ctx.params.get(expr.name);
|
|
508
|
+
if (resolved === void 0) {
|
|
509
|
+
throw new UndefinedVariableError(expr.name, ctx.sourceFile, { location: expr.loc });
|
|
510
|
+
}
|
|
511
|
+
return resolved;
|
|
512
|
+
}
|
|
513
|
+
if (typed.type === "TextContent") {
|
|
514
|
+
const text = value;
|
|
515
|
+
return {
|
|
516
|
+
...text,
|
|
517
|
+
value: interpolateText(text.value, ctx)
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
if (typed.type === "TypeExpression") {
|
|
521
|
+
return value;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (typeof value === "object") {
|
|
525
|
+
const result = {};
|
|
526
|
+
for (const [key, val] of Object.entries(value)) {
|
|
527
|
+
result[key] = interpolateValue(val, ctx);
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
531
|
+
return value;
|
|
532
|
+
}
|
|
533
|
+
function interpolateContent(content, ctx) {
|
|
534
|
+
switch (content.type) {
|
|
535
|
+
case "TextContent":
|
|
536
|
+
return {
|
|
537
|
+
...content,
|
|
538
|
+
value: interpolateText(content.value, ctx)
|
|
539
|
+
};
|
|
540
|
+
case "ObjectContent":
|
|
541
|
+
return {
|
|
542
|
+
...content,
|
|
543
|
+
properties: interpolateProperties(content.properties, ctx)
|
|
544
|
+
};
|
|
545
|
+
case "ArrayContent":
|
|
546
|
+
return {
|
|
547
|
+
...content,
|
|
548
|
+
elements: content.elements.map((e) => interpolateValue(e, ctx))
|
|
549
|
+
};
|
|
550
|
+
case "MixedContent":
|
|
551
|
+
return {
|
|
552
|
+
...content,
|
|
553
|
+
text: content.text ? { ...content.text, value: interpolateText(content.text.value, ctx) } : void 0,
|
|
554
|
+
properties: interpolateProperties(content.properties, ctx)
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function interpolateProperties(properties, ctx) {
|
|
559
|
+
const result = {};
|
|
560
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
561
|
+
result[key] = interpolateValue(value, ctx);
|
|
562
|
+
}
|
|
563
|
+
return result;
|
|
564
|
+
}
|
|
565
|
+
function interpolateBlock(block, ctx) {
|
|
566
|
+
return {
|
|
567
|
+
...block,
|
|
568
|
+
content: interpolateContent(block.content, ctx)
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function interpolateAST(ast, ctx) {
|
|
572
|
+
if (ctx.params.size === 0) {
|
|
573
|
+
return ast;
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
...ast,
|
|
577
|
+
// Interpolate meta fields (but not params definitions)
|
|
578
|
+
meta: ast.meta ? {
|
|
579
|
+
...ast.meta,
|
|
580
|
+
fields: interpolateProperties(ast.meta.fields, ctx)
|
|
581
|
+
// Keep params as-is (they're definitions, not values to interpolate)
|
|
582
|
+
} : void 0,
|
|
583
|
+
// Interpolate blocks
|
|
584
|
+
blocks: ast.blocks.map((b) => interpolateBlock(b, ctx)),
|
|
585
|
+
// Interpolate extend blocks
|
|
586
|
+
extends: ast.extends.map((e) => ({
|
|
587
|
+
...e,
|
|
588
|
+
content: interpolateContent(e.content, ctx)
|
|
589
|
+
}))
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
|
|
307
593
|
// packages/cli/src/commands/init.ts
|
|
308
594
|
import { fileURLToPath } from "url";
|
|
309
595
|
import { dirname as dirname3 } from "path";
|
|
@@ -15215,6 +15501,29 @@ var Enum = createToken({
|
|
|
15215
15501
|
pattern: /enum/,
|
|
15216
15502
|
longer_alt: Identifier
|
|
15217
15503
|
});
|
|
15504
|
+
var StringType = createToken({
|
|
15505
|
+
name: "StringType",
|
|
15506
|
+
pattern: /string/,
|
|
15507
|
+
longer_alt: Identifier
|
|
15508
|
+
});
|
|
15509
|
+
var NumberType = createToken({
|
|
15510
|
+
name: "NumberType",
|
|
15511
|
+
pattern: /number/,
|
|
15512
|
+
longer_alt: Identifier
|
|
15513
|
+
});
|
|
15514
|
+
var BooleanType = createToken({
|
|
15515
|
+
name: "BooleanType",
|
|
15516
|
+
pattern: /boolean/,
|
|
15517
|
+
longer_alt: Identifier
|
|
15518
|
+
});
|
|
15519
|
+
var TemplateOpen = createToken({
|
|
15520
|
+
name: "TemplateOpen",
|
|
15521
|
+
pattern: /\{\{/
|
|
15522
|
+
});
|
|
15523
|
+
var TemplateClose = createToken({
|
|
15524
|
+
name: "TemplateClose",
|
|
15525
|
+
pattern: /\}\}/
|
|
15526
|
+
});
|
|
15218
15527
|
var At = createToken({
|
|
15219
15528
|
name: "At",
|
|
15220
15529
|
pattern: /@/
|
|
@@ -15288,6 +15597,9 @@ var allTokens = [
|
|
|
15288
15597
|
// Paths (must be before DotDot to handle ../)
|
|
15289
15598
|
PathReference,
|
|
15290
15599
|
RelativePath,
|
|
15600
|
+
// Template expression tokens (multi-char, before single braces)
|
|
15601
|
+
TemplateOpen,
|
|
15602
|
+
TemplateClose,
|
|
15291
15603
|
// Multi-char symbols before single-char
|
|
15292
15604
|
DotDot,
|
|
15293
15605
|
// Keywords before Identifier
|
|
@@ -15301,6 +15613,9 @@ var allTokens = [
|
|
|
15301
15613
|
Null,
|
|
15302
15614
|
Range,
|
|
15303
15615
|
Enum,
|
|
15616
|
+
StringType,
|
|
15617
|
+
NumberType,
|
|
15618
|
+
BooleanType,
|
|
15304
15619
|
// Single-char symbols
|
|
15305
15620
|
At,
|
|
15306
15621
|
LBrace,
|
|
@@ -15366,22 +15681,24 @@ var PromptScriptParser = class extends CstParser {
|
|
|
15366
15681
|
});
|
|
15367
15682
|
/**
|
|
15368
15683
|
* inheritDecl
|
|
15369
|
-
* : '@' 'inherit' pathRef
|
|
15684
|
+
* : '@' 'inherit' pathRef paramCallList?
|
|
15370
15685
|
*/
|
|
15371
15686
|
inheritDecl = this.RULE("inheritDecl", () => {
|
|
15372
15687
|
this.CONSUME(At);
|
|
15373
15688
|
this.CONSUME(Inherit);
|
|
15374
15689
|
this.SUBRULE(this.pathRef);
|
|
15690
|
+
this.OPTION(() => this.SUBRULE(this.paramCallList));
|
|
15375
15691
|
});
|
|
15376
15692
|
/**
|
|
15377
15693
|
* useDecl
|
|
15378
|
-
* : '@' 'use' pathRef ('as' Identifier)?
|
|
15694
|
+
* : '@' 'use' pathRef paramCallList? ('as' Identifier)?
|
|
15379
15695
|
*/
|
|
15380
15696
|
useDecl = this.RULE("useDecl", () => {
|
|
15381
15697
|
this.CONSUME(At);
|
|
15382
15698
|
this.CONSUME(Use);
|
|
15383
15699
|
this.SUBRULE(this.pathRef);
|
|
15384
|
-
this.OPTION(() =>
|
|
15700
|
+
this.OPTION(() => this.SUBRULE(this.paramCallList));
|
|
15701
|
+
this.OPTION2(() => {
|
|
15385
15702
|
this.CONSUME(As);
|
|
15386
15703
|
this.CONSUME(Identifier);
|
|
15387
15704
|
});
|
|
@@ -15432,10 +15749,18 @@ var PromptScriptParser = class extends CstParser {
|
|
|
15432
15749
|
});
|
|
15433
15750
|
/**
|
|
15434
15751
|
* field
|
|
15435
|
-
* : (Identifier | StringLiteral) '?'? ':' value ('=' value)?
|
|
15752
|
+
* : (Identifier | StringLiteral | StringType | NumberType | BooleanType) '?'? ':' value ('=' value)?
|
|
15753
|
+
*
|
|
15754
|
+
* Note: StringType/NumberType/BooleanType are also valid field keys (e.g., { string: "value" })
|
|
15436
15755
|
*/
|
|
15437
15756
|
field = this.RULE("field", () => {
|
|
15438
|
-
this.OR([
|
|
15757
|
+
this.OR([
|
|
15758
|
+
{ ALT: () => this.CONSUME(Identifier) },
|
|
15759
|
+
{ ALT: () => this.CONSUME(StringLiteral) },
|
|
15760
|
+
{ ALT: () => this.CONSUME(StringType) },
|
|
15761
|
+
{ ALT: () => this.CONSUME(NumberType) },
|
|
15762
|
+
{ ALT: () => this.CONSUME(BooleanType) }
|
|
15763
|
+
]);
|
|
15439
15764
|
this.OPTION(() => this.CONSUME(Question));
|
|
15440
15765
|
this.CONSUME(Colon);
|
|
15441
15766
|
this.SUBRULE(this.value);
|
|
@@ -15447,7 +15772,10 @@ var PromptScriptParser = class extends CstParser {
|
|
|
15447
15772
|
/**
|
|
15448
15773
|
* value
|
|
15449
15774
|
* : StringLiteral | NumberLiteral | True | False | Null
|
|
15450
|
-
* | TextBlock | array | object | typeExpr | Identifier
|
|
15775
|
+
* | TextBlock | array | paramDefList | object | typeExpr | templateExpr | Identifier
|
|
15776
|
+
*
|
|
15777
|
+
* Note: paramDefList must come before object since both start with LBrace,
|
|
15778
|
+
* and paramDefList is more specific (contains paramType like 'string', 'number', 'boolean').
|
|
15451
15779
|
*/
|
|
15452
15780
|
value = this.RULE("value", () => {
|
|
15453
15781
|
this.OR([
|
|
@@ -15458,11 +15786,42 @@ var PromptScriptParser = class extends CstParser {
|
|
|
15458
15786
|
{ ALT: () => this.CONSUME(Null) },
|
|
15459
15787
|
{ ALT: () => this.CONSUME(TextBlock) },
|
|
15460
15788
|
{ ALT: () => this.SUBRULE(this.array) },
|
|
15789
|
+
{ ALT: () => this.SUBRULE(this.paramDefList), GATE: () => this.isParamDefListAhead() },
|
|
15461
15790
|
{ ALT: () => this.SUBRULE(this.object) },
|
|
15462
15791
|
{ ALT: () => this.SUBRULE(this.typeExpr) },
|
|
15792
|
+
{ ALT: () => this.SUBRULE(this.templateExpr) },
|
|
15463
15793
|
{ ALT: () => this.CONSUME(Identifier) }
|
|
15464
15794
|
]);
|
|
15465
15795
|
});
|
|
15796
|
+
/**
|
|
15797
|
+
* Check if we're about to parse a paramDefList.
|
|
15798
|
+
* A paramDefList starts with '{' and contains paramDef entries like 'name: string'.
|
|
15799
|
+
*/
|
|
15800
|
+
isParamDefListAhead() {
|
|
15801
|
+
const tokens = this.LA(1);
|
|
15802
|
+
if (!tokens || tokens.tokenType?.name !== "LBrace") return false;
|
|
15803
|
+
const second = this.LA(2);
|
|
15804
|
+
if (!second || second.tokenType?.name !== "Identifier") return false;
|
|
15805
|
+
const third = this.LA(3);
|
|
15806
|
+
if (!third) return false;
|
|
15807
|
+
if (third.tokenType?.name === "Colon") {
|
|
15808
|
+
const fourth = this.LA(4);
|
|
15809
|
+
if (!fourth) return false;
|
|
15810
|
+
return ["StringType", "NumberType", "BooleanType", "Enum"].includes(
|
|
15811
|
+
fourth.tokenType?.name ?? ""
|
|
15812
|
+
);
|
|
15813
|
+
}
|
|
15814
|
+
if (third.tokenType?.name === "Question") {
|
|
15815
|
+
const fourth = this.LA(4);
|
|
15816
|
+
if (!fourth || fourth.tokenType?.name !== "Colon") return false;
|
|
15817
|
+
const fifth = this.LA(5);
|
|
15818
|
+
if (!fifth) return false;
|
|
15819
|
+
return ["StringType", "NumberType", "BooleanType", "Enum"].includes(
|
|
15820
|
+
fifth.tokenType?.name ?? ""
|
|
15821
|
+
);
|
|
15822
|
+
}
|
|
15823
|
+
return false;
|
|
15824
|
+
}
|
|
15466
15825
|
/**
|
|
15467
15826
|
* array
|
|
15468
15827
|
* : '[' (value (',' value?)*)? ']'
|
|
@@ -15547,14 +15906,90 @@ var PromptScriptParser = class extends CstParser {
|
|
|
15547
15906
|
this.CONSUME2(Identifier);
|
|
15548
15907
|
});
|
|
15549
15908
|
});
|
|
15909
|
+
// ============================================================
|
|
15910
|
+
// Template Parameter Rules
|
|
15911
|
+
// ============================================================
|
|
15912
|
+
/**
|
|
15913
|
+
* paramCallList (for @inherit/@use calls)
|
|
15914
|
+
* : '(' (paramArg (',' paramArg)*)? ')'
|
|
15915
|
+
*/
|
|
15916
|
+
paramCallList = this.RULE("paramCallList", () => {
|
|
15917
|
+
this.CONSUME(LParen);
|
|
15918
|
+
this.OPTION(() => {
|
|
15919
|
+
this.SUBRULE(this.paramArg);
|
|
15920
|
+
this.MANY(() => {
|
|
15921
|
+
this.CONSUME(Comma);
|
|
15922
|
+
this.SUBRULE2(this.paramArg);
|
|
15923
|
+
});
|
|
15924
|
+
});
|
|
15925
|
+
this.CONSUME(RParen);
|
|
15926
|
+
});
|
|
15927
|
+
/**
|
|
15928
|
+
* paramArg
|
|
15929
|
+
* : Identifier ':' value
|
|
15930
|
+
*/
|
|
15931
|
+
paramArg = this.RULE("paramArg", () => {
|
|
15932
|
+
this.CONSUME(Identifier);
|
|
15933
|
+
this.CONSUME(Colon);
|
|
15934
|
+
this.SUBRULE(this.value);
|
|
15935
|
+
});
|
|
15936
|
+
/**
|
|
15937
|
+
* paramDefList (for @meta { params: {...} })
|
|
15938
|
+
* : '{' (paramDef ','?)* '}'
|
|
15939
|
+
*/
|
|
15940
|
+
paramDefList = this.RULE("paramDefList", () => {
|
|
15941
|
+
this.CONSUME(LBrace);
|
|
15942
|
+
this.MANY(() => {
|
|
15943
|
+
this.SUBRULE(this.paramDef);
|
|
15944
|
+
this.OPTION(() => this.CONSUME(Comma));
|
|
15945
|
+
});
|
|
15946
|
+
this.CONSUME(RBrace);
|
|
15947
|
+
});
|
|
15948
|
+
/**
|
|
15949
|
+
* paramDef
|
|
15950
|
+
* : Identifier '?'? ':' paramType ('=' value)?
|
|
15951
|
+
*/
|
|
15952
|
+
paramDef = this.RULE("paramDef", () => {
|
|
15953
|
+
this.CONSUME(Identifier);
|
|
15954
|
+
this.OPTION(() => this.CONSUME(Question));
|
|
15955
|
+
this.CONSUME(Colon);
|
|
15956
|
+
this.SUBRULE(this.paramType);
|
|
15957
|
+
this.OPTION2(() => {
|
|
15958
|
+
this.CONSUME(Equals);
|
|
15959
|
+
this.SUBRULE(this.value);
|
|
15960
|
+
});
|
|
15961
|
+
});
|
|
15962
|
+
/**
|
|
15963
|
+
* paramType
|
|
15964
|
+
* : 'string' | 'number' | 'boolean' | enumType
|
|
15965
|
+
*/
|
|
15966
|
+
paramType = this.RULE("paramType", () => {
|
|
15967
|
+
this.OR([
|
|
15968
|
+
{ ALT: () => this.CONSUME(StringType) },
|
|
15969
|
+
{ ALT: () => this.CONSUME(NumberType) },
|
|
15970
|
+
{ ALT: () => this.CONSUME(BooleanType) },
|
|
15971
|
+
{ ALT: () => this.SUBRULE(this.enumType) }
|
|
15972
|
+
]);
|
|
15973
|
+
});
|
|
15974
|
+
/**
|
|
15975
|
+
* templateExpr
|
|
15976
|
+
* : '{{' Identifier '}}'
|
|
15977
|
+
*/
|
|
15978
|
+
templateExpr = this.RULE("templateExpr", () => {
|
|
15979
|
+
this.CONSUME(TemplateOpen);
|
|
15980
|
+
this.CONSUME(Identifier);
|
|
15981
|
+
this.CONSUME(TemplateClose);
|
|
15982
|
+
});
|
|
15550
15983
|
};
|
|
15551
15984
|
var parser = new PromptScriptParser();
|
|
15552
15985
|
|
|
15553
15986
|
// packages/parser/src/grammar/visitor.ts
|
|
15554
15987
|
var BaseVisitor = parser.getBaseCstVisitorConstructor();
|
|
15988
|
+
var defaultEnvProvider = (name) => process.env[name];
|
|
15555
15989
|
var PromptScriptVisitor = class extends BaseVisitor {
|
|
15556
15990
|
filename = "<unknown>";
|
|
15557
15991
|
interpolateEnv = false;
|
|
15992
|
+
envProvider = defaultEnvProvider;
|
|
15558
15993
|
constructor() {
|
|
15559
15994
|
super();
|
|
15560
15995
|
this.validateVisitor();
|
|
@@ -15571,6 +16006,19 @@ var PromptScriptVisitor = class extends BaseVisitor {
|
|
|
15571
16006
|
setInterpolateEnv(enabled) {
|
|
15572
16007
|
this.interpolateEnv = enabled;
|
|
15573
16008
|
}
|
|
16009
|
+
/**
|
|
16010
|
+
* Set a custom environment provider function.
|
|
16011
|
+
* Use this to provide environment variables from sources other than process.env.
|
|
16012
|
+
*/
|
|
16013
|
+
setEnvProvider(provider) {
|
|
16014
|
+
this.envProvider = provider;
|
|
16015
|
+
}
|
|
16016
|
+
/**
|
|
16017
|
+
* Reset the environment provider to the default (process.env).
|
|
16018
|
+
*/
|
|
16019
|
+
resetEnvProvider() {
|
|
16020
|
+
this.envProvider = defaultEnvProvider;
|
|
16021
|
+
}
|
|
15574
16022
|
/**
|
|
15575
16023
|
* Create source location from a token.
|
|
15576
16024
|
*/
|
|
@@ -15617,27 +16065,40 @@ var PromptScriptVisitor = class extends BaseVisitor {
|
|
|
15617
16065
|
*/
|
|
15618
16066
|
metaBlock(ctx) {
|
|
15619
16067
|
const fields = {};
|
|
16068
|
+
let params;
|
|
15620
16069
|
if (ctx.field) {
|
|
15621
16070
|
for (const fieldNode of ctx.field) {
|
|
15622
|
-
const { name, value } = this.visit(fieldNode);
|
|
15623
|
-
|
|
16071
|
+
const { name, value, isParamsDef, paramsDefs } = this.visit(fieldNode);
|
|
16072
|
+
if (isParamsDef && paramsDefs) {
|
|
16073
|
+
params = paramsDefs;
|
|
16074
|
+
} else {
|
|
16075
|
+
fields[name] = value;
|
|
16076
|
+
}
|
|
15624
16077
|
}
|
|
15625
16078
|
}
|
|
15626
|
-
|
|
16079
|
+
const meta = {
|
|
15627
16080
|
type: "MetaBlock",
|
|
15628
16081
|
fields,
|
|
15629
16082
|
loc: this.loc(ctx.At[0])
|
|
15630
16083
|
};
|
|
16084
|
+
if (params) {
|
|
16085
|
+
meta.params = params;
|
|
16086
|
+
}
|
|
16087
|
+
return meta;
|
|
15631
16088
|
}
|
|
15632
16089
|
/**
|
|
15633
16090
|
* inheritDecl → InheritDeclaration
|
|
15634
16091
|
*/
|
|
15635
16092
|
inheritDecl(ctx) {
|
|
15636
|
-
|
|
16093
|
+
const inherit = {
|
|
15637
16094
|
type: "InheritDeclaration",
|
|
15638
16095
|
path: this.visit(ctx.pathRef[0]),
|
|
15639
16096
|
loc: this.loc(ctx.At[0])
|
|
15640
16097
|
};
|
|
16098
|
+
if (ctx.paramCallList) {
|
|
16099
|
+
inherit.params = this.visit(ctx.paramCallList[0]);
|
|
16100
|
+
}
|
|
16101
|
+
return inherit;
|
|
15641
16102
|
}
|
|
15642
16103
|
/**
|
|
15643
16104
|
* useDecl → UseDeclaration
|
|
@@ -15648,6 +16109,9 @@ var PromptScriptVisitor = class extends BaseVisitor {
|
|
|
15648
16109
|
path: this.visit(ctx.pathRef[0]),
|
|
15649
16110
|
loc: this.loc(ctx.At[0])
|
|
15650
16111
|
};
|
|
16112
|
+
if (ctx.paramCallList) {
|
|
16113
|
+
use.params = this.visit(ctx.paramCallList[0]);
|
|
16114
|
+
}
|
|
15651
16115
|
if (ctx.Identifier) {
|
|
15652
16116
|
use.alias = ctx.Identifier[0].image;
|
|
15653
16117
|
}
|
|
@@ -15769,20 +16233,31 @@ var PromptScriptVisitor = class extends BaseVisitor {
|
|
|
15769
16233
|
return this.parseStringLiteral(token.image);
|
|
15770
16234
|
}
|
|
15771
16235
|
/**
|
|
15772
|
-
* field → { name, value, optional, defaultValue }
|
|
16236
|
+
* field → { name, value, optional, defaultValue, isParamsDef?, paramsDefs? }
|
|
15773
16237
|
*/
|
|
15774
16238
|
field(ctx) {
|
|
15775
16239
|
let name;
|
|
15776
16240
|
if (ctx.Identifier) {
|
|
15777
16241
|
name = ctx.Identifier[0].image;
|
|
15778
|
-
} else {
|
|
16242
|
+
} else if (ctx.StringLiteral) {
|
|
15779
16243
|
name = this.parseStringLiteral(ctx.StringLiteral[0].image);
|
|
16244
|
+
} else if (ctx.StringType) {
|
|
16245
|
+
name = "string";
|
|
16246
|
+
} else if (ctx.NumberType) {
|
|
16247
|
+
name = "number";
|
|
16248
|
+
} else if (ctx.BooleanType) {
|
|
16249
|
+
name = "boolean";
|
|
16250
|
+
} else {
|
|
16251
|
+
throw new Error("Unknown field key type");
|
|
15780
16252
|
}
|
|
15781
16253
|
const optional = ctx.Question ? true : void 0;
|
|
15782
16254
|
const values2 = ctx.value;
|
|
15783
|
-
const
|
|
16255
|
+
const valueResult = this.visit(values2[0]);
|
|
15784
16256
|
const defaultValue = values2.length > 1 ? this.visit(values2[1]) : void 0;
|
|
15785
|
-
|
|
16257
|
+
if (name === "params" && Array.isArray(valueResult) && valueResult.length > 0 && valueResult[0]?.type === "ParamDefinition") {
|
|
16258
|
+
return { name, value: {}, isParamsDef: true, paramsDefs: valueResult };
|
|
16259
|
+
}
|
|
16260
|
+
return { name, value: valueResult, optional, defaultValue };
|
|
15786
16261
|
}
|
|
15787
16262
|
/**
|
|
15788
16263
|
* value → Value
|
|
@@ -15819,12 +16294,18 @@ var PromptScriptVisitor = class extends BaseVisitor {
|
|
|
15819
16294
|
if (ctx.array) {
|
|
15820
16295
|
return this.visit(ctx.array[0]);
|
|
15821
16296
|
}
|
|
16297
|
+
if (ctx.paramDefList) {
|
|
16298
|
+
return this.visit(ctx.paramDefList[0]);
|
|
16299
|
+
}
|
|
15822
16300
|
if (ctx.object) {
|
|
15823
16301
|
return this.visit(ctx.object[0]);
|
|
15824
16302
|
}
|
|
15825
16303
|
if (ctx.typeExpr) {
|
|
15826
16304
|
return this.visit(ctx.typeExpr[0]);
|
|
15827
16305
|
}
|
|
16306
|
+
if (ctx.templateExpr) {
|
|
16307
|
+
return this.visit(ctx.templateExpr[0]);
|
|
16308
|
+
}
|
|
15828
16309
|
if (ctx.Identifier) {
|
|
15829
16310
|
return ctx.Identifier[0].image;
|
|
15830
16311
|
}
|
|
@@ -15903,6 +16384,87 @@ var PromptScriptVisitor = class extends BaseVisitor {
|
|
|
15903
16384
|
return ctx.Identifier.map((token) => token.image).join(".");
|
|
15904
16385
|
}
|
|
15905
16386
|
// ============================================================
|
|
16387
|
+
// Template Parameter Visitor Methods
|
|
16388
|
+
// ============================================================
|
|
16389
|
+
/**
|
|
16390
|
+
* paramCallList → ParamArgument[]
|
|
16391
|
+
*/
|
|
16392
|
+
paramCallList(ctx) {
|
|
16393
|
+
if (!ctx.paramArg) {
|
|
16394
|
+
return [];
|
|
16395
|
+
}
|
|
16396
|
+
return ctx.paramArg.map((node) => this.visit(node));
|
|
16397
|
+
}
|
|
16398
|
+
/**
|
|
16399
|
+
* paramArg → ParamArgument
|
|
16400
|
+
*/
|
|
16401
|
+
paramArg(ctx) {
|
|
16402
|
+
return {
|
|
16403
|
+
type: "ParamArgument",
|
|
16404
|
+
name: ctx.Identifier[0].image,
|
|
16405
|
+
value: this.visit(ctx.value[0]),
|
|
16406
|
+
loc: this.loc(ctx.Identifier[0])
|
|
16407
|
+
};
|
|
16408
|
+
}
|
|
16409
|
+
/**
|
|
16410
|
+
* paramDefList → ParamDefinition[]
|
|
16411
|
+
*/
|
|
16412
|
+
paramDefList(ctx) {
|
|
16413
|
+
if (!ctx.paramDef) {
|
|
16414
|
+
return [];
|
|
16415
|
+
}
|
|
16416
|
+
return ctx.paramDef.map((node) => this.visit(node));
|
|
16417
|
+
}
|
|
16418
|
+
/**
|
|
16419
|
+
* paramDef → ParamDefinition
|
|
16420
|
+
*/
|
|
16421
|
+
paramDef(ctx) {
|
|
16422
|
+
const name = ctx.Identifier[0].image;
|
|
16423
|
+
const optional = ctx.Question !== void 0;
|
|
16424
|
+
const paramType = this.visit(ctx.paramType[0]);
|
|
16425
|
+
const defaultValue = ctx.value ? this.visit(ctx.value[0]) : void 0;
|
|
16426
|
+
return {
|
|
16427
|
+
type: "ParamDefinition",
|
|
16428
|
+
name,
|
|
16429
|
+
paramType,
|
|
16430
|
+
optional: optional || defaultValue !== void 0,
|
|
16431
|
+
defaultValue,
|
|
16432
|
+
loc: this.loc(ctx.Identifier[0])
|
|
16433
|
+
};
|
|
16434
|
+
}
|
|
16435
|
+
/**
|
|
16436
|
+
* paramType → ParamType
|
|
16437
|
+
*/
|
|
16438
|
+
paramType(ctx) {
|
|
16439
|
+
if (ctx.StringType) {
|
|
16440
|
+
return { kind: "string" };
|
|
16441
|
+
}
|
|
16442
|
+
if (ctx.NumberType) {
|
|
16443
|
+
return { kind: "number" };
|
|
16444
|
+
}
|
|
16445
|
+
if (ctx.BooleanType) {
|
|
16446
|
+
return { kind: "boolean" };
|
|
16447
|
+
}
|
|
16448
|
+
if (ctx.enumType) {
|
|
16449
|
+
const enumExpr = this.visit(ctx.enumType[0]);
|
|
16450
|
+
return {
|
|
16451
|
+
kind: "enum",
|
|
16452
|
+
options: enumExpr.constraints?.options ?? []
|
|
16453
|
+
};
|
|
16454
|
+
}
|
|
16455
|
+
throw new Error("Unknown param type");
|
|
16456
|
+
}
|
|
16457
|
+
/**
|
|
16458
|
+
* templateExpr → TemplateExpression
|
|
16459
|
+
*/
|
|
16460
|
+
templateExpr(ctx) {
|
|
16461
|
+
return {
|
|
16462
|
+
type: "TemplateExpression",
|
|
16463
|
+
name: ctx.Identifier[0].image,
|
|
16464
|
+
loc: this.loc(ctx.TemplateOpen[0])
|
|
16465
|
+
};
|
|
16466
|
+
}
|
|
16467
|
+
// ============================================================
|
|
15906
16468
|
// Helper Methods
|
|
15907
16469
|
// ============================================================
|
|
15908
16470
|
/**
|
|
@@ -15924,7 +16486,7 @@ var PromptScriptVisitor = class extends BaseVisitor {
|
|
|
15924
16486
|
interpolateEnvVars(text) {
|
|
15925
16487
|
const envVarPattern = /\$\{([A-Za-z_]\w*)(?::-([^}]*))?\}/g;
|
|
15926
16488
|
return text.replace(envVarPattern, (_match, varName, defaultValue) => {
|
|
15927
|
-
const envValue =
|
|
16489
|
+
const envValue = this.envProvider(varName);
|
|
15928
16490
|
if (envValue !== void 0) {
|
|
15929
16491
|
return envValue;
|
|
15930
16492
|
}
|
|
@@ -15983,7 +16545,8 @@ function parse(source, options = {}) {
|
|
|
15983
16545
|
filename = "<unknown>",
|
|
15984
16546
|
tolerant = false,
|
|
15985
16547
|
recovery = false,
|
|
15986
|
-
interpolateEnv = false
|
|
16548
|
+
interpolateEnv = false,
|
|
16549
|
+
envProvider
|
|
15987
16550
|
} = options;
|
|
15988
16551
|
const isRecoveryMode = tolerant || recovery;
|
|
15989
16552
|
const errors = [];
|
|
@@ -16018,6 +16581,11 @@ function parse(source, options = {}) {
|
|
|
16018
16581
|
}
|
|
16019
16582
|
try {
|
|
16020
16583
|
visitor.setInterpolateEnv(interpolateEnv);
|
|
16584
|
+
if (envProvider) {
|
|
16585
|
+
visitor.setEnvProvider(envProvider);
|
|
16586
|
+
} else {
|
|
16587
|
+
visitor.resetEnvProvider();
|
|
16588
|
+
}
|
|
16021
16589
|
const ast = visitor.visit(cst, filename);
|
|
16022
16590
|
return { ast, errors };
|
|
16023
16591
|
} catch (err) {
|
|
@@ -16987,8 +17555,28 @@ var Resolver = class {
|
|
|
16987
17555
|
sources.push(...parent.sources);
|
|
16988
17556
|
errors.push(...parent.errors);
|
|
16989
17557
|
if (parent.ast) {
|
|
17558
|
+
let resolvedParent = parent.ast;
|
|
17559
|
+
if (parent.ast.meta?.params || ast.inherit.params) {
|
|
17560
|
+
this.logger.debug(`Binding template parameters for ${parentPath}`);
|
|
17561
|
+
try {
|
|
17562
|
+
const params = bindParams(
|
|
17563
|
+
ast.inherit.params,
|
|
17564
|
+
parent.ast.meta?.params,
|
|
17565
|
+
parentPath,
|
|
17566
|
+
ast.inherit.loc
|
|
17567
|
+
);
|
|
17568
|
+
if (params.size > 0) {
|
|
17569
|
+
const ctx = { params, sourceFile: parentPath };
|
|
17570
|
+
resolvedParent = interpolateAST(parent.ast, ctx);
|
|
17571
|
+
this.logger.debug(`Interpolated ${params.size} parameter(s)`);
|
|
17572
|
+
}
|
|
17573
|
+
} catch (err) {
|
|
17574
|
+
errors.push(new ResolveError(err.message, ast.inherit.loc));
|
|
17575
|
+
return ast;
|
|
17576
|
+
}
|
|
17577
|
+
}
|
|
16990
17578
|
this.logger.debug(`Merging with parent AST`);
|
|
16991
|
-
return resolveInheritance(
|
|
17579
|
+
return resolveInheritance(resolvedParent, ast);
|
|
16992
17580
|
}
|
|
16993
17581
|
} catch (err) {
|
|
16994
17582
|
if (err instanceof CircularDependencyError) {
|
|
@@ -17012,8 +17600,23 @@ var Resolver = class {
|
|
|
17012
17600
|
sources.push(...imported.sources);
|
|
17013
17601
|
errors.push(...imported.errors);
|
|
17014
17602
|
if (imported.ast) {
|
|
17603
|
+
let resolvedImport = imported.ast;
|
|
17604
|
+
if (imported.ast.meta?.params || use.params) {
|
|
17605
|
+
this.logger.debug(`Binding template parameters for ${importPath}`);
|
|
17606
|
+
try {
|
|
17607
|
+
const params = bindParams(use.params, imported.ast.meta?.params, importPath, use.loc);
|
|
17608
|
+
if (params.size > 0) {
|
|
17609
|
+
const ctx = { params, sourceFile: importPath };
|
|
17610
|
+
resolvedImport = interpolateAST(imported.ast, ctx);
|
|
17611
|
+
this.logger.debug(`Interpolated ${params.size} parameter(s)`);
|
|
17612
|
+
}
|
|
17613
|
+
} catch (err) {
|
|
17614
|
+
errors.push(new ResolveError(err.message, use.loc));
|
|
17615
|
+
continue;
|
|
17616
|
+
}
|
|
17617
|
+
}
|
|
17015
17618
|
this.logger.debug(`Merging import${use.alias ? ` as "${use.alias}"` : ""}`);
|
|
17016
|
-
result = resolveUses(result, use,
|
|
17619
|
+
result = resolveUses(result, use, resolvedImport);
|
|
17017
17620
|
}
|
|
17018
17621
|
} catch (err) {
|
|
17019
17622
|
if (err instanceof CircularDependencyError) {
|
|
@@ -18262,6 +18865,92 @@ var emptyBlock = {
|
|
|
18262
18865
|
}
|
|
18263
18866
|
};
|
|
18264
18867
|
|
|
18868
|
+
// packages/validator/src/rules/valid-params.ts
|
|
18869
|
+
function paramTypeToString2(paramType) {
|
|
18870
|
+
switch (paramType.kind) {
|
|
18871
|
+
case "string":
|
|
18872
|
+
return "string";
|
|
18873
|
+
case "number":
|
|
18874
|
+
return "number";
|
|
18875
|
+
case "boolean":
|
|
18876
|
+
return "boolean";
|
|
18877
|
+
case "enum":
|
|
18878
|
+
return `enum(${paramType.options.map((o) => `"${o}"`).join(", ")})`;
|
|
18879
|
+
}
|
|
18880
|
+
}
|
|
18881
|
+
function getValueType2(value) {
|
|
18882
|
+
if (value === null) return "null";
|
|
18883
|
+
if (typeof value === "string") return "string";
|
|
18884
|
+
if (typeof value === "number") return "number";
|
|
18885
|
+
if (typeof value === "boolean") return "boolean";
|
|
18886
|
+
if (Array.isArray(value)) return "array";
|
|
18887
|
+
if (typeof value === "object" && "type" in value) {
|
|
18888
|
+
const typed = value;
|
|
18889
|
+
if (typed.type === "TextContent") return "string";
|
|
18890
|
+
if (typed.type === "TemplateExpression") return "template";
|
|
18891
|
+
if (typed.type === "TypeExpression") return "type";
|
|
18892
|
+
}
|
|
18893
|
+
return "object";
|
|
18894
|
+
}
|
|
18895
|
+
function valueMatchesType(value, paramType) {
|
|
18896
|
+
const actualType = getValueType2(value);
|
|
18897
|
+
switch (paramType.kind) {
|
|
18898
|
+
case "string":
|
|
18899
|
+
return actualType === "string";
|
|
18900
|
+
case "number":
|
|
18901
|
+
return actualType === "number";
|
|
18902
|
+
case "boolean":
|
|
18903
|
+
return actualType === "boolean";
|
|
18904
|
+
case "enum":
|
|
18905
|
+
return actualType === "string" && paramType.options.includes(value);
|
|
18906
|
+
}
|
|
18907
|
+
}
|
|
18908
|
+
var validParams = {
|
|
18909
|
+
id: "PS009",
|
|
18910
|
+
name: "valid-params",
|
|
18911
|
+
description: "Validate parameter definitions in @meta",
|
|
18912
|
+
defaultSeverity: "error",
|
|
18913
|
+
validate: (ctx) => {
|
|
18914
|
+
const params = ctx.ast.meta?.params;
|
|
18915
|
+
if (!params || params.length === 0) {
|
|
18916
|
+
return;
|
|
18917
|
+
}
|
|
18918
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18919
|
+
for (const param of params) {
|
|
18920
|
+
if (seen.has(param.name)) {
|
|
18921
|
+
ctx.report({
|
|
18922
|
+
message: `Duplicate parameter definition: '${param.name}'`,
|
|
18923
|
+
location: param.loc,
|
|
18924
|
+
suggestion: "Remove the duplicate parameter definition"
|
|
18925
|
+
});
|
|
18926
|
+
}
|
|
18927
|
+
seen.add(param.name);
|
|
18928
|
+
}
|
|
18929
|
+
for (const param of params) {
|
|
18930
|
+
if (param.defaultValue !== void 0) {
|
|
18931
|
+
if (!valueMatchesType(param.defaultValue, param.paramType)) {
|
|
18932
|
+
const expectedType = paramTypeToString2(param.paramType);
|
|
18933
|
+
const actualType = getValueType2(param.defaultValue);
|
|
18934
|
+
ctx.report({
|
|
18935
|
+
message: `Default value for '${param.name}' has wrong type: expected ${expectedType}, got ${actualType}`,
|
|
18936
|
+
location: param.loc,
|
|
18937
|
+
suggestion: `Change the default value to a ${expectedType}`
|
|
18938
|
+
});
|
|
18939
|
+
}
|
|
18940
|
+
}
|
|
18941
|
+
}
|
|
18942
|
+
for (const param of params) {
|
|
18943
|
+
if (param.optional && param.defaultValue === void 0) {
|
|
18944
|
+
ctx.report({
|
|
18945
|
+
message: `Optional parameter '${param.name}' has no default value`,
|
|
18946
|
+
location: param.loc,
|
|
18947
|
+
suggestion: "Consider adding a default value or marking as required"
|
|
18948
|
+
});
|
|
18949
|
+
}
|
|
18950
|
+
}
|
|
18951
|
+
}
|
|
18952
|
+
};
|
|
18953
|
+
|
|
18265
18954
|
// packages/validator/src/rules/index.ts
|
|
18266
18955
|
var allRules = [
|
|
18267
18956
|
// Required meta rules (PS001, PS002)
|
|
@@ -18278,7 +18967,9 @@ var allRules = [
|
|
|
18278
18967
|
// Deprecated features (PS007)
|
|
18279
18968
|
deprecated,
|
|
18280
18969
|
// Empty blocks (PS008)
|
|
18281
|
-
emptyBlock
|
|
18970
|
+
emptyBlock,
|
|
18971
|
+
// Valid params (PS009)
|
|
18972
|
+
validParams
|
|
18282
18973
|
];
|
|
18283
18974
|
|
|
18284
18975
|
// packages/validator/src/validator.ts
|
package/package.json
CHANGED