@atscript/typescript 0.1.30 → 0.1.31

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/cli.cjs CHANGED
@@ -289,8 +289,9 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
289
289
  if (isGrp) this.write(")");
290
290
  return this.write("[]");
291
291
  }
292
+ if ((0, __atscript_core.isPrimitive)(def)) return this.write(renderPrimitiveTypeDef(def.config.type));
292
293
  }
293
- renderStructure(struct, asClass) {
294
+ renderStructure(struct, asClass, interfaceNode) {
294
295
  this.blockln("{}");
295
296
  const patterns = [];
296
297
  const propsDefs = new Set();
@@ -336,6 +337,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
336
337
  this.writeln("static toJsonSchema: () => any");
337
338
  if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
338
339
  this.writeln("static toExampleData?: () => any");
340
+ if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
339
341
  }
340
342
  this.pop();
341
343
  }
@@ -362,20 +364,20 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
362
364
  }
363
365
  if (!firstParentProps && (0, __atscript_core.isStructure)(fpDef)) firstParentProps = fpDef.props;
364
366
  }
365
- this.renderStructureFiltered(resolved, node.id, firstParentProps);
367
+ this.renderStructureFiltered(resolved, node.id, firstParentProps, node);
366
368
  } else this.writeln("{}");
367
369
  } else {
368
370
  this.write(`class ${node.id} `);
369
371
  const struct = node.getDefinition();
370
- if (struct?.entity === "structure") this.renderStructure(struct, node.id);
372
+ if (struct?.entity === "structure") this.renderStructure(struct, node.id, node);
371
373
  else this.writeln("{}");
372
374
  }
373
375
  this.writeln();
374
376
  }
375
377
  /**
376
378
  * Renders a structure block, optionally filtering out props that exist in a parent.
377
- */ renderStructureFiltered(struct, asClass, filterProps) {
378
- if (!filterProps) return this.renderStructure(struct, asClass);
379
+ */ renderStructureFiltered(struct, asClass, filterProps, interfaceNode) {
380
+ if (!filterProps) return this.renderStructure(struct, asClass, interfaceNode);
379
381
  this.blockln("{}");
380
382
  for (const prop of Array.from(struct.props.values())) {
381
383
  if (filterProps.has(prop.id)) continue;
@@ -398,6 +400,7 @@ else this.writeln("{}");
398
400
  this.writeln("static toJsonSchema: () => any");
399
401
  if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
400
402
  this.writeln("static toExampleData?: () => any");
403
+ if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
401
404
  this.pop();
402
405
  }
403
406
  renderType(node) {
@@ -453,6 +456,50 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFina
453
456
  this.writeln("const toExampleData: (() => any) | undefined");
454
457
  this.popln();
455
458
  }
459
+ /**
460
+ * Checks whether an interface has the `@db.table` annotation.
461
+ *
462
+ * NOTE: Only `@db.table` interfaces get the `__flat` static property.
463
+ * This is intentionally hardcoded — `__flat` exists solely to improve
464
+ * type-safety for filter expressions and `$select`/`$sort` operations
465
+ * in the DB layer. Non-DB interfaces have no use for dot-notation path types.
466
+ */ hasDbTable(node) {
467
+ return !!node.annotations?.some((a) => a.name === "db.table");
468
+ }
469
+ /**
470
+ * Renders the `static __flat` property — a map of all dot-notation paths
471
+ * to their TypeScript value types.
472
+ *
473
+ * This enables type-safe autocomplete for filter keys and `$select`/`$sort`
474
+ * paths when using `AtscriptDbTable`. The `FlatOf<T>` utility type extracts
475
+ * this map at the type level.
476
+ *
477
+ * Special rendering rules:
478
+ * - **Intermediate paths** (structures, arrays of structures) → `never`
479
+ * (prevents `$eq` comparisons, but allows `$select` and `$exists`)
480
+ * - **`@db.json` fields** → `string` (stored as serialized JSON in DB,
481
+ * not individually queryable)
482
+ * - **Leaf fields** → their original TypeScript type
483
+ */ renderFlat(node) {
484
+ const flatMap = (0, __atscript_core.flattenInterfaceNode)(this.doc, node);
485
+ if (flatMap.size === 0) return;
486
+ this.write("static __flat: ");
487
+ this.blockln("{}");
488
+ for (const [path$3, descriptor] of flatMap) {
489
+ this.write(`"${escapeQuotes(path$3)}"`);
490
+ if (descriptor.optional) this.write("?");
491
+ this.write(": ");
492
+ if (descriptor.intermediate) this.writeln("never");
493
+ else if (descriptor.dbJson) this.writeln("string");
494
+ else {
495
+ const originalDef = descriptor.propNode?.getDefinition();
496
+ const defToRender = originalDef && !((0, __atscript_core.isGroup)(descriptor.def) && descriptor.def !== originalDef) ? originalDef : descriptor.def;
497
+ const renderedDef = this.renderTypeDefString(defToRender);
498
+ renderedDef.split("\n").forEach((l) => this.writeln(l));
499
+ }
500
+ }
501
+ this.pop();
502
+ }
456
503
  phantomPropType(def) {
457
504
  if (!def) return undefined;
458
505
  if ((0, __atscript_core.isPrimitive)(def) && def.config.type === "phantom") return def.id;
package/dist/index.cjs CHANGED
@@ -287,8 +287,9 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
287
287
  if (isGrp) this.write(")");
288
288
  return this.write("[]");
289
289
  }
290
+ if ((0, __atscript_core.isPrimitive)(def)) return this.write(renderPrimitiveTypeDef(def.config.type));
290
291
  }
291
- renderStructure(struct, asClass) {
292
+ renderStructure(struct, asClass, interfaceNode) {
292
293
  this.blockln("{}");
293
294
  const patterns = [];
294
295
  const propsDefs = new Set();
@@ -334,6 +335,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
334
335
  this.writeln("static toJsonSchema: () => any");
335
336
  if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
336
337
  this.writeln("static toExampleData?: () => any");
338
+ if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
337
339
  }
338
340
  this.pop();
339
341
  }
@@ -360,20 +362,20 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
360
362
  }
