@atscript/typescript 0.1.48 → 0.1.50

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.
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ const require_plugin = require('./plugin-cY7VLpmN.cjs');
3
+ require('./json-schema-BgW_S2sP.cjs');
4
+ const path = require_plugin.__toESM(require("path"));
5
+ const __atscript_core = require_plugin.__toESM(require("@atscript/core"));
6
+ const fs = require_plugin.__toESM(require("fs"));
7
+
8
+ //#region packages/typescript/src/test-utils.ts
9
+ async function prepareFixtures(options) {
10
+ const { rootDir, plugins = [], entries, include, formats = ["js", "dts"] } = options;
11
+ const repo = await (0, __atscript_core.build)({
12
+ rootDir,
13
+ plugins: [require_plugin.tsPlugin(), ...plugins],
14
+ ...entries ? { entries } : { include: include ?? ["**/*.as"] }
15
+ });
16
+ const results = await Promise.all(formats.map((format) => repo.generate({
17
+ outDir: ".",
18
+ format
19
+ })));
20
+ for (const files of results) for (const file of files) {
21
+ const target = file.target ?? path.default.join(rootDir, file.fileName);
22
+ (0, fs.writeFileSync)(target, file.content);
23
+ }
24
+ }
25
+
26
+ //#endregion
27
+ exports.prepareFixtures = prepareFixtures
@@ -0,0 +1,25 @@
1
+ import { TAtscriptPlugin } from '@atscript/core';
2
+
3
+ interface PrepareFixturesOptions {
4
+ /** Absolute directory containing the `.as` fixture files. */
5
+ rootDir: string;
6
+ /** Additional plugins. `tsPlugin()` is auto-injected; do not pass it here. */
7
+ plugins?: TAtscriptPlugin[];
8
+ /** Glob includes; defaults to `['**\/*.as']` when `entries` is not set. */
9
+ include?: string[];
10
+ /** Explicit entry filenames relative to `rootDir`; takes precedence over `include`. */
11
+ entries?: string[];
12
+ /** Output formats to generate; defaults to `['js', 'dts']`. */
13
+ formats?: Array<'js' | 'dts'>;
14
+ }
15
+ /**
16
+ * Compiles `.as` fixture files under `rootDir` and writes the generated
17
+ * artifacts (`.as.js` and/or `.as.d.ts`) next to their sources.
18
+ *
19
+ * `tsPlugin()` is injected automatically — callers pass only extra plugins.
20
+ * Writes are unconditional; artifacts are treated as test-run outputs.
21
+ */
22
+ declare function prepareFixtures(options: PrepareFixturesOptions): Promise<void>;
23
+
24
+ export { prepareFixtures };
25
+ export type { PrepareFixturesOptions };
@@ -0,0 +1,26 @@
1
+ import { tsPlugin } from "./plugin-D96fH7Va.mjs";
2
+ import "./json-schema-DrJMwvm1.mjs";
3
+ import path from "path";
4
+ import { build } from "@atscript/core";
5
+ import { writeFileSync } from "fs";
6
+
7
+ //#region packages/typescript/src/test-utils.ts
8
+ async function prepareFixtures(options) {
9
+ const { rootDir, plugins = [], entries, include, formats = ["js", "dts"] } = options;
10
+ const repo = await build({
11
+ rootDir,
12
+ plugins: [tsPlugin(), ...plugins],
13
+ ...entries ? { entries } : { include: include ?? ["**/*.as"] }
14
+ });
15
+ const results = await Promise.all(formats.map((format) => repo.generate({
16
+ outDir: ".",
17
+ format
18
+ })));
19
+ for (const files of results) for (const file of files) {
20
+ const target = file.target ?? path.join(rootDir, file.fileName);
21
+ writeFileSync(target, file.content);
22
+ }
23
+ }
24
+
25
+ //#endregion
26
+ export { prepareFixtures };
package/dist/utils.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const require_json_schema = require('./json-schema-Bu4xgpQn.cjs');
2
+ const require_json_schema = require('./json-schema-BgW_S2sP.cjs');
3
3
 
4
4
  //#region packages/typescript/src/runtime/default-value.ts
