@atscript/typescript 0.1.26 → 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/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 != null && node.id) {
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 != null ? node.id : undefined);
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 != null && node.id) {
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 != null ? node.id : undefined);
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
- //#region packages/typescript/src/index.ts
1874
- var src_default = tsPlugin;
1875
-
1876
- //#endregion
1877
- module.exports = src_default;
1874
+ exports.default = tsPlugin
1875
+ exports.tsPlugin = tsPlugin
package/dist/index.d.ts CHANGED
@@ -20,4 +20,4 @@ interface TTsPluginOptions {
20
20
  }
21
21
  declare const tsPlugin: (opts?: TTsPluginOptions) => TAtscriptPlugin;
22
22
 
23
- export { tsPlugin as default };
23
+ export { tsPlugin as default, tsPlugin };
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 != null && node.id) {
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 != null ? node.id : undefined);
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
- //#region packages/typescript/src/index.ts
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;
@@ -901,6 +901,17 @@ function mergeJsonSchemas(types) {
901
901
  if (prop.validator({ unknownProps: "ignore" }).validate(raw, true)) return { value: raw };
902
902
  return undefined;
903
903
  }
904
+ if (mode === "db") {
905
+ const dbValue = prop.metadata.get("db.default.value");
906
+ if (dbValue !== undefined) {
907
+ const parsed$1 = parseRawValue(dbValue, prop);
908
+ if (parsed$1 !== undefined && prop.validator({ unknownProps: "ignore" }).validate(parsed$1, true)) return { value: parsed$1 };
909
+ return undefined;
910
+ }
911
+ const dbFn = prop.metadata.get("db.default.fn");
912
+ if (dbFn !== undefined) return { value: dbFn };
913
+ return undefined;
914
+ }
904
915
  const metaKey = mode === "default" ? "meta.default" : "meta.example";
905
916
  const rawStr = prop.metadata.get(metaKey);
906
917
  if (rawStr === undefined) return undefined;
package/dist/utils.d.ts CHANGED
@@ -354,19 +354,21 @@ interface TCreateDataOptions {
354
354
  * - `'empty'` — structural defaults only (`''`, `0`, `false`, `[]`, `{}`); optional props skipped
355
355
  * - `'default'` — use `@meta.default` annotations; optional props skipped unless annotated
356
356
  * - `'example'` — use `@meta.example` annotations; optional props always included; arrays get one sample item
357
+ * - `'db'` — use `@db.default.value` (parsed) or `@db.default.fn` (returns fn name string); optional props skipped unless annotated
357
358
  * - `function` — custom resolver per field; optional props skipped unless resolver returns a value
358
359
  *
359
360
  * @default 'empty'
360
361
  */
361
- mode?: 'empty' | 'default' | 'example' | TValueResolver;
362
+ mode?: 'empty' | 'default' | 'example' | 'db' | TValueResolver;
362
363
  }
