@atscript/core 0.1.27 → 0.1.28
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/index.cjs +427 -134
- package/dist/index.d.ts +7 -12
- package/dist/index.mjs +427 -134
- package/package.json +9 -3
- package/scripts/setup-skills.js +77 -0
- package/skills/atscript-core/.gitkeep +0 -0
- package/skills/atscript-core/SKILL.md +32 -0
- package/skills/atscript-core/annotations.md +157 -0
- package/skills/atscript-core/core.md +125 -0
- package/skills/atscript-core/plugins.md +206 -0
- package/skills/atscript-core/primitives.md +156 -0
package/dist/index.cjs
CHANGED
|
@@ -433,60 +433,13 @@ else obj[key] = value;
|
|
|
433
433
|
var SemanticPrimitiveNode = class SemanticPrimitiveNode extends SemanticNode {
|
|
434
434
|
applyAnnotations() {
|
|
435
435
|
this.annotations = [];
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
name: "expect.maxLength",
|
|
444
|
-
token: dummyToken,
|
|
445
|
-
args: [num(this.config.expect.maxLength)]
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
if (this.type === "string") {
|
|
449
|
-
if (this.config.expect?.required === true) this.annotations.push({
|
|
450
|
-
name: "meta.required",
|
|
451
|
-
token: dummyToken,
|
|
452
|
-
args: []
|
|
453
|
-
});
|
|
454
|
-
if (this.config.expect?.pattern !== undefined) {
|
|
455
|
-
const patterns = Array.isArray(this.config.expect.pattern) ? this.config.expect.pattern : [this.config.expect.pattern];
|
|
456
|
-
for (const p of patterns) {
|
|
457
|
-
const args = typeof p === "string" ? [text$1(p)] : [text$1(p.source), text$1(p.flags)];
|
|
458
|
-
if (this.config.expect.message) args[2] = text$1(this.config.expect.message);
|
|
459
|
-
this.annotations.push({
|
|
460
|
-
name: "expect.pattern",
|
|
461
|
-
token: dummyToken,
|
|
462
|
-
args
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
if (this.type === "number") {
|
|
468
|
-
if (typeof this.config.expect?.min === "number") this.annotations.push({
|
|
469
|
-
name: "expect.min",
|
|
470
|
-
token: dummyToken,
|
|
471
|
-
args: [num(this.config.expect.min)]
|
|
472
|
-
});
|
|
473
|
-
if (typeof this.config.expect?.max === "number") this.annotations.push({
|
|
474
|
-
name: "expect.max",
|
|
475
|
-
token: dummyToken,
|
|
476
|
-
args: [num(this.config.expect.max)]
|
|
477
|
-
});
|
|
478
|
-
if (this.config.expect?.int === true) this.annotations.push({
|
|
479
|
-
name: "expect.int",
|
|
480
|
-
token: dummyToken,
|
|
481
|
-
args: []
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
if (this.type === "boolean") {
|
|
485
|
-
if (this.config.expect?.required === true) this.annotations.push({
|
|
486
|
-
name: "meta.required",
|
|
487
|
-
token: dummyToken,
|
|
488
|
-
args: []
|
|
489
|
-
});
|
|
436
|
+
for (const [name, value] of Object.entries(this.config.annotations || {})) {
|
|
437
|
+
const spec = this.annotationTree ? resolveAnnotation(name, this.annotationTree) : undefined;
|
|
438
|
+
const isMultiple = spec?.config.multiple ?? false;
|
|
439
|
+
if (Array.isArray(value)) {
|
|
440
|
+
if (isMultiple) for (const item of value) this.annotations.push(toAnnotationTokens(name, item, spec));
|
|
441
|
+
else if (value.length > 0) this.annotations.push(toAnnotationTokens(name, value[0], spec));
|
|
442
|
+
} else this.annotations.push(toAnnotationTokens(name, value, spec));
|
|
490
443
|
}
|
|
491
444
|
}
|
|
492
445
|
get key() {
|
|
@@ -515,8 +468,8 @@ var SemanticPrimitiveNode = class SemanticPrimitiveNode extends SemanticNode {
|
|
|
515
468
|
s += this.renderChildren();
|
|
516
469
|
return indent + s.split("\n").join(`\n${indent}`);
|
|
517
470
|
}
|
|
518
|
-
constructor(_id, config, parentKey = "") {
|
|
519
|
-
super("primitive"), _define_property$11(this, "_id", void 0), _define_property$11(this, "config", void 0), _define_property$11(this, "parentKey", void 0), _define_property$11(this, "type", void 0), _define_property$11(this, "props", void 0), _define_property$11(this, "tags", void 0), this._id = _id, this.config = config, this.parentKey = parentKey;
|
|
471
|
+
constructor(_id, config, parentKey = "", annotationTree) {
|
|
472
|
+
super("primitive"), _define_property$11(this, "_id", void 0), _define_property$11(this, "config", void 0), _define_property$11(this, "parentKey", void 0), _define_property$11(this, "annotationTree", void 0), _define_property$11(this, "type", void 0), _define_property$11(this, "props", void 0), _define_property$11(this, "tags", void 0), this._id = _id, this.config = config, this.parentKey = parentKey, this.annotationTree = annotationTree;
|
|
520
473
|
this.props = new Map();
|
|
521
474
|
this.tags = new Set([_id, ...config?.tags || []]);
|
|
522
475
|
for (const [ext, def] of Object.entries(config.extensions || {})) {
|
|
@@ -525,11 +478,11 @@ var SemanticPrimitiveNode = class SemanticPrimitiveNode extends SemanticNode {
|
|
|
525
478
|
documentation: def.documentation ?? config.documentation,
|
|
526
479
|
extensions: def.extensions,
|
|
527
480
|
tags: Array.from(new Set([...def.tags || [], ...Array.from(this.tags)])),
|
|
528
|
-
|
|
529
|
-
...config.
|
|
530
|
-
...def.
|
|
481
|
+
annotations: {
|
|
482
|
+
...config.annotations,
|
|
483
|
+
...def.annotations
|
|
531
484
|
}
|
|
532
|
-
}, this.key);
|
|
485
|
+
}, this.key, this.annotationTree);
|
|
533
486
|
this.props.set(ext, node);
|
|
534
487
|
}
|
|
535
488
|
if (typeof config.type === "object") this.type = config.type.kind === "final" ? config.type.value : config.type.kind;
|
|
@@ -537,6 +490,36 @@ else this.type = config.type;
|
|
|
537
490
|
this.applyAnnotations();
|
|
538
491
|
}
|
|
539
492
|
};
|
|
493
|
+
function toAnnotationTokens(name, value, spec) {
|
|
494
|
+
if (typeof value === "boolean") return {
|
|
495
|
+
name,
|
|
496
|
+
token: dummyToken,
|
|
497
|
+
args: []
|
|
498
|
+
};
|
|
499
|
+
if (typeof value === "string") return {
|
|
500
|
+
name,
|
|
501
|
+
token: dummyToken,
|
|
502
|
+
args: [text$1(value)]
|
|
503
|
+
};
|
|
504
|
+
if (typeof value === "number") return {
|
|
505
|
+
name,
|
|
506
|
+
token: dummyToken,
|
|
507
|
+
args: [num(value)]
|
|
508
|
+
};
|
|
509
|
+
const argNames = spec?.arguments.map((a) => a.name) ?? Object.keys(value);
|
|
510
|
+
const raw = argNames.map((argName) => {
|
|
511
|
+
const v = value[argName];
|
|
512
|
+
if (v === undefined) return undefined;
|
|
513
|
+
return typeof v === "number" ? num(v) : text$1(String(v));
|
|
514
|
+
});
|
|
515
|
+
while (raw.length > 0 && raw[raw.length - 1] === undefined) raw.pop();
|
|
516
|
+
const args = raw.map((t) => t ?? text$1(""));
|
|
517
|
+
return {
|
|
518
|
+
name,
|
|
519
|
+
token: dummyToken,
|
|
520
|
+
args
|
|
521
|
+
};
|
|
522
|
+
}
|
|
540
523
|
const dummyToken = new Token({
|
|
541
524
|
getRange: () => ({
|
|
542
525
|
end: {
|
|
@@ -3048,6 +3031,162 @@ const zeroRange = {
|
|
|
3048
3031
|
}
|
|
3049
3032
|
};
|
|
3050
3033
|
|
|
3034
|
+
//#endregion
|
|
3035
|
+
//#region packages/core/src/defaults/db-annotations.ts
|
|
3036
|
+
const dbAnnotations = {
|
|
3037
|
+
table: new AnnotationSpec({
|
|
3038
|
+
description: "Marks an interface as a database-persisted entity (table in SQL, collection in MongoDB). If the name argument is omitted, the adapter derives the table name from the interface name.\n\n**Example:**\n```atscript\n@db.table \"users\"\nexport interface User { ... }\n```\n",
|
|
3039
|
+
nodeType: ["interface"],
|
|
3040
|
+
argument: {
|
|
3041
|
+
optional: true,
|
|
3042
|
+
name: "name",
|
|
3043
|
+
type: "string",
|
|
3044
|
+
description: "Table/collection name. If omitted, derived from interface name."
|
|
3045
|
+
}
|
|
3046
|
+
}),
|
|
3047
|
+
schema: new AnnotationSpec({
|
|
3048
|
+
description: "Assigns the entity to a database schema/namespace.\n\n**Example:**\n```atscript\n@db.table \"users\"\n@db.schema \"auth\"\nexport interface User { ... }\n```\n",
|
|
3049
|
+
nodeType: ["interface"],
|
|
3050
|
+
argument: {
|
|
3051
|
+
name: "name",
|
|
3052
|
+
type: "string",
|
|
3053
|
+
description: "Schema/namespace name."
|
|
3054
|
+
}
|
|
3055
|
+
}),
|
|
3056
|
+
index: {
|
|
3057
|
+
plain: new AnnotationSpec({
|
|
3058
|
+
description: "Standard (non-unique) index for query performance. Fields sharing the same index name form a composite index.\n\n**Example:**\n```atscript\n@db.index.plain \"idx_timeline\", \"desc\"\ncreatedAt: number.timestamp\n```\n",
|
|
3059
|
+
nodeType: ["prop"],
|
|
3060
|
+
multiple: true,
|
|
3061
|
+
mergeStrategy: "append",
|
|
3062
|
+
argument: [{
|
|
3063
|
+
optional: true,
|
|
3064
|
+
name: "name",
|
|
3065
|
+
type: "string",
|
|
3066
|
+
description: "Index name / composite group name."
|
|
3067
|
+
}, {
|
|
3068
|
+
optional: true,
|
|
3069
|
+
name: "sort",
|
|
3070
|
+
type: "string",
|
|
3071
|
+
values: ["asc", "desc"],
|
|
3072
|
+
description: "Sort direction. Defaults to \"asc\"."
|
|
3073
|
+
}]
|
|
3074
|
+
}),
|
|
3075
|
+
unique: new AnnotationSpec({
|
|
3076
|
+
description: "Unique index — ensures no two rows/documents have the same value(s). Fields sharing the same index name form a composite unique constraint.\n\n**Example:**\n```atscript\n@db.index.unique \"tenant_email\"\nemail: string.email\n```\n",
|
|
3077
|
+
nodeType: ["prop"],
|
|
3078
|
+
multiple: true,
|
|
3079
|
+
mergeStrategy: "append",
|
|
3080
|
+
argument: {
|
|
3081
|
+
optional: true,
|
|
3082
|
+
name: "name",
|
|
3083
|
+
type: "string",
|
|
3084
|
+
description: "Index name / composite group name."
|
|
3085
|
+
}
|
|
3086
|
+
}),
|
|
3087
|
+
fulltext: new AnnotationSpec({
|
|
3088
|
+
description: "Full-text search index. Fields sharing the same index name form a composite full-text index.\n\n**Example:**\n```atscript\n@db.index.fulltext \"ft_content\"\ntitle: string\n```\n",
|
|
3089
|
+
nodeType: ["prop"],
|
|
3090
|
+
multiple: true,
|
|
3091
|
+
mergeStrategy: "append",
|
|
3092
|
+
argument: {
|
|
3093
|
+
optional: true,
|
|
3094
|
+
name: "name",
|
|
3095
|
+
type: "string",
|
|
3096
|
+
description: "Index name / composite group name."
|
|
3097
|
+
}
|
|
3098
|
+
})
|
|
3099
|
+
},
|
|
3100
|
+
column: new AnnotationSpec({
|
|
3101
|
+
description: "Overrides the physical column/document-field name in the database.\n\n**Example:**\n```atscript\n@db.column \"first_name\"\nfirstName: string\n```\n",
|
|
3102
|
+
nodeType: ["prop"],
|
|
3103
|
+
argument: {
|
|
3104
|
+
name: "name",
|
|
3105
|
+
type: "string",
|
|
3106
|
+
description: "The actual column/field name in the DB."
|
|
3107
|
+
}
|
|
3108
|
+
}),
|
|
3109
|
+
default: {
|
|
3110
|
+
value: new AnnotationSpec({
|
|
3111
|
+
description: "Sets a static DB-level default value (used in DDL DEFAULT clause). For string fields the value is used as-is; for other types it is parsed as JSON.\n\n**Example:**\n```atscript\n@db.default.value \"active\"\nstatus: string\n```\n",
|
|
3112
|
+
nodeType: ["prop"],
|
|
3113
|
+
argument: {
|
|
3114
|
+
name: "value",
|
|
3115
|
+
type: "string",
|
|
3116
|
+
description: "Static default value. Strings used as-is; other types parsed via JSON.parse()."
|
|
3117
|
+
}
|
|
3118
|
+
}),
|
|
3119
|
+
fn: new AnnotationSpec({
|
|
3120
|
+
description: "Sets a DB-level generated default. The function name is portable — each adapter maps it to the appropriate DB mechanism.\n\n**Example:**\n```atscript\n@db.default.fn \"increment\"\nid: number\n```\n",
|
|
3121
|
+
nodeType: ["prop"],
|
|
3122
|
+
argument: {
|
|
3123
|
+
name: "fn",
|
|
3124
|
+
type: "string",
|
|
3125
|
+
values: [
|
|
3126
|
+
"increment",
|
|
3127
|
+
"uuid",
|
|
3128
|
+
"now"
|
|
3129
|
+
],
|
|
3130
|
+
description: "Generation function name: \"increment\", \"uuid\", or \"now\"."
|
|
3131
|
+
},
|
|
3132
|
+
validate(token, args, doc) {
|
|
3133
|
+
const errors = [];
|
|
3134
|
+
if (!args[0]) return errors;
|
|
3135
|
+
const fnName = args[0].text;
|
|
3136
|
+
const validFns = [
|
|
3137
|
+
"increment",
|
|
3138
|
+
"uuid",
|
|
3139
|
+
"now"
|
|
3140
|
+
];
|
|
3141
|
+
if (!validFns.includes(fnName)) {
|
|
3142
|
+
errors.push({
|
|
3143
|
+
message: `Unknown @db.default.fn "${fnName}" — expected "increment", "uuid", or "now"`,
|
|
3144
|
+
severity: 1,
|
|
3145
|
+
range: args[0].range
|
|
3146
|
+
});
|
|
3147
|
+
return errors;
|
|
3148
|
+
}
|
|
3149
|
+
const field = token.parentNode;
|
|
3150
|
+
const definition$1 = field.getDefinition();
|
|
3151
|
+
if (!definition$1 || !isRef(definition$1)) return errors;
|
|
3152
|
+
const unwound = doc.unwindType(definition$1.id, definition$1.chain);
|
|
3153
|
+
if (!unwound || !isPrimitive(unwound.def)) return errors;
|
|
3154
|
+
const baseType = unwound.def.config.type;
|
|
3155
|
+
if (fnName === "increment" && baseType !== "number") errors.push({
|
|
3156
|
+
message: `@db.default.fn "increment" is not compatible with type "${baseType}" — requires number`,
|
|
3157
|
+
severity: 1,
|
|
3158
|
+
range: token.range
|
|
3159
|
+
});
|
|
3160
|
+
else if (fnName === "uuid" && baseType !== "string") errors.push({
|
|
3161
|
+
message: `@db.default.fn "uuid" is not compatible with type "${baseType}" — requires string`,
|
|
3162
|
+
severity: 1,
|
|
3163
|
+
range: token.range
|
|
3164
|
+
});
|
|
3165
|
+
else if (fnName === "now" && !["number", "string"].includes(baseType)) errors.push({
|
|
3166
|
+
message: `@db.default.fn "now" is not compatible with type "${baseType}" — requires number or string`,
|
|
3167
|
+
severity: 1,
|
|
3168
|
+
range: token.range
|
|
3169
|
+
});
|
|
3170
|
+
return errors;
|
|
3171
|
+
}
|
|
3172
|
+
})
|
|
3173
|
+
},
|
|
3174
|
+
ignore: new AnnotationSpec({
|
|
3175
|
+
description: "Excludes a field from the database schema. The field exists in the Atscript type but has no column in the DB.\n\n**Example:**\n```atscript\n@db.ignore\ndisplayName: string\n```\n",
|
|
3176
|
+
nodeType: ["prop"],
|
|
3177
|
+
validate(token, args, doc) {
|
|
3178
|
+
const errors = [];
|
|
3179
|
+
const field = token.parentNode;
|
|
3180
|
+
if (field.countAnnotations("meta.id") > 0) errors.push({
|
|
3181
|
+
message: `@db.ignore cannot coexist with @meta.id — a field cannot be both a primary key and excluded from the database`,
|
|
3182
|
+
severity: 1,
|
|
3183
|
+
range: token.range
|
|
3184
|
+
});
|
|
3185
|
+
return errors;
|
|
3186
|
+
}
|
|
3187
|
+
})
|
|
3188
|
+
};
|
|
3189
|
+
|
|
3051
3190
|
//#endregion
|
|
3052
3191
|
//#region packages/core/src/defaults/emit-annotations.ts
|
|
3053
3192
|
const emitAnnotations = { jsonSchema: new AnnotationSpec({
|
|
@@ -3224,7 +3363,35 @@ const expectAnnotations = {
|
|
|
3224
3363
|
}
|
|
3225
3364
|
return [];
|
|
3226
3365
|
}
|
|
3227
|
-
})
|
|
3366
|
+
}),
|
|
3367
|
+
array: { key: new AnnotationSpec({
|
|
3368
|
+
description: "Marks a **key field** inside an array. This annotation is used to identify unique fields within an array that can be used as **lookup keys**.\n\n\n\n**Example:**\n```atscript\nexport interface User {\n id: string\n profiles: {\n @expect.array.key\n profileId: string\n name: string\n }[]\n}\n```\n",
|
|
3369
|
+
nodeType: ["prop", "type"],
|
|
3370
|
+
multiple: false,
|
|
3371
|
+
validate(token, args, doc) {
|
|
3372
|
+
const field = token.parentNode;
|
|
3373
|
+
const errors = [];
|
|
3374
|
+
const isOptional = !!field.token("optional");
|
|
3375
|
+
if (isOptional) errors.push({
|
|
3376
|
+
message: `@expect.array.key can't be optional`,
|
|
3377
|
+
severity: 1,
|
|
3378
|
+
range: field.token("identifier").range
|
|
3379
|
+
});
|
|
3380
|
+
const definition$1 = field.getDefinition();
|
|
3381
|
+
if (!definition$1) return errors;
|
|
3382
|
+
let wrongType = false;
|
|
3383
|
+
if (isRef(definition$1)) {
|
|
3384
|
+
const def = doc.unwindType(definition$1.id, definition$1.chain)?.def;
|
|
3385
|
+
if (isPrimitive(def) && !["string", "number"].includes(def.config.type)) wrongType = true;
|
|
3386
|
+
} else wrongType = true;
|
|
3387
|
+
if (wrongType) errors.push({
|
|
3388
|
+
message: `@expect.array.key must be of type string or number`,
|
|
3389
|
+
severity: 1,
|
|
3390
|
+
range: token.range
|
|
3391
|
+
});
|
|
3392
|
+
return errors;
|
|
3393
|
+
}
|
|
3394
|
+
}) }
|
|
3228
3395
|
};
|
|
3229
3396
|
|
|
3230
3397
|
//#endregion
|
|
@@ -3239,13 +3406,8 @@ const metaAnnotations = {
|
|
|
3239
3406
|
}
|
|
3240
3407
|
}),
|
|
3241
3408
|
id: new AnnotationSpec({
|
|
3242
|
-
description: "Marks a field as a unique identifier across multiple domains (DB, API, UI, logs).\n\n**Example:**```atscript@meta.
|
|
3243
|
-
|
|
3244
|
-
optional: true,
|
|
3245
|
-
name: "name",
|
|
3246
|
-
type: "string",
|
|
3247
|
-
description: "Custom identifier name (defaults to property name if omitted)."
|
|
3248
|
-
}
|
|
3409
|
+
description: "Marks a field as a unique identifier across multiple domains (DB, API, UI, logs). Multiple fields can be annotated with `@meta.id` to form a composite primary key.\n\n**Example:**```atscript@meta.idid: string```",
|
|
3410
|
+
nodeType: ["prop"]
|
|
3249
3411
|
}),
|
|
3250
3412
|
description: new AnnotationSpec({
|
|
3251
3413
|
description: "Provides a **detailed description** of a field or entity, often used in documentation.\n\n**Example:**```atscript@meta.description \"Stores the user email address\"email: string```",
|
|
@@ -3264,15 +3426,6 @@ const metaAnnotations = {
|
|
|
3264
3426
|
description: "A line of documentation text. Multiple annotations can be used to form a full Markdown document."
|
|
3265
3427
|
}
|
|
3266
3428
|
}),
|
|
3267
|
-
placeholder: new AnnotationSpec({
|
|
3268
|
-
description: "Defines a **default placeholder value** for UI input fields.\n\n**Example:**```atscript@meta.placeholder \"Enter your name\"name: string```",
|
|
3269
|
-
nodeType: ["prop", "type"],
|
|
3270
|
-
argument: {
|
|
3271
|
-
name: "text",
|
|
3272
|
-
type: "string",
|
|
3273
|
-
description: "The placeholder text to display in UI forms."
|
|
3274
|
-
}
|
|
3275
|
-
}),
|
|
3276
3429
|
sensitive: new AnnotationSpec({
|
|
3277
3430
|
description: "Marks a field as **sensitive** (e.g., passwords, API keys), ensuring it is hidden in logs and UI.\n\n**Example:**```atscript@meta.sensitivepassword: string```",
|
|
3278
3431
|
nodeType: ["prop", "type"],
|
|
@@ -3310,34 +3463,6 @@ const metaAnnotations = {
|
|
|
3310
3463
|
type: "string",
|
|
3311
3464
|
description: "The example value. Strings are used as-is; other types are parsed as JSON."
|
|
3312
3465
|
}
|
|
3313
|
-
}),
|
|
3314
|
-
isKey: new AnnotationSpec({
|
|
3315
|
-
description: "Marks a **key field** inside an array. This annotation is used to identify unique fields within an array that can be used as **lookup keys**.\n\n\n\n**Example:**\n```atscript\nexport interface User {\n id: string\n profiles: {\n @meta.isKey\n profileId: string\n name: string\n }[]\n}\n```\n",
|
|
3316
|
-
nodeType: ["prop", "type"],
|
|
3317
|
-
multiple: false,
|
|
3318
|
-
validate(token, args, doc) {
|
|
3319
|
-
const field = token.parentNode;
|
|
3320
|
-
const errors = [];
|
|
3321
|
-
const isOptional = !!field.token("optional");
|
|
3322
|
-
if (isOptional) errors.push({
|
|
3323
|
-
message: `@meta.isKey can't be optional`,
|
|
3324
|
-
severity: 1,
|
|
3325
|
-
range: field.token("identifier").range
|
|
3326
|
-
});
|
|
3327
|
-
const definition$1 = field.getDefinition();
|
|
3328
|
-
if (!definition$1) return errors;
|
|
3329
|
-
let wrongType = false;
|
|
3330
|
-
if (isRef(definition$1)) {
|
|
3331
|
-
const def = doc.unwindType(definition$1.id, definition$1.chain)?.def;
|
|
3332
|
-
if (isPrimitive(def) && !["string", "number"].includes(def.config.type)) wrongType = true;
|
|
3333
|
-
} else wrongType = true;
|
|
3334
|
-
if (wrongType) errors.push({
|
|
3335
|
-
message: `@meta.isKey must be of type string or number`,
|
|
3336
|
-
severity: 1,
|
|
3337
|
-
range: token.range
|
|
3338
|
-
});
|
|
3339
|
-
return errors;
|
|
3340
|
-
}
|
|
3341
3466
|
})
|
|
3342
3467
|
};
|
|
3343
3468
|
|
|
@@ -3345,11 +3470,11 @@ const metaAnnotations = {
|
|
|
3345
3470
|
//#region packages/core/src/defaults/primitives.ts
|
|
3346
3471
|
const positive = {
|
|
3347
3472
|
documentation: "Number that greater than or equal to zero.",
|
|
3348
|
-
|
|
3473
|
+
annotations: { "expect.min": 0 }
|
|
3349
3474
|
};
|
|
3350
3475
|
const negative = {
|
|
3351
3476
|
documentation: "Number that less than or equal to zero.",
|
|
3352
|
-
|
|
3477
|
+
annotations: { "expect.max": 0 }
|
|
3353
3478
|
};
|
|
3354
3479
|
const positiveOrNegative = {
|
|
3355
3480
|
positive,
|
|
@@ -3363,47 +3488,60 @@ const primitives = {
|
|
|
3363
3488
|
extensions: {
|
|
3364
3489
|
email: {
|
|
3365
3490
|
documentation: "Represents an email address.",
|
|
3366
|
-
expect: {
|
|
3367
|
-
pattern:
|
|
3491
|
+
annotations: { "expect.pattern": {
|
|
3492
|
+
pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
|
|
3368
3493
|
message: "Invalid email format."
|
|
3369
|
-
}
|
|
3494
|
+
} }
|
|
3370
3495
|
},
|
|
3371
3496
|
phone: {
|
|
3372
3497
|
documentation: "Represents an phone number.",
|
|
3373
|
-
expect: {
|
|
3374
|
-
pattern:
|
|
3498
|
+
annotations: { "expect.pattern": {
|
|
3499
|
+
pattern: "^\\+?[0-9\\s-]{10,15}$",
|
|
3375
3500
|
message: "Invalid phone number format."
|
|
3376
|
-
}
|
|
3501
|
+
} }
|
|
3377
3502
|
},
|
|
3378
3503
|
date: {
|
|
3379
3504
|
documentation: "Represents a date string.",
|
|
3380
|
-
expect:
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3505
|
+
annotations: { "expect.pattern": [
|
|
3506
|
+
{
|
|
3507
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
3508
|
+
message: "Invalid date format."
|
|
3509
|
+
},
|
|
3510
|
+
{
|
|
3511
|
+
pattern: "^\\d{2}/\\d{2}/\\d{4}$",
|
|
3512
|
+
message: "Invalid date format."
|
|
3513
|
+
},
|
|
3514
|
+
{
|
|
3515
|
+
pattern: "^\\d{2}-\\d{2}-\\d{4}$",
|
|
3516
|
+
message: "Invalid date format."
|
|
3517
|
+
},
|
|
3518
|
+
{
|
|
3519
|
+
pattern: "^\\d{1,2} [A-Za-z]+ \\d{4}$",
|
|
3520
|
+
message: "Invalid date format."
|
|
3521
|
+
}
|
|
3522
|
+
] }
|
|
3389
3523
|
},
|
|
3390
3524
|
isoDate: {
|
|
3391
3525
|
documentation: "Represents a date string in ISO format.",
|
|
3392
|
-
expect: {
|
|
3393
|
-
pattern:
|
|
3526
|
+
annotations: { "expect.pattern": [{
|
|
3527
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z$",
|
|
3394
3528
|
message: "Invalid ISO date format."
|
|
3395
|
-
}
|
|
3529
|
+
}, {
|
|
3530
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?([+-]\\d{2}:\\d{2})$",
|
|
3531
|
+
message: "Invalid ISO date format."
|
|
3532
|
+
}] }
|
|
3396
3533
|
},
|
|
3397
3534
|
uuid: {
|
|
3398
3535
|
documentation: "Represents a UUID.",
|
|
3399
|
-
expect: {
|
|
3400
|
-
pattern:
|
|
3536
|
+
annotations: { "expect.pattern": {
|
|
3537
|
+
pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
3538
|
+
flags: "i",
|
|
3401
3539
|
message: "Invalid UUID format."
|
|
3402
|
-
}
|
|
3540
|
+
} }
|
|
3403
3541
|
},
|
|
3404
3542
|
required: {
|
|
3405
3543
|
documentation: "Non-empty string that contains at least one non-whitespace character.",
|
|
3406
|
-
|
|
3544
|
+
annotations: { "meta.required": true }
|
|
3407
3545
|
}
|
|
3408
3546
|
}
|
|
3409
3547
|
},
|
|
@@ -3423,11 +3561,22 @@ const primitives = {
|
|
|
3423
3561
|
int: {
|
|
3424
3562
|
extensions: positiveOrNegative,
|
|
3425
3563
|
documentation: "Represents an integer number.",
|
|
3426
|
-
|
|
3564
|
+
annotations: { "expect.int": true }
|
|
3427
3565
|
},
|
|
3428
3566
|
timestamp: {
|
|
3429
3567
|
documentation: "Represents a timestamp.",
|
|
3430
|
-
|
|
3568
|
+
annotations: { "expect.int": true },
|
|
3569
|
+
extensions: {
|
|
3570
|
+
created: {
|
|
3571
|
+
documentation: "Timestamp auto-set on creation. Auto-applies @db.default.fn \"now\".",
|
|
3572
|
+
tags: ["created"],
|
|
3573
|
+
annotations: { "db.default.fn": "now" }
|
|
3574
|
+
},
|
|
3575
|
+
updated: {
|
|
3576
|
+
documentation: "Timestamp auto-updated on every write. DB adapters read the \"updated\" tag.",
|
|
3577
|
+
tags: ["updated"]
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3431
3580
|
}
|
|
3432
3581
|
}
|
|
3433
3582
|
},
|
|
@@ -3437,7 +3586,7 @@ const primitives = {
|
|
|
3437
3586
|
extensions: {
|
|
3438
3587
|
required: {
|
|
3439
3588
|
documentation: "Boolean that must be true. Useful for checkboxes like \"accept terms\".",
|
|
3440
|
-
|
|
3589
|
+
annotations: { "meta.required": true }
|
|
3441
3590
|
},
|
|
3442
3591
|
true: { documentation: "Represents a true value." },
|
|
3443
3592
|
false: { documentation: "Represents a false value." }
|
|
@@ -3461,6 +3610,148 @@ const primitives = {
|
|
|
3461
3610
|
}
|
|
3462
3611
|
};
|
|
3463
3612
|
|
|
3613
|
+
//#endregion
|
|
3614
|
+
//#region packages/core/src/defaults/ui-annotations.ts
|
|
3615
|
+
const uiAnnotations = {
|
|
3616
|
+
placeholder: new AnnotationSpec({
|
|
3617
|
+
description: "Defines **placeholder text** for UI input fields.\n\n**Example:**\n```atscript\n@ui.placeholder \"Enter your name\"\nname: string\n```\n",
|
|
3618
|
+
nodeType: ["prop", "type"],
|
|
3619
|
+
argument: {
|
|
3620
|
+
name: "text",
|
|
3621
|
+
type: "string",
|
|
3622
|
+
description: "The placeholder text to display in UI input fields."
|
|
3623
|
+
}
|
|
3624
|
+
}),
|
|
3625
|
+
component: new AnnotationSpec({
|
|
3626
|
+
description: "Hints which **UI component** to use when rendering this field.\n\n**Example:**\n```atscript\n@ui.component \"select\"\ncountry: string\n```\n",
|
|
3627
|
+
nodeType: ["prop", "type"],
|
|
3628
|
+
argument: {
|
|
3629
|
+
name: "name",
|
|
3630
|
+
type: "string",
|
|
3631
|
+
description: "The component name or type to use for rendering."
|
|
3632
|
+
}
|
|
3633
|
+
}),
|
|
3634
|
+
hidden: new AnnotationSpec({
|
|
3635
|
+
description: "Hides this field or entity from **UI forms and tables** entirely.\n\n**Example:**\n```atscript\n@ui.hidden\ninternalId: string\n```\n",
|
|
3636
|
+
nodeType: [
|
|
3637
|
+
"prop",
|
|
3638
|
+
"type",
|
|
3639
|
+
"interface"
|
|
3640
|
+
]
|
|
3641
|
+
}),
|
|
3642
|
+
group: new AnnotationSpec({
|
|
3643
|
+
description: "Groups fields into **form sections**. Fields sharing the same group name are rendered together.\n\n**Example:**\n```atscript\n@ui.group \"personal\"\nfirstName: string\n\n@ui.group \"personal\"\nlastName: string\n\n@ui.group \"contact\"\nemail: string.email\n```\n",
|
|
3644
|
+
nodeType: ["prop"],
|
|
3645
|
+
argument: {
|
|
3646
|
+
name: "name",
|
|
3647
|
+
type: "string",
|
|
3648
|
+
description: "The section/group name to place this field in."
|
|
3649
|
+
}
|
|
3650
|
+
}),
|
|
3651
|
+
order: new AnnotationSpec({
|
|
3652
|
+
description: "Sets the **display order** of a field in auto-generated forms. Lower numbers appear first.\n\n**Example:**\n```atscript\n@ui.order 1\nname: string\n\n@ui.order 2\nemail: string.email\n```\n",
|
|
3653
|
+
nodeType: ["prop"],
|
|
3654
|
+
argument: {
|
|
3655
|
+
name: "order",
|
|
3656
|
+
type: "number",
|
|
3657
|
+
description: "Display order number. Lower values appear first."
|
|
3658
|
+
}
|
|
3659
|
+
}),
|
|
3660
|
+
width: new AnnotationSpec({
|
|
3661
|
+
description: "Provides a **layout width hint** for the field in auto-generated forms.\n\n**Example:**\n```atscript\n@ui.width \"half\"\nfirstName: string\n```\n",
|
|
3662
|
+
nodeType: ["prop", "type"],
|
|
3663
|
+
argument: {
|
|
3664
|
+
name: "width",
|
|
3665
|
+
type: "string",
|
|
3666
|
+
description: "Layout width hint (e.g., \"half\", \"full\", \"third\", \"quarter\")."
|
|
3667
|
+
}
|
|
3668
|
+
}),
|
|
3669
|
+
icon: new AnnotationSpec({
|
|
3670
|
+
description: "Provides an **icon hint** for the field or entity.\n\n**Example:**\n```atscript\n@ui.icon \"mail\"\nemail: string.email\n```\n",
|
|
3671
|
+
nodeType: [
|
|
3672
|
+
"prop",
|
|
3673
|
+
"type",
|
|
3674
|
+
"interface"
|
|
3675
|
+
],
|
|
3676
|
+
argument: {
|
|
3677
|
+
name: "name",
|
|
3678
|
+
type: "string",
|
|
3679
|
+
description: "Icon name or identifier."
|
|
3680
|
+
}
|
|
3681
|
+
}),
|
|
3682
|
+
hint: new AnnotationSpec({
|
|
3683
|
+
description: "Provides **help text or tooltip** displayed near the field in UI forms.\n\n**Example:**\n```atscript\n@ui.hint \"Must be a valid business email\"\nemail: string.email\n```\n",
|
|
3684
|
+
nodeType: ["prop", "type"],
|
|
3685
|
+
argument: {
|
|
3686
|
+
name: "text",
|
|
3687
|
+
type: "string",
|
|
3688
|
+
description: "Help text or tooltip content."
|
|
3689
|
+
}
|
|
3690
|
+
}),
|
|
3691
|
+
disabled: new AnnotationSpec({
|
|
3692
|
+
description: "Marks a field as **disabled** (rendered but non-interactive) in UI forms.\n\n**Example:**\n```atscript\n@ui.disabled\nreferralCode: string\n```\n",
|
|
3693
|
+
nodeType: ["prop", "type"]
|
|
3694
|
+
}),
|
|
3695
|
+
type: new AnnotationSpec({
|
|
3696
|
+
description: "Specifies the **input type** for the field in UI forms. Maps to HTML input types or framework-specific equivalents.\n\n**Example:**\n```atscript\n@ui.type \"textarea\"\nbio: string\n\n@ui.type \"password\"\nsecret: string\n```\n",
|
|
3697
|
+
nodeType: ["prop", "type"],
|
|
3698
|
+
argument: {
|
|
3699
|
+
name: "type",
|
|
3700
|
+
type: "string",
|
|
3701
|
+
description: "Input type (e.g., \"text\", \"textarea\", \"password\", \"number\", \"date\", \"color\", \"range\")."
|
|
3702
|
+
}
|
|
3703
|
+
}),
|
|
3704
|
+
attr: new AnnotationSpec({
|
|
3705
|
+
description: "Passes an arbitrary **HTML/component attribute** to the rendered field. Multiple `@ui.attr` annotations can be used on the same field.\n\n**Example:**\n```atscript\n@ui.attr \"rows\", \"5\"\n@ui.attr \"autocomplete\", \"off\"\nbio: string\n```\n",
|
|
3706
|
+
nodeType: [
|
|
3707
|
+
"prop",
|
|
3708
|
+
"type",
|
|
3709
|
+
"interface"
|
|
3710
|
+
],
|
|
3711
|
+
multiple: true,
|
|
3712
|
+
mergeStrategy: "append",
|
|
3713
|
+
argument: [{
|
|
3714
|
+
name: "key",
|
|
3715
|
+
type: "string",
|
|
3716
|
+
description: "Attribute name."
|
|
3717
|
+
}, {
|
|
3718
|
+
name: "value",
|
|
3719
|
+
type: "string",
|
|
3720
|
+
description: "Attribute value."
|
|
3721
|
+
}]
|
|
3722
|
+
}),
|
|
3723
|
+
class: new AnnotationSpec({
|
|
3724
|
+
description: "Adds **CSS class names** to the rendered field or entity. Multiple `@ui.class` annotations are appended.\n\n**Example:**\n```atscript\n@ui.class \"text-bold\"\n@ui.class \"mt-4\"\ntitle: string\n```\n",
|
|
3725
|
+
nodeType: [
|
|
3726
|
+
"prop",
|
|
3727
|
+
"type",
|
|
3728
|
+
"interface"
|
|
3729
|
+
],
|
|
3730
|
+
multiple: true,
|
|
3731
|
+
mergeStrategy: "append",
|
|
3732
|
+
argument: {
|
|
3733
|
+
name: "names",
|
|
3734
|
+
type: "string",
|
|
3735
|
+
description: "One or more CSS class names (space-separated)."
|
|
3736
|
+
}
|
|
3737
|
+
}),
|
|
3738
|
+
style: new AnnotationSpec({
|
|
3739
|
+
description: "Adds **inline CSS styles** to the rendered field or entity. Multiple `@ui.style` annotations are appended.\n\n**Example:**\n```atscript\n@ui.style \"color: red\"\n@ui.style \"font-weight: bold\"\nwarning: string\n```\n",
|
|
3740
|
+
nodeType: [
|
|
3741
|
+
"prop",
|
|
3742
|
+
"type",
|
|
3743
|
+
"interface"
|
|
3744
|
+
],
|
|
3745
|
+
multiple: true,
|
|
3746
|
+
mergeStrategy: "append",
|
|
3747
|
+
argument: {
|
|
3748
|
+
name: "css",
|
|
3749
|
+
type: "string",
|
|
3750
|
+
description: "CSS style declarations (semicolon-separated)."
|
|
3751
|
+
}
|
|
3752
|
+
})
|
|
3753
|
+
};
|
|
3754
|
+
|
|
3464
3755
|
//#endregion
|
|
3465
3756
|
//#region packages/core/src/default-atscript-config.ts
|
|
3466
3757
|
function getDefaultAtscriptConfig() {
|
|
@@ -3469,7 +3760,9 @@ function getDefaultAtscriptConfig() {
|
|
|
3469
3760
|
annotations: {
|
|
3470
3761
|
meta: { ...metaAnnotations },
|
|
3471
3762
|
expect: { ...expectAnnotations },
|
|
3472
|
-
emit: { ...emitAnnotations }
|
|
3763
|
+
emit: { ...emitAnnotations },
|
|
3764
|
+
db: { ...dbAnnotations },
|
|
3765
|
+
ui: { ...uiAnnotations }
|
|
3473
3766
|
}
|
|
3474
3767
|
};
|
|
3475
3768
|
return defaulTAtscriptConfig;
|
|
@@ -3497,7 +3790,7 @@ var PluginManager = class {
|
|
|
3497
3790
|
this._docConfig = {};
|
|
3498
3791
|
if (raw?.primitives) {
|
|
3499
3792
|
this._docConfig.primitives = this._docConfig.primitives || new Map();
|
|
3500
|
-
for (const [key, value] of Object.entries(raw.primitives)) this._docConfig.primitives.set(key, new SemanticPrimitiveNode(key, value));
|
|
3793
|
+
for (const [key, value] of Object.entries(raw.primitives)) this._docConfig.primitives.set(key, new SemanticPrimitiveNode(key, value, "", raw.annotations));
|
|
3501
3794
|
}
|
|
3502
3795
|
if (raw?.annotations) {
|
|
3503
3796
|
this._docConfig.annotations = this._docConfig.annotations || {};
|