5
5
  /**
@@ -247,15 +247,21 @@ function serializeNode(def, path, options, visited) {
247
247
  if (refDepth > 0 && def.ref) {
248
248
  const refTarget = def.ref.type();
249
249
  if (refTarget) result.ref = {
250
- type: serializeNode(refTarget, [], {
250
+ field: def.ref.field,
251
+ type: refDepth < 1 && isHalfStep(refDepth) ? {
252
+ id: refTarget.id ?? "",
253
+ metadata: serializeMetadata(refTarget.metadata, [], refTarget.type.kind, options)
254
+ } : serializeNode(refTarget, [], {
251
255
  ...options,
252
256
  refDepth: refDepth - 1
253
- }, visited),
254
- field: def.ref.field
257
+ }, visited)
255
258
  };
256
259
  }
257
260
  return result;
258
261
  }
262
+ function isHalfStep(refDepth) {
263
+ return refDepth - Math.floor(refDepth) === .5;
264
+ }
259
265
  function serializeTypeDef(def, path, options, visited) {
260
266
  return require_json_schema.forAnnotatedType(def, {
261
267
  phantom(d) {
@@ -346,27 +352,28 @@ function deserializeAnnotatedType(data) {
346
352
  const resolved = new Map();
347
353
  return deserializeNode(data, resolved);
348
354
  }
355
+ function emptyObjectTypeDef() {
356
+ return {
357
+ kind: "object",
358
+ props: new Map(),
359
+ propsPatterns: [],
360
+ tags: new Set()
361
+ };
362
+ }
363
+ function toMetadataMap(record) {
364
+ return new Map(Object.entries(record));
365
+ }
349
366
  function deserializeNode(data, resolved) {
350
367
  if (data.type.kind === "$ref") {
351
368
  const refId = data.type.id;
352
- return resolved.get(refId) || require_json_schema.createAnnotatedTypeNode({
353
- kind: "object",
354
- props: new Map(),
355
- propsPatterns: [],
356
- tags: new Set()
357
- }, new Map(), { id: refId });
369
+ return resolved.get(refId) || require_json_schema.createAnnotatedTypeNode(emptyObjectTypeDef(), new Map(), { id: refId });
358
370
  }
359
- const metadata = new Map(Object.entries(data.metadata));
371
+ const metadata = toMetadataMap(data.metadata);
360
372
  let result;
361
373
  if (data.id) {
362
374
  const existing = resolved.get(data.id);
363
375
  if (existing) return existing;
364
- result = require_json_schema.createAnnotatedTypeNode({
365
- kind: "object",
366
- props: new Map(),
367
- propsPatterns: [],
368
- tags: new Set()
369
- }, metadata, {
376
+ result = require_json_schema.createAnnotatedTypeNode(emptyObjectTypeDef(), metadata, {
370
377
  optional: data.optional || undefined,
371
378
  id: data.id
372
379
  });
@@ -375,11 +382,20 @@ function deserializeNode(data, resolved) {
375
382
  const type = deserializeTypeDef(data.type, resolved);
376
383
  let ref;
377
384
  if (data.ref) {
378
- const deserializedRefTarget = deserializeNode(data.ref.type, resolved);
379
- ref = {
380
- type: () => deserializedRefTarget,
381
- field: data.ref.field
382
- };
385
+ const refTargetData = data.ref.type;
386
+ if ("type" in refTargetData) {
387
+ const deserializedRefTarget = deserializeNode(refTargetData, resolved);
388
+ ref = {
389
+ type: () => deserializedRefTarget,
390
+ field: data.ref.field
391
+ };
392
+ } else {
393
+ const sentinel = require_json_schema.createAnnotatedTypeNode(emptyObjectTypeDef(), toMetadataMap(refTargetData.metadata), { id: refTargetData.id || undefined });
394
+ ref = {
395
+ type: () => sentinel,
396
+ field: data.ref.field
397
+ };
398
+ }
383
399
  }
384
400
  if (result) {
385
401
  result.type = type;
package/dist/utils.d.ts CHANGED
@@ -491,12 +491,17 @@ interface TSerializedAnnotatedTypeInner {
491
491
  metadata: Record<string, unknown>;
492
492
  optional?: boolean;
493
493
  id?: string;
494
- /** FK reference — present when serialized with `refDepth > 0` */
494
+ /** FK reference; shallow `{ id, metadata }` target when `refDepth` has a `.5` fractional part. */
495
495
  ref?: {
496
- type: TSerializedAnnotatedTypeInner;
496
+ type: TSerializedAnnotatedTypeInner | TSerializedShallowRefTarget;
497
497
  field: string;
498
498
  };
