@atscript/typescript 0.1.26 → 0.1.27
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/README.md +50 -0
- package/dist/cli.cjs +4 -4
- package/dist/index.cjs +7 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +5 -9
- package/dist/utils.cjs +1 -1
- package/dist/utils.mjs +1 -1
- package/package.json +5 -5
- package/scripts/setup-skills.js +23 -13
- package/skills/atscript-typescript/SKILL.md +22 -14
- package/skills/atscript-typescript/annotations.md +51 -50
- package/skills/atscript-typescript/codegen.md +14 -10
- package/skills/atscript-typescript/core.md +23 -21
- package/skills/atscript-typescript/runtime.md +66 -54
- package/skills/atscript-typescript/syntax.md +31 -31
- package/skills/atscript-typescript/utilities.md +94 -67
- package/skills/atscript-typescript/validation.md +39 -39
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @atscript/typescript
|
|
2
|
+
|
|
3
|
+
TypeScript language extension for [Atscript](https://atscript.moost.org). Compiles `.as` files into `.d.ts` type declarations and `.js` runtime modules with full metadata, validation, and JSON Schema support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @atscript/typescript @atscript/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
For build-tool integration (Vite, Webpack, Rollup, esbuild, Rspack), also add:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add unplugin-atscript
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## AI Agent Skills
|
|
18
|
+
|
|
19
|
+
This package ships with structured skill files for AI coding agents (Claude Code, Cursor, Windsurf, Codex, etc.).
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Project-local (recommended — version-locked, commits with your repo)
|
|
23
|
+
npx atscript-typescript-skill
|
|
24
|
+
|
|
25
|
+
# Global (available across all your projects)
|
|
26
|
+
npx atscript-typescript-skill --global
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To keep skills automatically up-to-date, add a postinstall script to your `package.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"scripts": {
|
|
34
|
+
"postinstall": "atscript-typescript-skill --postinstall"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Type declarations** — `.d.ts` files from `.as` interfaces and types
|
|
42
|
+
- **Runtime metadata** — `.js` files with full metadata for validators and serializers
|
|
43
|
+
- **JSON Schema** — Build, parse, and merge JSON schemas from annotated types
|
|
44
|
+
- **Validation** — Validate data against types with plugin support
|
|
45
|
+
- **Serialization** — JSON-safe round-trip serialization/deserialization
|
|
46
|
+
- **CLI** — `asc` command for compiling `.as` files
|
|
47
|
+
|
|
48
|
+
## Documentation
|
|
49
|
+
|
|
50
|
+
Full documentation: [atscript.moost.org](https://atscript.moost.org)
|
package/dist/cli.cjs
CHANGED
|
@@ -1089,7 +1089,7 @@ function buildJsonSchema(type) {
|
|
|
1089
1089
|
const oneOf = d.type.items.map(build$1);
|
|
1090
1090
|
const mapping = {};
|
|
1091
1091
|
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
1092
|
-
const idx = Number.parseInt(origPath.split("/").pop());
|
|
1092
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1093
1093
|
const item = d.type.items[idx];
|
|
1094
1094
|
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
1095
1095
|
else mapping[val] = origPath;
|
|
@@ -1176,7 +1176,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1176
1176
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
1177
1177
|
const nameCounts = new Map();
|
|
1178
1178
|
const nodesByName = new Map();
|
|
1179
|
-
for (const node of this.doc.nodes) if (node.__typeId
|
|
1179
|
+
for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1180
1180
|
const name = node.id;
|
|
1181
1181
|
nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
|
|
1182
1182
|
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
@@ -1202,7 +1202,7 @@ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}_
|
|
|
1202
1202
|
* meaning annotations target properties inside a referenced type.
|
|
1203
1203
|
*/ hasAdHocAnnotationsThroughRef() {
|
|
1204
1204
|
if (!this._adHocAnnotations || this._propPath.length === 0) return false;
|
|
1205
|
-
const prefix = this._propPath.join(".")
|
|
1205
|
+
const prefix = `${this._propPath.join(".")}.`;
|
|
1206
1206
|
for (const key of this._adHocAnnotations.keys()) if (key.startsWith(prefix)) return true;
|
|
1207
1207
|
return false;
|
|
1208
1208
|
}
|
|
@@ -1316,7 +1316,7 @@ else {
|
|
|
1316
1316
|
case "type": {
|
|
1317
1317
|
const def = node.getDefinition();
|
|
1318
1318
|
const handle = this.toAnnotatedHandle(def, true);
|
|
1319
|
-
const typeId = this.typeIds.get(node) ?? (node.__typeId
|
|
1319
|
+
const typeId = this.typeIds.get(node) ?? (node.__typeId !== null && node.__typeId !== undefined ? node.id : undefined);
|
|
1320
1320
|
if (typeId) handle.id(typeId);
|
|
1321
1321
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1322
1322
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
3
|
//#region rolldown:runtime
|
|
3
4
|
var __create = Object.create;
|
|
4
5
|
var __defProp = Object.defineProperty;
|
|
@@ -1086,7 +1087,7 @@ function buildJsonSchema(type) {
|
|
|
1086
1087
|
const oneOf = d.type.items.map(build);
|
|
1087
1088
|
const mapping = {};
|
|
1088
1089
|
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
1089
|
-
const idx = Number.parseInt(origPath.split("/").pop());
|
|
1090
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1090
1091
|
const item = d.type.items[idx];
|
|
1091
1092
|
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
1092
1093
|
else mapping[val] = origPath;
|
|
@@ -1173,7 +1174,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1173
1174
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
1174
1175
|
const nameCounts = new Map();
|
|
1175
1176
|
const nodesByName = new Map();
|
|
1176
|
-
for (const node of this.doc.nodes) if (node.__typeId
|
|
1177
|
+
for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1177
1178
|
const name = node.id;
|
|
1178
1179
|
nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
|
|
1179
1180
|
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
@@ -1199,7 +1200,7 @@ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}_
|
|
|
1199
1200
|
* meaning annotations target properties inside a referenced type.
|
|
1200
1201
|
*/ hasAdHocAnnotationsThroughRef() {
|
|
1201
1202
|
if (!this._adHocAnnotations || this._propPath.length === 0) return false;
|
|
1202
|
-
const prefix = this._propPath.join(".")
|
|
1203
|
+
const prefix = `${this._propPath.join(".")}.`;
|
|
1203
1204
|
for (const key of this._adHocAnnotations.keys()) if (key.startsWith(prefix)) return true;
|
|
1204
1205
|
return false;
|
|
1205
1206
|
}
|
|
@@ -1313,7 +1314,7 @@ else {
|
|
|
1313
1314
|
case "type": {
|
|
1314
1315
|
const def = node.getDefinition();
|
|
1315
1316
|
const handle = this.toAnnotatedHandle(def, true);
|
|
1316
|
-
const typeId = this.typeIds.get(node) ?? (node.__typeId
|
|
1317
|
+
const typeId = this.typeIds.get(node) ?? (node.__typeId !== null && node.__typeId !== undefined ? node.id : undefined);
|
|
1317
1318
|
if (typeId) handle.id(typeId);
|
|
1318
1319
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1319
1320
|
}
|
|
@@ -1870,8 +1871,5 @@ else return t.optional ? `${t.type} | true` : t.type;
|
|
|
1870
1871
|
};
|
|
1871
1872
|
|
|
1872
1873
|
//#endregion
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
//#endregion
|
|
1877
|
-
module.exports = src_default;
|
|
1874
|
+
exports.default = tsPlugin
|
|
1875
|
+
exports.tsPlugin = tsPlugin
|
package/dist/index.d.ts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1062,7 +1062,7 @@ function buildJsonSchema(type) {
|
|
|
1062
1062
|
const oneOf = d.type.items.map(build);
|
|
1063
1063
|
const mapping = {};
|
|
1064
1064
|
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
1065
|
-
const idx = Number.parseInt(origPath.split("/").pop());
|
|
1065
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1066
1066
|
const item = d.type.items[idx];
|
|
1067
1067
|
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
1068
1068
|
else mapping[val] = origPath;
|
|
@@ -1149,7 +1149,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1149
1149
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
1150
1150
|
const nameCounts = new Map();
|
|
1151
1151
|
const nodesByName = new Map();
|
|
1152
|
-
for (const node of this.doc.nodes) if (node.__typeId
|
|
1152
|
+
for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1153
1153
|
const name = node.id;
|
|
1154
1154
|
nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
|
|
1155
1155
|
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
@@ -1175,7 +1175,7 @@ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}_
|
|
|
1175
1175
|
* meaning annotations target properties inside a referenced type.
|
|
1176
1176
|
*/ hasAdHocAnnotationsThroughRef() {
|
|
1177
1177
|
if (!this._adHocAnnotations || this._propPath.length === 0) return false;
|
|
1178
|
-
const prefix = this._propPath.join(".")
|
|
1178
|
+
const prefix = `${this._propPath.join(".")}.`;
|
|
1179
1179
|
for (const key of this._adHocAnnotations.keys()) if (key.startsWith(prefix)) return true;
|
|
1180
1180
|
return false;
|
|
1181
1181
|
}
|
|
@@ -1289,7 +1289,7 @@ else {
|
|
|
1289
1289
|
case "type": {
|
|
1290
1290
|
const def = node.getDefinition();
|
|
1291
1291
|
const handle = this.toAnnotatedHandle(def, true);
|
|
1292
|
-
const typeId = this.typeIds.get(node) ?? (node.__typeId
|
|
1292
|
+
const typeId = this.typeIds.get(node) ?? (node.__typeId !== null && node.__typeId !== undefined ? node.id : undefined);
|
|
1293
1293
|
if (typeId) handle.id(typeId);
|
|
1294
1294
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1295
1295
|
}
|
|
@@ -1846,8 +1846,4 @@ else return t.optional ? `${t.type} | true` : t.type;
|
|
|
1846
1846
|
};
|
|
1847
1847
|
|
|
1848
1848
|
//#endregion
|
|
1849
|
-
|
|
1850
|
-
var src_default = tsPlugin;
|
|
1851
|
-
|
|
1852
|
-
//#endregion
|
|
1853
|
-
export { src_default as default };
|
|
1849
|
+
export { tsPlugin as default, tsPlugin };
|
package/dist/utils.cjs
CHANGED
|
@@ -707,7 +707,7 @@ function buildJsonSchema(type) {
|
|
|
707
707
|
const oneOf = d.type.items.map(build$1);
|
|
708
708
|
const mapping = {};
|
|
709
709
|
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
710
|
-
const idx = Number.parseInt(origPath.split("/").pop());
|
|
710
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
711
711
|
const item = d.type.items[idx];
|
|
712
712
|
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
713
713
|
else mapping[val] = origPath;
|
package/dist/utils.mjs
CHANGED
|
@@ -706,7 +706,7 @@ function buildJsonSchema(type) {
|
|
|
706
706
|
const oneOf = d.type.items.map(build$1);
|
|
707
707
|
const mapping = {};
|
|
708
708
|
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
709
|
-
const idx = Number.parseInt(origPath.split("/").pop());
|
|
709
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
710
710
|
const item = d.type.items[idx];
|
|
711
711
|
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
712
712
|
else mapping[val] = origPath;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/typescript",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "Atscript: typescript-gen support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"bin": {
|
|
22
22
|
"asc": "./cli.cjs",
|
|
23
|
-
"
|
|
23
|
+
"atscript-typescript-skill": "./scripts/setup-skills.js"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
"./package.json": "./package.json"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@moostjs/event-cli": "^0.
|
|
61
|
-
"moost": "^0.
|
|
60
|
+
"@moostjs/event-cli": "^0.6.0",
|
|
61
|
+
"moost": "^0.6.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"vitest": "3.2.4"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@atscript/core": "^0.1.
|
|
67
|
+
"@atscript/core": "^0.1.27"
|
|
68
68
|
},
|
|
69
69
|
"build": [
|
|
70
70
|
{},
|
package/scripts/setup-skills.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/* prettier-ignore */
|
|
3
3
|
import fs from 'fs'
|
|
4
|
-
import path from 'path'
|
|
5
4
|
import os from 'os'
|
|
5
|
+
import path from 'path'
|
|
6
6
|
import { fileURLToPath } from 'url'
|
|
7
7
|
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -17,17 +17,18 @@ if (!fs.existsSync(SKILL_SRC)) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const AGENTS = {
|
|
20
|
-
'Claude Code': { dir: '.claude/skills',
|
|
21
|
-
'Cursor':
|
|
22
|
-
'Windsurf':
|
|
23
|
-
'Codex':
|
|
24
|
-
'OpenCode':
|
|
20
|
+
'Claude Code': { dir: '.claude/skills', global: path.join(os.homedir(), '.claude', 'skills') },
|
|
21
|
+
'Cursor': { dir: '.cursor/skills', global: path.join(os.homedir(), '.cursor', 'skills') },
|
|
22
|
+
'Windsurf': { dir: '.windsurf/skills', global: path.join(os.homedir(), '.windsurf', 'skills') },
|
|
23
|
+
'Codex': { dir: '.codex/skills', global: path.join(os.homedir(), '.codex', 'skills') },
|
|
24
|
+
'OpenCode': { dir: '.opencode/skills', global: path.join(os.homedir(), '.opencode', 'skills') },
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const args = process.argv.slice(2)
|
|
28
28
|
const isGlobal = args.includes('--global') || args.includes('-g')
|
|
29
29
|
const isPostinstall = args.includes('--postinstall')
|
|
30
|
-
let installed = 0,
|
|
30
|
+
let installed = 0,
|
|
31
|
+
skipped = 0
|
|
31
32
|
const installedDirs = []
|
|
32
33
|
|
|
33
34
|
for (const [agentName, cfg] of Object.entries(AGENTS)) {
|
|
@@ -36,7 +37,10 @@ for (const [agentName, cfg] of Object.entries(AGENTS)) {
|
|
|
36
37
|
|
|
37
38
|
// In postinstall mode: silently skip agents that aren't set up globally
|
|
38
39
|
if (isPostinstall || isGlobal) {
|
|
39
|
-
if (!fs.existsSync(agentRootDir)) {
|
|
40
|
+
if (!fs.existsSync(agentRootDir)) {
|
|
41
|
+
skipped++
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
const dest = path.join(targetBase, SKILL_NAME)
|
|
@@ -55,13 +59,17 @@ for (const [agentName, cfg] of Object.entries(AGENTS)) {
|
|
|
55
59
|
if (!isGlobal && installedDirs.length > 0) {
|
|
56
60
|
const gitignorePath = path.join(process.cwd(), '.gitignore')
|
|
57
61
|
let gitignoreContent = ''
|
|
58
|
-
try {
|
|
62
|
+
try {
|
|
63
|
+
gitignoreContent = fs.readFileSync(gitignorePath, 'utf8')
|
|
64
|
+
} catch {}
|
|
59
65
|
const linesToAdd = installedDirs.filter(d => !gitignoreContent.includes(d))
|
|
60
66
|
if (linesToAdd.length > 0) {
|
|
61
67
|
const hasHeader = gitignoreContent.includes('# AI agent skills')
|
|
62
|
-
const block =
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
const block =
|
|
69
|
+
(gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '') +
|
|
70
|
+
(hasHeader ? '' : '\n# AI agent skills (auto-generated by setup-skills)\n') +
|
|
71
|
+
linesToAdd.join('\n') +
|
|
72
|
+
'\n'
|
|
65
73
|
fs.appendFileSync(gitignorePath, block)
|
|
66
74
|
console.log(`📝 Added ${linesToAdd.length} entries to .gitignore`)
|
|
67
75
|
}
|
|
@@ -70,7 +78,9 @@ if (!isGlobal && installedDirs.length > 0) {
|
|
|
70
78
|
if (installed === 0 && isPostinstall) {
|
|
71
79
|
// Silence is fine — no agents present, nothing to do
|
|
72
80
|
} else if (installed === 0 && skipped === Object.keys(AGENTS).length) {
|
|
73
|
-
console.log(
|
|
81
|
+
console.log(
|
|
82
|
+
'No agent directories detected. Try --global or run without it for project-local install.'
|
|
83
|
+
)
|
|
74
84
|
} else if (installed === 0) {
|
|
75
85
|
console.log('Nothing installed. Run without --global to install project-locally.')
|
|
76
86
|
} else {
|
|
@@ -11,15 +11,15 @@ Atscript is a universal type and metadata description language. `@atscript/types
|
|
|
11
11
|
|
|
12
12
|
Read the domain file that matches the task. Do not load all files — only what you need.
|
|
13
13
|
|
|
14
|
-
| Domain
|
|
15
|
-
|
|
16
|
-
| Setup & configuration
|
|
17
|
-
| `.as` file syntax
|
|
14
|
+
| Domain | File | Load when… |
|
|
15
|
+
| ------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
16
|
+
| Setup & configuration | [core.md](core.md) | Installing, configuring `atscript.config.ts`, using the `tsPlugin`, running the CLI |
|
|
17
|
+
| `.as` file syntax | [syntax.md](syntax.md) | Writing `.as` files — interfaces, types, imports/exports, property syntax |
|
|
18
18
|
| Annotations & primitives | [annotations.md](annotations.md) | Using built-in `@meta.*`/`@expect.*` annotations, defining custom annotations or primitives |
|
|
19
|
-
| Code generation
|
|
20
|
-
| Runtime type system
|
|
21
|
-
| Validation
|
|
22
|
-
| Utility functions
|
|
19
|
+
| Code generation | [codegen.md](codegen.md) | Understanding what `.d.ts` and `.js` files are generated, `atscript.d.ts` global types |
|
|
20
|
+
| Runtime type system | [runtime.md](runtime.md) | Reading/writing metadata, walking type definitions, understanding `TAtscriptAnnotatedType` |
|
|
21
|
+
| Validation | [validation.md](validation.md) | Validating data, type guards, error handling, custom validator plugins |
|
|
22
|
+
| Utility functions | [utilities.md](utilities.md) | Serialization, flattening, JSON Schema, `createDataFromAnnotatedType`, `forAnnotatedType` |
|
|
23
23
|
|
|
24
24
|
## Quick reference
|
|
25
25
|
|
|
@@ -29,12 +29,20 @@ import tsPlugin from '@atscript/typescript'
|
|
|
29
29
|
|
|
30
30
|
// Runtime utilities (used in app code)
|
|
31
31
|
import {
|
|
32
|
-
defineAnnotatedType,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
defineAnnotatedType,
|
|
33
|
+
isAnnotatedType,
|
|
34
|
+
annotate,
|
|
35
|
+
Validator,
|
|
36
|
+
ValidatorError,
|
|
37
|
+
buildJsonSchema,
|
|
38
|
+
fromJsonSchema,
|
|
39
|
+
mergeJsonSchemas,
|
|
40
|
+
serializeAnnotatedType,
|
|
41
|
+
deserializeAnnotatedType,
|
|
42
|
+
flattenAnnotatedType,
|
|
43
|
+
createDataFromAnnotatedType,
|
|
44
|
+
forAnnotatedType,
|
|
45
|
+
throwFeatureDisabled,
|
|
38
46
|
} from '@atscript/typescript/utils'
|
|
39
47
|
|
|
40
48
|
// CLI
|
|
@@ -6,36 +6,36 @@
|
|
|
6
6
|
|
|
7
7
|
### `@meta.*` — Metadata Annotations
|
|
8
8
|
|
|
9
|
-
| Annotation
|
|
10
|
-
|
|
11
|
-
| `@meta.label`
|
|
12
|
-
| `@meta.id`
|
|
13
|
-
| `@meta.description`
|
|
14
|
-
| `@meta.documentation` | `text: string`
|
|
15
|
-
| `@meta.placeholder`
|
|
16
|
-
| `@meta.sensitive`
|
|
17
|
-
| `@meta.readonly`
|
|
18
|
-
| `@meta.required`
|
|
19
|
-
| `@meta.default`
|
|
20
|
-
| `@meta.example`
|
|
21
|
-
| `@meta.isKey`
|
|
9
|
+
| Annotation | Arguments | Description |
|
|
10
|
+
| --------------------- | -------------------------- | ------------------------------------------------------------------ |
|
|
11
|
+
| `@meta.label` | `text: string` | Human-readable label for UI, logs, documentation |
|
|
12
|
+
| `@meta.id` | `name?: string` (optional) | Mark field as unique identifier; optional custom name |
|
|
13
|
+
| `@meta.description` | `text: string` | Detailed description of a field or entity |
|
|
14
|
+
| `@meta.documentation` | `text: string` | Multi-line docs (Markdown). Multiple allowed — each appends |
|
|
15
|
+
| `@meta.placeholder` | `text: string` | Placeholder for UI input fields (props/types only) |
|
|
16
|
+
| `@meta.sensitive` | _(none)_ | Mark as sensitive (passwords, API keys). Strips from serialization |
|
|
17
|
+
| `@meta.readonly` | _(none)_ | Mark as read-only |
|
|
18
|
+
| `@meta.required` | `message?: string` | Required field. Strings: non-whitespace. Booleans: must be `true` |
|
|
19
|
+
| `@meta.default` | `value: string` | Default value (strings as-is, others parsed as JSON) |
|
|
20
|
+
| `@meta.example` | `value: string` | Example value (strings as-is, others parsed as JSON) |
|
|
21
|
+
| `@meta.isKey` | _(none)_ | Mark field as key inside array (string/number types only) |
|
|
22
22
|
|
|
23
23
|
### `@expect.*` — Validation Constraints
|
|
24
24
|
|
|
25
|
-
| Annotation
|
|
26
|
-
|
|
27
|
-
| `@expect.minLength` | `length: number`, `message?: string`
|
|
28
|
-
| `@expect.maxLength` | `length: number`, `message?: string`
|
|
29
|
-
| `@expect.min`
|
|
30
|
-
| `@expect.max`
|
|
31
|
-
| `@expect.int`
|
|
32
|
-
| `@expect.pattern`
|
|
25
|
+
| Annotation | Arguments | Applies To | Description |
|
|
26
|
+
| ------------------- | ------------------------------------------------------- | ------------- | ------------------------------------------------------ |
|
|
27
|
+
| `@expect.minLength` | `length: number`, `message?: string` | string, array | Minimum length |
|
|
28
|
+
| `@expect.maxLength` | `length: number`, `message?: string` | string, array | Maximum length |
|
|
29
|
+
| `@expect.min` | `minValue: number`, `message?: string` | number | Minimum value |
|
|
30
|
+
| `@expect.max` | `maxValue: number`, `message?: string` | number | Maximum value |
|
|
31
|
+
| `@expect.int` | _(none)_ | number | Must be integer |
|
|
32
|
+
| `@expect.pattern` | `pattern: string`, `flags?: string`, `message?: string` | string | Regex validation. **Multiple allowed** (all must pass) |
|
|
33
33
|
|
|
34
34
|
### `@emit.*` — Build-time Directives
|
|
35
35
|
|
|
36
|
-
| Annotation
|
|
37
|
-
|
|
38
|
-
| `@emit.jsonSchema` | interface
|
|
36
|
+
| Annotation | Applies To | Description |
|
|
37
|
+
| ------------------ | ---------- | ----------------------------------------------- |
|
|
38
|
+
| `@emit.jsonSchema` | interface | Pre-compute and embed JSON Schema at build time |
|
|
39
39
|
|
|
40
40
|
## Custom Annotations
|
|
41
41
|
|
|
@@ -102,10 +102,10 @@ new AnnotationSpec({
|
|
|
102
102
|
],
|
|
103
103
|
|
|
104
104
|
// Allow multiple instances on the same target
|
|
105
|
-
multiple: true,
|
|
105
|
+
multiple: true, // default: false
|
|
106
106
|
|
|
107
107
|
// How duplicates merge: 'replace' (last wins) or 'append' (collect into array)
|
|
108
|
-
mergeStrategy: 'append',
|
|
108
|
+
mergeStrategy: 'append', // default: 'replace'
|
|
109
109
|
|
|
110
110
|
// Human-readable description
|
|
111
111
|
description: 'What this annotation does',
|
|
@@ -124,13 +124,13 @@ new AnnotationSpec({
|
|
|
124
124
|
|
|
125
125
|
Each argument accepts:
|
|
126
126
|
|
|
127
|
-
| Field
|
|
128
|
-
|
|
129
|
-
| `name`
|
|
130
|
-
| `type`
|
|
131
|
-
| `optional`
|
|
132
|
-
| `description` | `string`
|
|
133
|
-
| `values`
|
|
127
|
+
| Field | Type | Description |
|
|
128
|
+
| ------------- | ----------------------------------- | ------------------------------------------- |
|
|
129
|
+
| `name` | `string` | Argument name (used in metadata object key) |
|
|
130
|
+
| `type` | `'string' \| 'number' \| 'boolean'` | Expected type |
|
|
131
|
+
| `optional` | `boolean` | Whether the argument can be omitted |
|
|
132
|
+
| `description` | `string` | Human-readable description |
|
|
133
|
+
| `values` | `string[]` | Allowed values (enum-like constraint) |
|
|
134
134
|
|
|
135
135
|
### How Annotations Map to Runtime Metadata
|
|
136
136
|
|
|
@@ -140,6 +140,7 @@ Each argument accepts:
|
|
|
140
140
|
- **`multiple: true`** → metadata value is an array
|
|
141
141
|
|
|
142
142
|
Example: `@api.endpoint "/users" "GET"` becomes:
|
|
143
|
+
|
|
143
144
|
```ts
|
|
144
145
|
metadata.get('api.endpoint') // → { path: "/users", method: "GET" }
|
|
145
146
|
```
|
|
@@ -200,26 +201,26 @@ export default defineConfig({
|
|
|
200
201
|
|
|
201
202
|
### `TPrimitiveConfig` Options
|
|
202
203
|
|
|
203
|
-
| Field
|
|
204
|
-
|
|
205
|
-
| `type`
|
|
206
|
-
| `tags`
|
|
207
|
-
| `documentation` | `string`
|
|
208
|
-
| `expect`
|
|
209
|
-
| `extensions`
|
|
204
|
+
| Field | Type | Description |
|
|
205
|
+
| --------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
206
|
+
| `type` | `TPrimitiveTypeDef` | Base type: `'string'`, `'number'`, `'boolean'`, `'void'`, `'null'`, `'phantom'`, or complex type |
|
|
207
|
+
| `tags` | `string[]` | Custom tags for categorization |
|
|
208
|
+
| `documentation` | `string` | Documentation string |
|
|
209
|
+
| `expect` | object | Built-in validation constraints |
|
|
210
|
+
| `extensions` | `Record<string, Partial<TPrimitiveConfig>>` | Sub-types accessible via dot notation |
|
|
210
211
|
|
|
211
212
|
### `expect` Validation on Primitives
|
|
212
213
|
|
|
213
|
-
| Field
|
|
214
|
-
|
|
215
|
-
| `min`
|
|
216
|
-
| `max`
|
|
217
|
-
| `int`
|
|
218
|
-
| `minLength` | string, array
|
|
219
|
-
| `maxLength` | string, array
|
|
220
|
-
| `pattern`
|
|
221
|
-
| `required`
|
|
222
|
-
| `message`
|
|
214
|
+
| Field | Applies To | Description |
|
|
215
|
+
| ----------- | --------------- | -------------------------------- |
|
|
216
|
+
| `min` | number | Minimum value |
|
|
217
|
+
| `max` | number | Maximum value |
|
|
218
|
+
| `int` | number | Must be integer |
|
|
219
|
+
| `minLength` | string, array | Minimum length |
|
|
220
|
+
| `maxLength` | string, array | Maximum length |
|
|
221
|
+
| `pattern` | string | Regex pattern(s) |
|
|
222
|
+
| `required` | string, boolean | Non-empty / must be true |
|
|
223
|
+
| `message` | any | Custom error message for pattern |
|
|
223
224
|
|
|
224
225
|
### Usage in `.as` Files
|
|
225
226
|
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
Each `.as` file produces two outputs:
|
|
8
8
|
|
|
9
|
-
| Output
|
|
10
|
-
|
|
9
|
+
| Output | Generated By | Contains |
|
|
10
|
+
| ----------- | ------------------------------ | ---------------------------------------------------------------------------------------------------- |
|
|
11
11
|
| `*.as.d.ts` | `npx asc -f dts` or build tool | TypeScript type declarations — interfaces become `declare class` with static type/metadata/validator |
|
|
12
|
-
| `*.as.js`
|
|
12
|
+
| `*.as.js` | Build tool (unplugin-atscript) | Runtime module — classes with full type definitions, metadata maps, and validator factories |
|
|
13
13
|
|
|
14
14
|
## `.d.ts` Output
|
|
15
15
|
|
|
@@ -31,9 +31,12 @@ Generates `user.as.d.ts`:
|
|
|
31
31
|
|
|
32
32
|
```ts
|
|
33
33
|
import type {
|
|
34
|
-
TAtscriptTypeObject,
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
TAtscriptTypeObject,
|
|
35
|
+
TAtscriptAnnotatedType,
|
|
36
|
+
TMetadataMap,
|
|
37
|
+
Validator,
|
|
38
|
+
TValidatorOptions,
|
|
39
|
+
} from '@atscript/typescript/utils'
|
|
37
40
|
|
|
38
41
|
export declare class User {
|
|
39
42
|
name: string
|
|
@@ -48,7 +51,7 @@ export declare class User {
|
|
|
48
51
|
static toExampleData?: () => any
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
export type Status =
|
|
54
|
+
export type Status = 'active' | 'inactive'
|
|
52
55
|
declare namespace Status {
|
|
53
56
|
const __is_atscript_annotated_type: true
|
|
54
57
|
const type: TAtscriptTypeComplex<Status>
|
|
@@ -60,6 +63,7 @@ declare namespace Status {
|
|
|
60
63
|
```
|
|
61
64
|
|
|
62
65
|
Key points:
|
|
66
|
+
|
|
63
67
|
- **Interfaces** become `declare class` — so they work both as types and runtime values
|
|
64
68
|
- **Types** become a `type` alias + a companion `namespace` with runtime statics
|
|
65
69
|
- Each has `type`, `metadata`, `validator()`, `toJsonSchema()`, and `toExampleData()` statics
|
|
@@ -92,7 +96,7 @@ declare global {
|
|
|
92
96
|
'expect.minLength': { length: number; message?: string }
|
|
93
97
|
// ... all annotations used in your project
|
|
94
98
|
}
|
|
95
|
-
type AtscriptPrimitiveTags =
|
|
99
|
+
type AtscriptPrimitiveTags = 'string' | 'number' | 'boolean' | 'null'
|
|
96
100
|
}
|
|
97
101
|
```
|
|
98
102
|
|
|
@@ -108,8 +112,8 @@ This file is **auto-generated** based on the annotations actually used across al
|
|
|
108
112
|
|
|
109
113
|
Generated files use these import paths:
|
|
110
114
|
|
|
111
|
-
| Import
|
|
112
|
-
|
|
115
|
+
| Import | Source |
|
|
116
|
+
| ---------------------------- | ------------------------------------------------- |
|
|
113
117
|
| `@atscript/typescript/utils` | Runtime utilities (used by generated `.js` files) |
|
|
114
118
|
|
|
115
119
|
When importing from `.as` files in your TypeScript code:
|