361
363
  if (!firstParentProps && (0, __atscript_core.isStructure)(fpDef)) firstParentProps = fpDef.props;
362
364
  }
363
- this.renderStructureFiltered(resolved, node.id, firstParentProps);
365
+ this.renderStructureFiltered(resolved, node.id, firstParentProps, node);
364
366
  } else this.writeln("{}");
365
367
  } else {
366
368
  this.write(`class ${node.id} `);
367
369
  const struct = node.getDefinition();
368
- if (struct?.entity === "structure") this.renderStructure(struct, node.id);
370
+ if (struct?.entity === "structure") this.renderStructure(struct, node.id, node);
369
371
  else this.writeln("{}");
370
372
  }
371
373
  this.writeln();
372
374
  }
373
375
  /**
374
376
  * Renders a structure block, optionally filtering out props that exist in a parent.
375
- */ renderStructureFiltered(struct, asClass, filterProps) {
376
- if (!filterProps) return this.renderStructure(struct, asClass);
377
+ */ renderStructureFiltered(struct, asClass, filterProps, interfaceNode) {
378
+ if (!filterProps) return this.renderStructure(struct, asClass, interfaceNode);
377
379
  this.blockln("{}");
378
380
  for (const prop of Array.from(struct.props.values())) {
379
381
  if (filterProps.has(prop.id)) continue;
@@ -396,6 +398,7 @@ else this.writeln("{}");
396
398
  this.writeln("static toJsonSchema: () => any");
397
399
  if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
398
400
  this.writeln("static toExampleData?: () => any");
401
+ if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
399
402
  this.pop();
400
403
  }
401
404
  renderType(node) {
@@ -451,6 +454,50 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFina
451
454
  this.writeln("const toExampleData: (() => any) | undefined");
452
455
  this.popln();
453
456
  }
457
+ /**
458
+ * Checks whether an interface has the `@db.table` annotation.
459
+ *
460
+ * NOTE: Only `@db.table` interfaces get the `__flat` static property.
461
+ * This is intentionally hardcoded — `__flat` exists solely to improve
462
+ * type-safety for filter expressions and `$select`/`$sort` operations
463
+ * in the DB layer. Non-DB interfaces have no use for dot-notation path types.
464
+ */ hasDbTable(node) {
465
+ return !!node.annotations?.some((a) => a.name === "db.table");
466
+ }
467
+ /**
468
+ * Renders the `static __flat` property — a map of all dot-notation paths
469
+ * to their TypeScript value types.
470
+ *
471
+ * This enables type-safe autocomplete for filter keys and `$select`/`$sort`
472
+ * paths when using `AtscriptDbTable`. The `FlatOf<T>` utility type extracts
473
+ * this map at the type level.
474
+ *
475
+ * Special rendering rules:
476
+ * - **Intermediate paths** (structures, arrays of structures) → `never`
477
+ * (prevents `$eq` comparisons, but allows `$select` and `$exists`)
478
+ * - **`@db.json` fields** → `string` (stored as serialized JSON in DB,
479
+ * not individually queryable)
480
+ * - **Leaf fields** → their original TypeScript type
481
+ */ renderFlat(node) {
482
+ const flatMap = (0, __atscript_core.flattenInterfaceNode)(this.doc, node);
483
+ if (flatMap.size === 0) return;
484
+ this.write("static __flat: ");
485
+ this.blockln("{}");
486
+ for (const [path$2, descriptor] of flatMap) {
487
+ this.write(`"${escapeQuotes(path$2)}"`);
488
+ if (descriptor.optional) this.write("?");
489
+ this.write(": ");
490
+ if (descriptor.intermediate) this.writeln("never");
491
+ else if (descriptor.dbJson) this.writeln("string");
492
+ else {
493
+ const originalDef = descriptor.propNode?.getDefinition();
494
+ const defToRender = originalDef && !((0, __atscript_core.isGroup)(descriptor.def) && descriptor.def !== originalDef) ? originalDef : descriptor.def;
495
+ const renderedDef = this.renderTypeDefString(defToRender);
496
+ renderedDef.split("\n").forEach((l) => this.writeln(l));
497
+ }
498
+ }
499
+ this.pop();
500
+ }
454
501
  phantomPropType(def) {
455
502
  if (!def) return undefined;
456
503
  if ((0, __atscript_core.isPrimitive)(def) && def.config.type === "phantom") return def.id;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { DEFAULT_FORMAT, isArray, isConst, isGroup, isInterface, isPrimitive, isRef, isStructure } from "@atscript/core";
2
+ import { DEFAULT_FORMAT, flattenInterfaceNode, isArray, isConst, isGroup, isInterface, isPrimitive, isRef, isStructure } from "@atscript/core";
3
3
 
4
4
  //#region packages/typescript/src/codegen/code-printer.ts
5
5
  function _define_property$4(obj, key, value) {
@@ -262,8 +262,9 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
262
262
  if (isGrp) this.write(")");
263
263
  return this.write("[]");
264
264
  }
265
+ if (isPrimitive(def)) return this.write(renderPrimitiveTypeDef(def.config.type));
265
266
  }
266
- renderStructure(struct, asClass) {
267
+ renderStructure(struct, asClass, interfaceNode) {
267
268
  this.blockln("{}");
268
269
  const patterns = [];
269
270
  const propsDefs = new Set();
@@ -309,6 +310,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
309
310
  this.writeln("static toJsonSchema: () => any");
310
311
  if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
311
312
  this.writeln("static toExampleData?: () => any");
313
+ if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
312
314
  }
313
315
  this.pop();
314
316
  }
@@ -335,20 +337,20 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
335
337
  }
336
338
  if (!firstParentProps && isStructure(fpDef)) firstParentProps = fpDef.props;
337
339
  }