499
499
  }
500
+ /** Shallow ref-target shape: identity + interface-level metadata only, no structural body. */
501
+ interface TSerializedShallowRefTarget {
502
+ id: string;
503
+ metadata: Record<string, unknown>;
504
+ }
500
505
  interface TSerializedTypeFinal {
501
506
  kind: '';
502
507
  designType: string;
@@ -555,10 +560,9 @@ interface TSerializeOptions {
555
560
  value: unknown;
556
561
  } | undefined | void;
557
562
  /**
558
- * How many levels of `.ref` (FK references) to expand during serialization.
559
- * - `0` (default): refs are stripped (backward-compatible)
560
- * - `1`: immediate refs are serialized, but refs inside referenced types are stripped
561
- * - `2+`: deeper expansion
563
+ * How many levels of `.ref` (FK references) to expand. `0` (default) strips refs; integer
564
+ * `N` expands N levels with full target bodies. A `.5` fractional part (e.g. `0.5`, `1.5`)
565
+ * emits a shallow `{ id, metadata }` at the tail level instead of the full body.
562
566
  */
563
567
  refDepth?: number;
564
568
  }
package/dist/utils.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createAnnotatedTypeNode, defineAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas } from "./json-schema-Bl8jkrCj.mjs";
1
+ import { Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createAnnotatedTypeNode, defineAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas } from "./json-schema-DrJMwvm1.mjs";
2
2
 
3
3
  //#region packages/typescript/src/runtime/default-value.ts