363
364
  /**
364
365
  * Creates a data object from an ATScript annotated type definition.
365
366
  *
366
- * Supports four modes:
367
+ * Supports five modes:
367
368
  * - `'empty'` — structural defaults only; optional props omitted
368
369
  * - `'default'` — uses `@meta.default` annotations; optional props omitted unless annotated
369
370
  * - `'example'` — uses `@meta.example` annotations; optional props always included; arrays get one sample item
371
+ * - `'db'` — uses `@db.default.value` (parsed) or `@db.default.fn` (fn name string); optional props omitted unless annotated
370
372
  * - `function` — custom resolver; optional props omitted unless resolver returns a value
371
373
  *
372
374
  * When a `@meta.default` / `@meta.example` value is set on a complex type (object, array)
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;
@@ -900,6 +900,17 @@ function mergeJsonSchemas(types) {
900
900
  if (prop.validator({ unknownProps: "ignore" }).validate(raw, true)) return { value: raw };
901
901
  return undefined;
902
902
  }
903
+ if (mode === "db") {
904
+ const dbValue = prop.metadata.get("db.default.value");
905
+ if (dbValue !== undefined) {
906
+ const parsed$1 = parseRawValue(dbValue, prop);
907
+ if (parsed$1 !== undefined && prop.validator({ unknownProps: "ignore" }).validate(parsed$1, true)) return { value: parsed$1 };
908
+ return undefined;
909
+ }
910
+ const dbFn = prop.metadata.get("db.default.fn");
911
+ if (dbFn !== undefined) return { value: dbFn };
912
+ return undefined;
913
+ }
903
914
  const metaKey = mode === "default" ? "meta.default" : "meta.example";
904
915
  const rawStr = prop.metadata.get(metaKey);
905
916
  if (rawStr === undefined) return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/typescript",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
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
- "setup-skills": "./scripts/setup-skills.js"
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.5.32",
61
- "moost": "^0.5.32"
60
+ "@moostjs/event-cli": "^0.6.2",
61
+ "moost": "^0.6.2"
62
62
  },
63
63
  "devDependencies": {
64
64
  "vitest": "3.2.4"
65
65
  },
66
66
  "peerDependencies": {
67
- "@atscript/core": "^0.1.26"
67
+ "@atscript/core": "^0.1.28"
68
68
  },
69
69
  "build": [
70
70
  {},
@@ -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', 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') },
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, skipped = 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)) { skipped++; continue }
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 { gitignoreContent = fs.readFileSync(gitignorePath, 'utf8') } catch {}
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 = (gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '')
63
- + (hasHeader ? '' : '\n# AI agent skills (auto-generated by setup-skills)\n')
64
- + linesToAdd.join('\n') + '\n'
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('No agent directories detected. Try --global or run without it for project-local install.')
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 | 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
- | Annotations & primitives | [annotations.md](annotations.md) | Using built-in `@meta.*`/`@expect.*` annotations, defining custom annotations or primitives |
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` |
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
+ | Annotations & primitives | [annotations.md](annotations.md) | Using built-in `@meta.*`/`@expect.*`/`@ui.*`/`@db.*` annotations, defining custom annotations or primitives |
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, isAnnotatedType, annotate,
33
- Validator, ValidatorError,
34
- buildJsonSchema, fromJsonSchema, mergeJsonSchemas,
35
- serializeAnnotatedType, deserializeAnnotatedType,
36
- flattenAnnotatedType, createDataFromAnnotatedType,
37
- forAnnotatedType, throwFeatureDisabled,
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,53 @@
6
6
 
7
7
  ### `@meta.*` — Metadata Annotations
8
8
 
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) |
9
+ | Annotation | Arguments | Description |
10
+ | --------------------- | -------------------------- | ------------------------------------------------------------------ |
11
+ | `@meta.label` | `text: string` | Human-readable label for UI, logs, documentation |
12
+ | `@meta.id` | _(none)_ | Mark field as unique identifier; multiple fields form composite PK |
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.sensitive` | _(none)_ | Mark as sensitive (passwords, API keys). Strips from serialization |
16
+ | `@meta.readonly` | _(none)_ | Mark as read-only |
17
+ | `@meta.required` | `message?: string` | Required field. Strings: non-whitespace. Booleans: must be `true` |
18
+ | `@meta.default` | `value: string` | Default value (strings as-is, others parsed as JSON) |
19
+ | `@meta.example` | `value: string` | Example value (strings as-is, others parsed as JSON) |
20
+ | `@expect.array.key` | _(none)_ | Mark field as key inside array (string/number types only) |
22
21
 
23
22
  ### `@expect.*` — Validation Constraints
24
23
 
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) |
24
+ | Annotation | Arguments | Applies To | Description |
25
+ | ------------------- | ------------------------------------------------------- | ------------- | ------------------------------------------------------ |
26
+ | `@expect.minLength` | `length: number`, `message?: string` | string, array | Minimum length |
27
+ | `@expect.maxLength` | `length: number`, `message?: string` | string, array | Maximum length |
28
+ | `@expect.min` | `minValue: number`, `message?: string` | number | Minimum value |
29
+ | `@expect.max` | `maxValue: number`, `message?: string` | number | Maximum value |
30
+ | `@expect.int` | _(none)_ | number | Must be integer |
31
+ | `@expect.pattern` | `pattern: string`, `flags?: string`, `message?: string` | string | Regex validation. **Multiple allowed** (all must pass) |
32
+
33
+ ### `@ui.*` — UI / Presentation Hints
34
+
35
+ | Annotation | Arguments | Description |
36
+ | ----------------- | ------------------------------ | ---------------------------------------------- |
37
+ | `@ui.placeholder` | `text: string` | Input placeholder text |
38
+ | `@ui.component` | `name: string` | UI component hint (`"select"`, `"datepicker"`) |
39
+ | `@ui.hidden` | _(none)_ | Hide from UI forms/tables |
40
+ | `@ui.group` | `name: string` | Group fields into form sections |
41
+ | `@ui.order` | `order: number` | Display order (lower = first) |
42
+ | `@ui.width` | `width: string` | Layout hint (`"half"`, `"full"`, `"third"`) |
43
+ | `@ui.icon` | `name: string` | Icon hint |
44
+ | `@ui.hint` | `text: string` | Help text / tooltip |
45
+ | `@ui.disabled` | _(none)_ | Non-interactive field |
46
+ | `@ui.type` | `type: string` | Input type (`"textarea"`, `"password"`, etc.) |
47
+ | `@ui.attr` | `key: string`, `value: string` | Arbitrary attribute (**multiple**, append) |
48
+ | `@ui.class` | `names: string` | CSS class names (**multiple**, append) |
49
+ | `@ui.style` | `css: string` | Inline CSS styles (**multiple**, append) |
33
50
 
34
51
  ### `@emit.*` — Build-time Directives
35
52
 
36
- | Annotation | Applies To | Description |
37
- |------------|-----------|-------------|
38
- | `@emit.jsonSchema` | interface | Pre-compute and embed JSON Schema at build time |
53
+ | Annotation | Applies To | Description |
54
+ | ------------------ | ---------- | ----------------------------------------------- |
55
+ | `@emit.jsonSchema` | interface | Pre-compute and embed JSON Schema at build time |
39
56
 
40
57
  ## Custom Annotations
41
58
 
@@ -49,21 +66,21 @@ export default defineConfig({
49
66
  plugins: [tsPlugin()],
50
67
  annotations: {
51
68
  // Namespaced annotations use nested objects
52
- ui: {
53
- // @ui.component "DatePicker"
54
- component: new AnnotationSpec({
55
- argument: { name: 'name', type: 'string' },
56
- description: 'UI component to render this field',
69
+ grid: {
70
+ // @grid.column 200
71
+ column: new AnnotationSpec({
72
+ argument: { name: 'width', type: 'number' },
73
+ description: 'Table column width in data grid',
57
74
  }),
58
75
 
59
- // @ui.hidden (no arguments — boolean flag)
76
+ // @grid.hidden (no arguments — boolean flag)
60
77
  hidden: new AnnotationSpec({
61
- description: 'Hide this field in the UI',
78
+ description: 'Hide column in data grid',
62
79
  }),
63
80
 
64
- // @ui.order 5
65
- order: new AnnotationSpec({
66
- argument: { name: 'position', type: 'number' },
81
+ // @grid.sortable
82
+ sortable: new AnnotationSpec({
83
+ description: 'Allow sorting by this column',
67
84
  }),
68
85
  },
69
86
 
@@ -102,10 +119,10 @@ new AnnotationSpec({
102
119
  ],
103
120
 
104
121
  // Allow multiple instances on the same target
105
- multiple: true, // default: false
122
+ multiple: true, // default: false
106
123
 
107
124
  // How duplicates merge: 'replace' (last wins) or 'append' (collect into array)
108
- mergeStrategy: 'append', // default: 'replace'
125
+ mergeStrategy: 'append', // default: 'replace'
109
126
 
110
127
  // Human-readable description
111
128
  description: 'What this annotation does',
@@ -124,13 +141,13 @@ new AnnotationSpec({
124
141
 
125
142
  Each argument accepts:
126
143
 
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) |
144
+ | Field | Type | Description |
145
+ | ------------- | ----------------------------------- | ------------------------------------------- |
146
+ | `name` | `string` | Argument name (used in metadata object key) |
147
+ | `type` | `'string' \| 'number' \| 'boolean'` | Expected type |
148
+ | `optional` | `boolean` | Whether the argument can be omitted |
149
+ | `description` | `string` | Human-readable description |
150
+ | `values` | `string[]` | Allowed values (enum-like constraint) |
134
151
 
135
152
  ### How Annotations Map to Runtime Metadata
136
153
 
@@ -140,6 +157,7 @@ Each argument accepts:
140
157
  - **`multiple: true`** → metadata value is an array
141
158
 
142
159
  Example: `@api.endpoint "/users" "GET"` becomes:
160
+
143
161
  ```ts
144
162
  metadata.get('api.endpoint') // → { path: "/users", method: "GET" }
145
163
  ```
@@ -200,26 +218,26 @@ export default defineConfig({
200
218
 
201
219
  ### `TPrimitiveConfig` Options
202
220
 
203
- | Field | Type | Description |
204
- |-------|------|-------------|
205
- | `type` | `TPrimitiveTypeDef` | Base type: `'string'`, `'number'`, `'boolean'`, `'void'`, `'null'`, `'phantom'`, or complex type |
206
- | `tags` | `string[]` | Custom tags for categorization |
207
- | `documentation` | `string` | Documentation string |
208
- | `expect` | object | Built-in validation constraints |
209
- | `extensions` | `Record<string, Partial<TPrimitiveConfig>>` | Sub-types accessible via dot notation |
221
+ | Field | Type | Description |
222
+ | --------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------ |
223
+ | `type` | `TPrimitiveTypeDef` | Base type: `'string'`, `'number'`, `'boolean'`, `'void'`, `'null'`, `'phantom'`, or complex type |
224
+ | `tags` | `string[]` | Custom tags for categorization |
225
+ | `documentation` | `string` | Documentation string |
226
+ | `expect` | object | Built-in validation constraints |
227
+ | `extensions` | `Record<string, Partial<TPrimitiveConfig>>` | Sub-types accessible via dot notation |
210
228
 
211
229
  ### `expect` Validation on Primitives
212
230
 
213
- | Field | Applies To | Description |
214
- |-------|-----------|-------------|
215
- | `min` | number | Minimum value |
216
- | `max` | number | Maximum value |
217
- | `int` | number | Must be integer |
218
- | `minLength` | string, array | Minimum length |
219
- | `maxLength` | string, array | Maximum length |
220
- | `pattern` | string | Regex pattern(s) |
221
- | `required` | string, boolean | Non-empty / must be true |
222
- | `message` | any | Custom error message for pattern |
231
+ | Field | Applies To | Description |
232
+ | ----------- | --------------- | -------------------------------- |
233
+ | `min` | number | Minimum value |
234
+ | `max` | number | Maximum value |
235
+ | `int` | number | Must be integer |
236
+ | `minLength` | string, array | Minimum length |
237
+ | `maxLength` | string, array | Maximum length |
238
+ | `pattern` | string | Regex pattern(s) |
239
+ | `required` | string, boolean | Non-empty / must be true |
240
+ | `message` | any | Custom error message for pattern |
223
241
 
224
242
  ### Usage in `.as` Files
225
243