338
- this.renderStructureFiltered(resolved, node.id, firstParentProps);
340
+ this.renderStructureFiltered(resolved, node.id, firstParentProps, node);
339
341
  } else this.writeln("{}");
340
342
  } else {
341
343
  this.write(`class ${node.id} `);
342
344
  const struct = node.getDefinition();
343
- if (struct?.entity === "structure") this.renderStructure(struct, node.id);
345
+ if (struct?.entity === "structure") this.renderStructure(struct, node.id, node);
344
346
  else this.writeln("{}");
345
347
  }
346
348
  this.writeln();
347
349
  }
348
350
  /**
349
351
  * Renders a structure block, optionally filtering out props that exist in a parent.
350
- */ renderStructureFiltered(struct, asClass, filterProps) {
351
- if (!filterProps) return this.renderStructure(struct, asClass);
352
+ */ renderStructureFiltered(struct, asClass, filterProps, interfaceNode) {
353
+ if (!filterProps) return this.renderStructure(struct, asClass, interfaceNode);
352
354
  this.blockln("{}");
353
355
  for (const prop of Array.from(struct.props.values())) {
354
356
  if (filterProps.has(prop.id)) continue;
@@ -371,6 +373,7 @@ else this.writeln("{}");
371
373
  this.writeln("static toJsonSchema: () => any");
372
374
  if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
373
375
  this.writeln("static toExampleData?: () => any");
376
+ if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
374
377
  this.pop();
375
378
  }
376
379
  renderType(node) {
@@ -426,6 +429,50 @@ else if (isPrimitive(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
426
429
  this.writeln("const toExampleData: (() => any) | undefined");
427
430
  this.popln();
428
431
  }
432
+ /**
433
+ * Checks whether an interface has the `@db.table` annotation.
434
+ *
435
+ * NOTE: Only `@db.table` interfaces get the `__flat` static property.
436
+ * This is intentionally hardcoded — `__flat` exists solely to improve
437
+ * type-safety for filter expressions and `$select`/`$sort` operations
438
+ * in the DB layer. Non-DB interfaces have no use for dot-notation path types.
439
+ */ hasDbTable(node) {
440
+ return !!node.annotations?.some((a) => a.name === "db.table");
441
+ }
442
+ /**
443
+ * Renders the `static __flat` property — a map of all dot-notation paths
444
+ * to their TypeScript value types.
445
+ *
446
+ * This enables type-safe autocomplete for filter keys and `$select`/`$sort`
447
+ * paths when using `AtscriptDbTable`. The `FlatOf<T>` utility type extracts
448
+ * this map at the type level.
449
+ *
450
+ * Special rendering rules:
451
+ * - **Intermediate paths** (structures, arrays of structures) → `never`
452
+ * (prevents `$eq` comparisons, but allows `$select` and `$exists`)
453
+ * - **`@db.json` fields** → `string` (stored as serialized JSON in DB,
454
+ * not individually queryable)
455
+ * - **Leaf fields** → their original TypeScript type
456
+ */ renderFlat(node) {
457
+ const flatMap = flattenInterfaceNode(this.doc, node);
458
+ if (flatMap.size === 0) return;
459
+ this.write("static __flat: ");
460
+ this.blockln("{}");
461
+ for (const [path$1, descriptor] of flatMap) {
462
+ this.write(`"${escapeQuotes(path$1)}"`);
463
+ if (descriptor.optional) this.write("?");
464
+ this.write(": ");
465
+ if (descriptor.intermediate) this.writeln("never");
466
+ else if (descriptor.dbJson) this.writeln("string");
467
+ else {
468
+ const originalDef = descriptor.propNode?.getDefinition();
469
+ const defToRender = originalDef && !(isGroup(descriptor.def) && descriptor.def !== originalDef) ? originalDef : descriptor.def;
470
+ const renderedDef = this.renderTypeDefString(defToRender);
471
+ renderedDef.split("\n").forEach((l) => this.writeln(l));
472
+ }
473
+ }
474
+ this.pop();
475
+ }
429
476
  phantomPropType(def) {
430
477
  if (!def) return undefined;
431
478
  if (isPrimitive(def) && def.config.type === "phantom") return def.id;
package/dist/utils.d.ts CHANGED
@@ -48,7 +48,7 @@ interface TValidatorPluginContext {
48
48
  * @typeParam T - The annotated type definition.
49
49
  * @typeParam DataType - The TypeScript type that `validate` narrows to (auto-inferred).
50
50
  */
51
- declare class Validator<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> {
51
+ declare class Validator<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType$1<T>> {
52
52
  protected readonly def: T;
53
53
  protected opts: TValidatorOptions;
54
54
  constructor(def: T, opts?: Partial<TValidatorOptions>);
@@ -159,7 +159,7 @@ type InferDataType<T> = T extends {
159
159
  * type Data = TAtscriptDataType<typeof MyInterface>
160
160
  * ```
161
161
  */
162
- type TAtscriptDataType<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType> = T extends {
162
+ type TAtscriptDataType$1<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType> = T extends {
163
163
  type: {
164
164
  __dataType?: infer D;
165
165
  };
@@ -535,5 +535,16 @@ declare function serializeAnnotatedType(type: TAtscriptAnnotatedType, options?:
535
535
  */
536
536
  declare function deserializeAnnotatedType(data: TSerializedAnnotatedType): TAtscriptAnnotatedType;
537
537
 
538
+ /**
539
+ * Extracts the flat dot-notation type map from an Atscript annotated type.
540
+ * If the type has a `__flat` static property (emitted for `@db.table` interfaces),
541
+ * returns that flat map. Otherwise falls back to `TAtscriptDataType<T>`.
542
+ *
543
+ * Use this for type-safe filters and selects with dot-notation field paths.
544
+ */
545
+ type FlatOf<T> = T extends {
546
+ __flat: infer F;
547
+ } ? F : TAtscriptDataType<T>;
548
+
538
549
  export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
539
- export type { InferDataType, TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptDataType, TAtscriptTypeArray, TAtscriptTypeComplex, TAtscriptTypeDef, TAtscriptTypeFinal, TAtscriptTypeObject, TCreateDataOptions, TFlattenOptions, TJsonSchema, TMetadataMap, TProcessAnnotationContext, TSerializeOptions, TSerializedAnnotatedType, TSerializedAnnotatedTypeInner, TSerializedTypeArray, TSerializedTypeComplex, TSerializedTypeDef, TSerializedTypeFinal, TSerializedTypeObject, TValidatorOptions, TValidatorPlugin, TValidatorPluginContext, TValueResolver };
550
+ export type { FlatOf, InferDataType, TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptDataType$1 as TAtscriptDataType, TAtscriptTypeArray, TAtscriptTypeComplex, TAtscriptTypeDef, TAtscriptTypeFinal, TAtscriptTypeObject, TCreateDataOptions, TFlattenOptions, TJsonSchema, TMetadataMap, TProcessAnnotationContext, TSerializeOptions, TSerializedAnnotatedType, TSerializedAnnotatedTypeInner, TSerializedTypeArray, TSerializedTypeComplex, TSerializedTypeDef, TSerializedTypeFinal, TSerializedTypeObject, TValidatorOptions, TValidatorPlugin, TValidatorPluginContext, TValueResolver };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/typescript",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "Atscript: typescript-gen support.",
5
5
  "keywords": [
6
6
  "annotations",
@@ -64,7 +64,7 @@
64
64
  "vitest": "3.2.4"
65
65
  },
66
66
  "peerDependencies": {
67
- "@atscript/core": "^0.1.30"
67
+ "@atscript/core": "^0.1.31"
68
68
  },
69
69
  "build": [
70
70
  {},