4
4
  /**
@@ -246,15 +246,21 @@ function serializeNode(def, path, options, visited) {
246
246
  if (refDepth > 0 && def.ref) {
247
247
  const refTarget = def.ref.type();
248
248
  if (refTarget) result.ref = {
249
- type: serializeNode(refTarget, [], {
249
+ field: def.ref.field,
250
+ type: refDepth < 1 && isHalfStep(refDepth) ? {
251
+ id: refTarget.id ?? "",
252
+ metadata: serializeMetadata(refTarget.metadata, [], refTarget.type.kind, options)
253
+ } : serializeNode(refTarget, [], {
250
254
  ...options,
251
255
  refDepth: refDepth - 1
252
- }, visited),
253
- field: def.ref.field
256
+ }, visited)
254
257
  };
255
258
  }
256
259
  return result;
257
260
  }
261
+ function isHalfStep(refDepth) {
262
+ return refDepth - Math.floor(refDepth) === .5;
263
+ }
258
264
  function serializeTypeDef(def, path, options, visited) {
259
265
  return forAnnotatedType(def, {
260
266
  phantom(d) {
@@ -345,27 +351,28 @@ function deserializeAnnotatedType(data) {
345
351
  const resolved = new Map();
346
352
  return deserializeNode(data, resolved);
347
353
  }
354
+ function emptyObjectTypeDef() {
355
+ return {
356
+ kind: "object",
357
+ props: new Map(),
358
+ propsPatterns: [],
359
+ tags: new Set()
360
+ };
361
+ }
362
+ function toMetadataMap(record) {
363
+ return new Map(Object.entries(record));
364
+ }
348
365
  function deserializeNode(data, resolved) {
349
366
  if (data.type.kind === "$ref") {
350
367
  const refId = data.type.id;
351
- return resolved.get(refId) || createAnnotatedTypeNode({
352
- kind: "object",
353
- props: new Map(),
354
- propsPatterns: [],
355
- tags: new Set()
356
- }, new Map(), { id: refId });
368
+ return resolved.get(refId) || createAnnotatedTypeNode(emptyObjectTypeDef(), new Map(), { id: refId });
357
369
  }
358
- const metadata = new Map(Object.entries(data.metadata));
370
+ const metadata = toMetadataMap(data.metadata);
359
371
  let result;
360
372
  if (data.id) {
361
373
  const existing = resolved.get(data.id);
362
374
  if (existing) return existing;
363
- result = createAnnotatedTypeNode({
364
- kind: "object",
365
- props: new Map(),
366
- propsPatterns: [],
367
- tags: new Set()
368
- }, metadata, {
375
+ result = createAnnotatedTypeNode(emptyObjectTypeDef(), metadata, {
369
376
  optional: data.optional || undefined,
370
377
  id: data.id
371
378
  });
@@ -374,11 +381,20 @@ function deserializeNode(data, resolved) {
374
381
  const type = deserializeTypeDef(data.type, resolved);
375
382
  let ref;
376
383
  if (data.ref) {
377
- const deserializedRefTarget = deserializeNode(data.ref.type, resolved);
378
- ref = {
379
- type: () => deserializedRefTarget,
380
- field: data.ref.field
381
- };
384
+ const refTargetData = data.ref.type;
385
+ if ("type" in refTargetData) {
386
+ const deserializedRefTarget = deserializeNode(refTargetData, resolved);
387
+ ref = {
388
+ type: () => deserializedRefTarget,
389
+ field: data.ref.field
390
+ };
391
+ } else {
392
+ const sentinel = createAnnotatedTypeNode(emptyObjectTypeDef(), toMetadataMap(refTargetData.metadata), { id: refTargetData.id || undefined });
393
+ ref = {
394
+ type: () => sentinel,
395
+ field: data.ref.field
396
+ };
397
+ }
382
398
  }
383
399
  if (result) {
384
400
  result.type = type;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/typescript",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "description": "Atscript: typescript-gen support.",
5
5
  "keywords": [
6
6
  "annotations",
@@ -19,14 +19,11 @@
19
19
  "directory": "packages/typescript"
20
20
  },
21
21
  "bin": {
22
- "asc": "./cli.cjs",
23
- "atscript-typescript-skill": "./scripts/setup-skills.js"
22
+ "asc": "./cli.cjs"
24
23
  },
25
24
  "files": [
26
25
  "dist",
27
- "cli.cjs",
28
- "skills",
29
- "scripts/setup-skills.js"
26
+ "cli.cjs"
30
27
  ],
31
28
  "type": "module",
32
29
  "main": "dist/index.mjs",
@@ -36,6 +33,9 @@
36
33
  "utils": [
37
34
  "dist/utils.d.ts"
38
35
  ],
36
+ "test-utils": [
37
+ "dist/test-utils.d.ts"
38
+ ],
39
39
  "": [
40
40
  "dist/index.d.ts"
41
41
  ]
@@ -54,19 +54,25 @@
54
54
  "require": "./dist/utils.cjs",
55
55
  "default": "./dist/utils.mjs"
56
56
  },
57
+ "./test-utils": {
58
+ "types": "./dist/test-utils.d.ts",
59
+ "import": "./dist/test-utils.mjs",
60
+ "require": "./dist/test-utils.cjs",
61
+ "default": "./dist/test-utils.mjs"
62
+ },
57
63
  "./package.json": "./package.json"
58
64
  },
59
65
  "dependencies": {
60
- "@moostjs/event-cli": "^0.6.7",
61
- "moost": "^0.6.7"
66
+ "@moostjs/event-cli": "^0.6.8",
67
+ "moost": "^0.6.8"
62
68
  },
63
69
  "devDependencies": {
64
- "vitest": "3.2.4",
65
- "@atscript/db": "^0.1.48"
70
+ "@atscript/db": "^0.1.48",
71
+ "vitest": "3.2.4"
66
72
  },
67
73
  "peerDependencies": {
68
74
  "@atscript/db": "^0.1.48",
69
- "@atscript/core": "^0.1.48"
75
+ "@atscript/core": "^0.1.50"
70
76
  },
71
77
  "peerDependenciesMeta": {
72
78
  "@atscript/db": {
@@ -77,7 +83,8 @@
77
83
  {
78
84
  "entries": [
79
85
  "src/index.ts",
80
- "src/utils.ts"
86
+ "src/utils.ts",
87
+ "src/test-utils.ts"
81
88
  ]
82
89
  },
83
90
  {
@@ -90,7 +97,6 @@
90
97
  ],
91
98
  "scripts": {
92
99
  "pub": "pnpm publish --access public",
93
- "test": "vitest",
94
- "setup-skills": "node ./scripts/setup-skills.js"
100
+ "test": "vitest"
95
101
  }
96
102
  }
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env node
2
- /* prettier-ignore */
3
- import fs from 'fs'
4
- import os from 'os'
5
- import path from 'path'
6
- import { fileURLToPath } from 'url'
7
-
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
-
10
- const SKILL_NAME = 'atscript-typescript'
11
- const SKILL_SRC = path.join(__dirname, '..', 'skills', SKILL_NAME)
12
-
13
- if (!fs.existsSync(SKILL_SRC)) {
14
- console.error(`No skills found at ${SKILL_SRC}`)
15
- console.error('Add your SKILL.md files to the skills/' + SKILL_NAME + '/ directory first.')
16
- process.exit(1)
17
- }
18
-
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') },
25
- }
26
-
27
- const args = process.argv.slice(2)
28
- const isGlobal = args.includes('--global') || args.includes('-g')
29
- const isPostinstall = args.includes('--postinstall')
30
- let installed = 0,
31
- skipped = 0
32
- const installedDirs = []
33
-
34
- for (const [agentName, cfg] of Object.entries(AGENTS)) {
35
- const targetBase = isGlobal ? cfg.global : path.join(process.cwd(), cfg.dir)
36
- const agentRootDir = path.dirname(cfg.global) // Check if the agent has ever been installed globally
37
-
38
- // In postinstall mode: silently skip agents that aren't set up globally
39
- if (isPostinstall || isGlobal) {
40
- if (!fs.existsSync(agentRootDir)) {
41
- skipped++
42
- continue
43
- }
44
- }
45
-
46
- const dest = path.join(targetBase, SKILL_NAME)
47
- try {
48
- fs.mkdirSync(dest, { recursive: true })
49
- fs.cpSync(SKILL_SRC, dest, { recursive: true })
50
- console.log(`✅ ${agentName}: installed to ${dest}`)
51
- installed++
52
- if (!isGlobal) installedDirs.push(cfg.dir + '/' + SKILL_NAME)
53
- } catch (err) {
54
- console.warn(`⚠️ ${agentName}: failed — ${err.message}`)
55
- }
56
- }
57
-
58
- // Add locally-installed skill dirs to .gitignore
59
- if (!isGlobal && installedDirs.length > 0) {
60
- const gitignorePath = path.join(process.cwd(), '.gitignore')
61
- let gitignoreContent = ''
62
- try {
63
- gitignoreContent = fs.readFileSync(gitignorePath, 'utf8')
64
- } catch {}
65
- const linesToAdd = installedDirs.filter(d => !gitignoreContent.includes(d))
66
- if (linesToAdd.length > 0) {
67
- const hasHeader = gitignoreContent.includes('# AI agent skills')
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'
73
- fs.appendFileSync(gitignorePath, block)
74
- console.log(`📝 Added ${linesToAdd.length} entries to .gitignore`)
75
- }
76
- }
77
-
78
- if (installed === 0 && isPostinstall) {
79
- // Silence is fine — no agents present, nothing to do
80
- } else if (installed === 0 && skipped === Object.keys(AGENTS).length) {
81
- console.log(
82
- 'No agent directories detected. Try --global or run without it for project-local install.'
83
- )
84
- } else if (installed === 0) {
85
- console.log('Nothing installed. Run without --global to install project-locally.')
86
- } else {
87
- console.log(`\n✨ Done! Restart your AI agent to pick up the "${SKILL_NAME}" skill.`)
88
- }
File without changes
@@ -1,52 +0,0 @@
1
- ---
2
- name: atscript-typescript
3
- description: Use when working with Atscript (.as files) in TypeScript projects — writing or editing .as interfaces/types/annotations, configuring atscript.config.ts or tsPlugin, understanding generated .d.ts/.js output, using runtime APIs (TAtscriptAnnotatedType, metadata, validators), generating JSON Schema or example data from types, serializing/deserializing annotated types, or running the asc CLI.
4
- ---
5
-
6
- # @atscript/typescript
7
-
8
- Atscript is a universal type and metadata description language. `@atscript/typescript` is the TypeScript language extension that compiles `.as` files into `.d.ts` type declarations and `.js` runtime modules with full metadata, validation, and JSON Schema support.
9
-
10
- ## How to use this skill
11
-
12
- Read the domain file that matches the task. Do not load all files — only what you need.
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.*`/`@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
-
24
- ## Quick reference
25
-
26
- ```ts
27
- // Main export (plugin for atscript.config)
28
- import tsPlugin from '@atscript/typescript'
29
-
30
- // Runtime utilities (used in app code)
31
- import {
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,
46
- } from '@atscript/typescript/utils'
47
-
48
- // CLI
49
- // npx asc -f dts — generate .d.ts files
50
- // npx asc -f js — generate .js files (not usually needed with unplugin)
51
- // npx asc — generate default formats (for typescript plugin this is d.ts only, but if there are other language plugins it may generate multiple formats)
52
- ```