graphddb 0.6.0 → 0.7.1
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/cdc/index.d.ts +2 -2
- package/dist/{chunk-N5NQM3SO.js → chunk-C5Q2NMRW.js} +1072 -25
- package/dist/cli.js +336 -12
- package/dist/{from-change-CFzBy7aU.d.ts → from-change-CWiXBcgi.d.ts} +1 -1
- package/dist/{index-CtJbTrfB.d.ts → index-CvS9ATKB.d.ts} +289 -104
- package/dist/index.d.ts +85 -7
- package/dist/index.js +269 -1
- package/dist/linter/index.d.ts +4 -4
- package/dist/{maintenance-view-adapter-CFeasCKo.d.ts → maintenance-view-adapter-NBTZbE8-.d.ts} +2539 -2242
- package/dist/{registry-DbqmFyab.d.ts → registry-BndKbZbg.d.ts} +1 -1
- package/dist/{relation-depth-0TiWr5OW.d.ts → relation-depth-CCGHLTOv.d.ts} +1 -1
- package/dist/spec/index.d.ts +3 -3
- package/dist/spec/index.js +13 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/transform/index.d.ts +216 -0
- package/dist/transform/index.js +982 -0
- package/docs/cqrs-contract.md +1 -1
- package/docs/design-patterns.md +9 -7
- package/docs/prepared-statements.md +232 -0
- package/docs/python-bridge.md +24 -4
- package/package.json +5 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
buildBridgeBundle,
|
|
4
4
|
buildManifest,
|
|
5
5
|
buildOperations
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-C5Q2NMRW.js";
|
|
7
7
|
import {
|
|
8
8
|
MetadataRegistry,
|
|
9
9
|
normalizeSelectSpec
|
|
@@ -543,6 +543,41 @@ var commandDefinitions = {
|
|
|
543
543
|
}
|
|
544
544
|
}
|
|
545
545
|
]
|
|
546
|
+
},
|
|
547
|
+
"transform.prepared": {
|
|
548
|
+
"effects": {
|
|
549
|
+
"risk_level": "medium",
|
|
550
|
+
"reads": [
|
|
551
|
+
"ts-app-sources"
|
|
552
|
+
],
|
|
553
|
+
"writes": [
|
|
554
|
+
"ts-app-sources"
|
|
555
|
+
]
|
|
556
|
+
},
|
|
557
|
+
"options": [
|
|
558
|
+
{
|
|
559
|
+
"name": "src",
|
|
560
|
+
"schema": {
|
|
561
|
+
"type": "string"
|
|
562
|
+
},
|
|
563
|
+
"file": {
|
|
564
|
+
"mode": "read",
|
|
565
|
+
"exists": true
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
"name": "write",
|
|
570
|
+
"schema": {
|
|
571
|
+
"type": "boolean"
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
"name": "aot",
|
|
576
|
+
"schema": {
|
|
577
|
+
"type": "string"
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
]
|
|
546
581
|
}
|
|
547
582
|
};
|
|
548
583
|
function deriveCommandPolicy(command_id, optionValues) {
|
|
@@ -569,8 +604,8 @@ function deriveCommandPolicy(command_id, optionValues) {
|
|
|
569
604
|
}
|
|
570
605
|
|
|
571
606
|
// src/generated/contract.ts
|
|
572
|
-
var CONTRACT_YAML = "cli_contracts: 0.1.0\n\ninfo:\n title: GraphDDB CLI\n version: 0.1.0\n description: >-\n Contract for the graphddb code-generation CLI. The CLI reads TypeScript\n GraphDDB model / query / command definitions (the single source of truth)\n and emits Python bindings (manifest.json, operations.json, types.py,\n repositories.py, __init__.py) for the Python bridge runtime.\n license:\n name: MIT\n contact:\n name: foo-log-inc\n url: https://github.com/foo-log-inc/graphddb\n\nartifact_slots:\n ts-definitions:\n description: >-\n TypeScript GraphDDB definition modules (entity models, defineQueries,\n defineCommands) used as the single source of truth.\n direction: read\n generated-code:\n description: >-\n Generated Python bindings and JSON specs written to the output directory\n (manifest.json, operations.json, types.py, repositories.py, __init__.py).\n direction: write\n\ncommand_sets:\n graphddb:\n summary: Graph data modeling on DynamoDB \u2014 code generation CLI.\n x-stdin: >-\n No command reads from stdin. All inputs are provided via file options.\n\n commands:\n # \u2500\u2500 generate python \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n generate.python:\n summary: Generate Python bindings from TypeScript GraphDDB definitions.\n description: >-\n Loads the TypeScript entity models, query definitions, and command\n definitions, builds the serializable manifest / operations bundle, and\n writes the Python bridge bindings (manifest.json, operations.json,\n types.py, repositories.py, __init__.py) to the output directory.\n Output is deterministic for a given set of inputs.\n usage:\n - graphddb generate python --entry models.ts --queries queries.ts --commands commands.ts --out generated/\n - graphddb generate python --entry definitions.ts --out generated/\n\n effects:\n risk_level: low\n reads:\n - ts-definitions\n writes:\n - generated-code\n\n x-agent:\n recommended_before_use:\n - \"Ensure the TypeScript definitions type-check and lint cleanly.\"\n\n options:\n - name: entry\n aliases: [e]\n required: true\n description: >-\n Entry module that registers the entity models (side-effecting\n import). May also export queries / commands.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: queries\n aliases: [q]\n description: >-\n Module exporting `queries` (a defineQueries map). Defaults to the\n entry module when omitted.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: commands\n aliases: [c]\n description: >-\n Module exporting `commands` (a defineCommands map). Defaults to the\n entry module when omitted.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: transactions\n aliases: [t]\n description: >-\n Module exporting `transactions` (a defineTransactions map).\n Defaults to the commands module (or the entry module) when omitted.\n Optional \u2014 a project with no declarative transactions omits it.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: contracts\n description: >-\n Module exporting `contracts` (a map of publicQueryModel /\n publicCommandModel results \u2014 the CQRS Contract layer). Defaults to\n the entry module when omitted. Optional \u2014 a project with no\n contracts omits it, and the output gains no `contracts` content.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: contexts\n description: >-\n Module exporting `contexts` (context-ownership: context name \u2192 the\n Model / Contract names that belong to it). Defaults to the\n contracts module (or the entry module) when omitted. Optional \u2014\n consumed by the boundary lint to tell own from foreign.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: dataclass\n description: >-\n Emit Python result / element types as @dataclass classes instead\n of TypedDict (decimal / Optional / Connection mapped accordingly).\n schema:\n type: boolean\n\n - name: out\n aliases: [o]\n required: true\n description: Output directory for the generated Python bindings.\n value_name: dir\n schema:\n type: string\n\n exits:\n '0':\n description: Generation succeeded.\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/GenerateResult'\n files:\n - path: '{options.out}/manifest.json'\n required: true\n media_type: application/json\n description: Entity / table / key / relation metadata.\n - path: '{options.out}/operations.json'\n required: true\n media_type: application/json\n description: Per-query / per-command execution specs.\n - path: '{options.out}/types.py'\n required: true\n media_type: text/x-python\n description: TypedDict result types derived from query selects.\n - path: '{options.out}/repositories.py'\n required: true\n media_type: text/x-python\n description: Per-entity repository classes (sync methods).\n - path: '{options.out}/__init__.py'\n required: true\n media_type: text/x-python\n description: Package re-exports for the generated bindings.\n\n '1':\n description: Unexpected error.\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n '2':\n description: Invalid arguments (missing/unreadable input or output).\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n # \u2500\u2500 generate cloudformation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n generate.cloudformation:\n summary: >-\n Generate an AWS CloudFormation template (DynamoDB table + GSIs) from\n TypeScript GraphDDB definitions.\n description: >-\n Loads the TypeScript entity models and builds the serializable\n manifest, then emits an AWS CloudFormation template describing the\n DynamoDB table(s) the single-table design maps onto: one\n `AWS::DynamoDB::Table` per physical table, with the base key schema\n (`PK` / `SK`), the unioned Global Secondary Indexes\n (`<indexName>PK` / `<indexName>SK`), `PAY_PER_REQUEST` billing, a\n `TableName` parameter (default = physical name), and Outputs\n (table name + ARN). Output is deterministic and, by default, YAML.\n usage:\n - graphddb generate cloudformation --entry models.ts --out table.yaml\n - graphddb generate cloudformation --entry models.ts --format json\n - graphddb generate cloudformation --entry models.ts --gsi-projection KEYS_ONLY --out table.yaml\n - graphddb generate cloudformation --entry models.ts --stream on --out table.yaml\n - graphddb generate cloudformation --entry models.ts --pitr --sse --table-class STANDARD_INFREQUENT_ACCESS --out table.yaml\n - graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --rcu 10 --wcu 5 --out table.yaml\n - graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --autoscale --autoscale-min 5 --autoscale-max 100 --autoscale-target 70 --out table.yaml\n\n effects:\n risk_level: low\n reads:\n - ts-definitions\n writes:\n - generated-code\n\n x-agent:\n recommended_before_use:\n - \"Ensure the TypeScript definitions type-check and lint cleanly.\"\n\n options:\n - name: entry\n aliases: [e]\n required: true\n description: >-\n Entry module that registers the entity models (side-effecting\n import).\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: out\n aliases: [o]\n description: >-\n Output file for the generated CloudFormation template. Written to\n stdout when omitted.\n value_name: file\n schema:\n type: string\n\n - name: format\n aliases: [f]\n description: Output serialization format (yaml or json).\n value_name: format\n schema:\n type: string\n enum: [yaml, json]\n default: yaml\n\n - name: gsi-projection\n description: >-\n GSI ProjectionType. ALL (default) projects every attribute;\n KEYS_ONLY projects only the key attributes.\n value_name: projection\n schema:\n type: string\n enum: [ALL, KEYS_ONLY]\n default: ALL\n\n - name: stream\n description: >-\n DynamoDB Streams (StreamSpecification NEW_AND_OLD_IMAGES) emission.\n auto (default) emits a stream only when the model set uses\n stream-based maintenance (updateMode:'stream'); on always emits;\n off never emits. When enabled, a StreamArn Output is added too.\n value_name: mode\n schema:\n type: string\n enum: [auto, on, off]\n default: auto\n\n - name: billing-mode\n description: >-\n DynamoDB billing mode. PAY_PER_REQUEST (default) is on-demand\n (no provisioned throughput). PROVISIONED emits BillingMode:\n PROVISIONED with ProvisionedThroughput (see --rcu / --wcu) on the\n table and every GSI, and unlocks Application Auto Scaling\n (--autoscale).\n value_name: mode\n schema:\n type: string\n enum: [PAY_PER_REQUEST, PROVISIONED]\n default: PAY_PER_REQUEST\n\n - name: rcu\n description: >-\n Provisioned ReadCapacityUnits (a positive integer) applied to the\n table and every GSI. Only valid with --billing-mode PROVISIONED\n (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: wcu\n description: >-\n Provisioned WriteCapacityUnits (a positive integer) applied to the\n table and every GSI. Only valid with --billing-mode PROVISIONED\n (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: autoscale\n description: >-\n Enable Application Auto Scaling. Emits an\n AWS::ApplicationAutoScaling::ScalableTarget +\n AWS::ApplicationAutoScaling::ScalingPolicy pair for the table read\n and write capacity AND for each GSI read and write capacity. Only\n valid with --billing-mode PROVISIONED (autoscaling is invalid on\n on-demand and is rejected). Off by default \u2014 no autoscaling\n resources are emitted unless this flag is passed.\n schema:\n type: boolean\n\n - name: autoscale-min\n description: >-\n Auto Scaling minimum capacity (MinCapacity) for every scalable\n target. Must be <= --autoscale-max. Only meaningful with\n --autoscale. Defaults to 5 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: autoscale-max\n description: >-\n Auto Scaling maximum capacity (MaxCapacity) for every scalable\n target. Must be >= --autoscale-min. Only meaningful with\n --autoscale. Defaults to 100 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: autoscale-target\n description: >-\n Auto Scaling target utilization percentage (TargetValue of the\n TargetTrackingScaling policy). Only meaningful with --autoscale.\n Defaults to 70 when omitted.\n value_name: percent\n schema:\n type: integer\n minimum: 1\n maximum: 100\n\n - name: pitr\n description: >-\n Enable point-in-time recovery (PointInTimeRecoverySpecification\n PointInTimeRecoveryEnabled:true). Off by default \u2014 the property is\n omitted entirely unless this flag is passed.\n schema:\n type: boolean\n\n - name: table-class\n description: >-\n DynamoDB table class. Omitted by default (AWS defaults to\n STANDARD); pass STANDARD or STANDARD_INFREQUENT_ACCESS to emit\n TableClass.\n value_name: class\n schema:\n type: string\n enum: [STANDARD, STANDARD_INFREQUENT_ACCESS]\n\n - name: sse\n description: >-\n Enable server-side encryption with the AWS-owned/managed key\n (SSESpecification SSEEnabled:true). Off by default \u2014 the property is\n omitted entirely unless this flag is passed.\n schema:\n type: boolean\n\n exits:\n '0':\n description: Generation succeeded.\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/GenerateResult'\n\n '1':\n description: Unexpected error.\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n '2':\n description: Invalid arguments (missing/unreadable input or output).\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n # \u2500\u2500 generate docs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n generate.docs:\n summary: >-\n Generate model-specification documentation (Markdown + Mermaid) from\n TypeScript GraphDDB definitions.\n description: >-\n Loads the TypeScript entity models (and, optionally, query / command\n definitions), builds the serializable manifest / operations bundle, then\n renders human-facing model documentation \u2014 Table Overview, Entity\n Relationship Diagram, Physical Key Schema, Property Matrix, Entity\n Details, Access Patterns, Mutation Contracts, Maintained Access Paths,\n CDC, and CloudFormation summary \u2014 as Markdown with Mermaid diagrams. The\n layout is defined by bundled Handlebars templates users can override with\n --template-dir (per-partial) and extend with --helpers. Output is\n deterministic for a given set of inputs.\n usage:\n - graphddb generate docs --entry models.ts --out doc.md\n - graphddb generate docs --entry models.ts --queries definitions.ts --out doc.md\n - graphddb generate docs --entry models.ts --out docs/\n - graphddb generate docs --entry models.ts --out docs/ --template-dir doc-templates/ --helpers doc-helpers.js\n\n effects:\n risk_level: low\n reads:\n - ts-definitions\n writes:\n - generated-code\n\n x-agent:\n recommended_before_use:\n - \"Ensure the TypeScript definitions type-check and lint cleanly.\"\n\n options:\n - name: entry\n aliases: [e]\n required: true\n description: >-\n Entry module that registers the entity models (side-effecting\n import). May also export queries / commands.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: queries\n aliases: [q]\n description: >-\n Module exporting `queries` / `commands` (defineQueries /\n defineCommands maps) used for the Access Patterns / Mutation\n Contracts sections. Defaults to the entry module when omitted.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: out\n aliases: [o]\n required: true\n description: >-\n Output path for the generated documentation. A file path ending in\n .md writes the single Markdown file at exactly that path (creating\n parent directories); any other value is treated as a directory and\n the file is written as <dir>/index.md.\n value_name: path\n schema:\n type: string\n\n - name: format\n aliases: [f]\n description: Output format. Only markdown is supported today.\n value_name: format\n schema:\n type: string\n enum: [markdown]\n default: markdown\n\n - name: split\n description: >-\n Output granularity. none (default) emits a single index.md; entity\n is reserved for a future per-entity split.\n value_name: mode\n schema:\n type: string\n enum: [none, entity]\n default: none\n\n - name: template-dir\n description: >-\n Directory of override <partial>.hbs templates. Each same-named\n partial (index / table-overview / er-diagram / key-schema /\n property-matrix / entity / access-pattern / maintained / cdc /\n cloudformation) overrides the bundled one; absent partials fall back\n to the bundled default.\n value_name: dir\n schema:\n type: string\n\n - name: helpers\n description: >-\n JS module whose default (or named `helpers`) export is a map of extra\n Handlebars helpers to register alongside the built-in set (eq / join\n / pascal / mermaidId / codeOrEmpty).\n value_name: file\n schema:\n type: string\n\n - name: billing-mode\n description: >-\n DynamoDB billing mode used in the Table Overview (shared with\n generate cloudformation). PAY_PER_REQUEST (default) or PROVISIONED.\n value_name: mode\n schema:\n type: string\n enum: [PAY_PER_REQUEST, PROVISIONED]\n default: PAY_PER_REQUEST\n\n - name: stream\n description: >-\n DynamoDB Streams state used in the Table Overview (shared with\n generate cloudformation). auto (default) detects stream-based\n maintenance (updateMode:'stream'); on always enabled; off never.\n value_name: mode\n schema:\n type: string\n enum: [auto, on, off]\n default: auto\n\n exits:\n '0':\n description: Generation succeeded.\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/GenerateResult'\n\n '1':\n description: Unexpected error.\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n '2':\n description: Invalid arguments (missing/unreadable input or output).\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\ncomponents:\n schemas:\n GenerateResult:\n type: object\n required: [status, outDir, files]\n properties:\n status:\n type: string\n enum: [ok]\n outDir:\n type: string\n description: Output directory the files were written to.\n files:\n type: array\n description: Paths of the generated files (relative to outDir).\n items:\n type: string\n Error:\n type: object\n required: [code, message]\n properties:\n code:\n type: string\n message:\n type: string\n";
|
|
573
|
-
var CONTRACT_JSON_STR = '{\n "cli_contracts": "0.1.0",\n "info": {\n "title": "GraphDDB CLI",\n "version": "0.1.0",\n "description": "Contract for the graphddb code-generation CLI. The CLI reads TypeScript GraphDDB model / query / command definitions (the single source of truth) and emits Python bindings (manifest.json, operations.json, types.py, repositories.py, __init__.py) for the Python bridge runtime.",\n "license": {\n "name": "MIT"\n },\n "contact": {\n "name": "foo-log-inc",\n "url": "https://github.com/foo-log-inc/graphddb"\n }\n },\n "artifact_slots": {\n "ts-definitions": {\n "description": "TypeScript GraphDDB definition modules (entity models, defineQueries, defineCommands) used as the single source of truth.",\n "direction": "read"\n },\n "generated-code": {\n "description": "Generated Python bindings and JSON specs written to the output directory (manifest.json, operations.json, types.py, repositories.py, __init__.py).",\n "direction": "write"\n }\n },\n "command_sets": {\n "graphddb": {\n "summary": "Graph data modeling on DynamoDB \u2014 code generation CLI.",\n "x-stdin": "No command reads from stdin. All inputs are provided via file options.",\n "commands": {\n "generate.python": {\n "summary": "Generate Python bindings from TypeScript GraphDDB definitions.",\n "description": "Loads the TypeScript entity models, query definitions, and command definitions, builds the serializable manifest / operations bundle, and writes the Python bridge bindings (manifest.json, operations.json, types.py, repositories.py, __init__.py) to the output directory. Output is deterministic for a given set of inputs.",\n "usage": [\n "graphddb generate python --entry models.ts --queries queries.ts --commands commands.ts --out generated/",\n "graphddb generate python --entry definitions.ts --out generated/"\n ],\n "effects": {\n "risk_level": "low",\n "reads": [\n "ts-definitions"\n ],\n "writes": [\n "generated-code"\n ]\n },\n "x-agent": {\n "recommended_before_use": [\n "Ensure the TypeScript definitions type-check and lint cleanly."\n ]\n },\n "options": [\n {\n "name": "entry",\n "aliases": [\n "e"\n ],\n "required": true,\n "description": "Entry module that registers the entity models (side-effecting import). May also export queries / commands.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "queries",\n "aliases": [\n "q"\n ],\n "description": "Module exporting `queries` (a defineQueries map). Defaults to the entry module when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "commands",\n "aliases": [\n "c"\n ],\n "description": "Module exporting `commands` (a defineCommands map). Defaults to the entry module when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "transactions",\n "aliases": [\n "t"\n ],\n "description": "Module exporting `transactions` (a defineTransactions map). Defaults to the commands module (or the entry module) when omitted. Optional \u2014 a project with no declarative transactions omits it.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "contracts",\n "description": "Module exporting `contracts` (a map of publicQueryModel / publicCommandModel results \u2014 the CQRS Contract layer). Defaults to the entry module when omitted. Optional \u2014 a project with no contracts omits it, and the output gains no `contracts` content.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "contexts",\n "description": "Module exporting `contexts` (context-ownership: context name \u2192 the Model / Contract names that belong to it). Defaults to the contracts module (or the entry module) when omitted. Optional \u2014 consumed by the boundary lint to tell own from foreign.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "dataclass",\n "description": "Emit Python result / element types as @dataclass classes instead of TypedDict (decimal / Optional / Connection mapped accordingly).",\n "schema": {\n "type": "boolean"\n }\n },\n {\n "name": "out",\n "aliases": [\n "o"\n ],\n "required": true,\n "description": "Output directory for the generated Python bindings.",\n "value_name": "dir",\n "schema": {\n "type": "string"\n }\n }\n ],\n "exits": {\n "0": {\n "description": "Generation succeeded.",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/GenerateResult"\n }\n },\n "files": [\n {\n "path": "{options.out}/manifest.json",\n "required": true,\n "media_type": "application/json",\n "description": "Entity / table / key / relation metadata."\n },\n {\n "path": "{options.out}/operations.json",\n "required": true,\n "media_type": "application/json",\n "description": "Per-query / per-command execution specs."\n },\n {\n "path": "{options.out}/types.py",\n "required": true,\n "media_type": "text/x-python",\n "description": "TypedDict result types derived from query selects."\n },\n {\n "path": "{options.out}/repositories.py",\n "required": true,\n "media_type": "text/x-python",\n "description": "Per-entity repository classes (sync methods)."\n },\n {\n "path": "{options.out}/__init__.py",\n "required": true,\n "media_type": "text/x-python",\n "description": "Package re-exports for the generated bindings."\n }\n ]\n },\n "1": {\n "description": "Unexpected error.",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n },\n "2": {\n "description": "Invalid arguments (missing/unreadable input or output).",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n }\n }\n },\n "generate.cloudformation": {\n "summary": "Generate an AWS CloudFormation template (DynamoDB table + GSIs) from TypeScript GraphDDB definitions.",\n "description": "Loads the TypeScript entity models and builds the serializable manifest, then emits an AWS CloudFormation template describing the DynamoDB table(s) the single-table design maps onto: one `AWS::DynamoDB::Table` per physical table, with the base key schema (`PK` / `SK`), the unioned Global Secondary Indexes (`<indexName>PK` / `<indexName>SK`), `PAY_PER_REQUEST` billing, a `TableName` parameter (default = physical name), and Outputs (table name + ARN). Output is deterministic and, by default, YAML.",\n "usage": [\n "graphddb generate cloudformation --entry models.ts --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --format json",\n "graphddb generate cloudformation --entry models.ts --gsi-projection KEYS_ONLY --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --stream on --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --pitr --sse --table-class STANDARD_INFREQUENT_ACCESS --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --rcu 10 --wcu 5 --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --autoscale --autoscale-min 5 --autoscale-max 100 --autoscale-target 70 --out table.yaml"\n ],\n "effects": {\n "risk_level": "low",\n "reads": [\n "ts-definitions"\n ],\n "writes": [\n "generated-code"\n ]\n },\n "x-agent": {\n "recommended_before_use": [\n "Ensure the TypeScript definitions type-check and lint cleanly."\n ]\n },\n "options": [\n {\n "name": "entry",\n "aliases": [\n "e"\n ],\n "required": true,\n "description": "Entry module that registers the entity models (side-effecting import).",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "out",\n "aliases": [\n "o"\n ],\n "description": "Output file for the generated CloudFormation template. Written to stdout when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "format",\n "aliases": [\n "f"\n ],\n "description": "Output serialization format (yaml or json).",\n "value_name": "format",\n "schema": {\n "type": "string",\n "enum": [\n "yaml",\n "json"\n ],\n "default": "yaml"\n }\n },\n {\n "name": "gsi-projection",\n "description": "GSI ProjectionType. ALL (default) projects every attribute; KEYS_ONLY projects only the key attributes.",\n "value_name": "projection",\n "schema": {\n "type": "string",\n "enum": [\n "ALL",\n "KEYS_ONLY"\n ],\n "default": "ALL"\n }\n },\n {\n "name": "stream",\n "description": "DynamoDB Streams (StreamSpecification NEW_AND_OLD_IMAGES) emission. auto (default) emits a stream only when the model set uses stream-based maintenance (updateMode:\'stream\'); on always emits; off never emits. When enabled, a StreamArn Output is added too.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "auto",\n "on",\n "off"\n ],\n "default": "auto"\n }\n },\n {\n "name": "billing-mode",\n "description": "DynamoDB billing mode. PAY_PER_REQUEST (default) is on-demand (no provisioned throughput). PROVISIONED emits BillingMode: PROVISIONED with ProvisionedThroughput (see --rcu / --wcu) on the table and every GSI, and unlocks Application Auto Scaling (--autoscale).",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "PAY_PER_REQUEST",\n "PROVISIONED"\n ],\n "default": "PAY_PER_REQUEST"\n }\n },\n {\n "name": "rcu",\n "description": "Provisioned ReadCapacityUnits (a positive integer) applied to the table and every GSI. Only valid with --billing-mode PROVISIONED (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "wcu",\n "description": "Provisioned WriteCapacityUnits (a positive integer) applied to the table and every GSI. Only valid with --billing-mode PROVISIONED (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "autoscale",\n "description": "Enable Application Auto Scaling. Emits an AWS::ApplicationAutoScaling::ScalableTarget + AWS::ApplicationAutoScaling::ScalingPolicy pair for the table read and write capacity AND for each GSI read and write capacity. Only valid with --billing-mode PROVISIONED (autoscaling is invalid on on-demand and is rejected). Off by default \u2014 no autoscaling resources are emitted unless this flag is passed.",\n "schema": {\n "type": "boolean"\n }\n },\n {\n "name": "autoscale-min",\n "description": "Auto Scaling minimum capacity (MinCapacity) for every scalable target. Must be <= --autoscale-max. Only meaningful with --autoscale. Defaults to 5 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "autoscale-max",\n "description": "Auto Scaling maximum capacity (MaxCapacity) for every scalable target. Must be >= --autoscale-min. Only meaningful with --autoscale. Defaults to 100 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "autoscale-target",\n "description": "Auto Scaling target utilization percentage (TargetValue of the TargetTrackingScaling policy). Only meaningful with --autoscale. Defaults to 70 when omitted.",\n "value_name": "percent",\n "schema": {\n "type": "integer",\n "minimum": 1,\n "maximum": 100\n }\n },\n {\n "name": "pitr",\n "description": "Enable point-in-time recovery (PointInTimeRecoverySpecification PointInTimeRecoveryEnabled:true). Off by default \u2014 the property is omitted entirely unless this flag is passed.",\n "schema": {\n "type": "boolean"\n }\n },\n {\n "name": "table-class",\n "description": "DynamoDB table class. Omitted by default (AWS defaults to STANDARD); pass STANDARD or STANDARD_INFREQUENT_ACCESS to emit TableClass.",\n "value_name": "class",\n "schema": {\n "type": "string",\n "enum": [\n "STANDARD",\n "STANDARD_INFREQUENT_ACCESS"\n ]\n }\n },\n {\n "name": "sse",\n "description": "Enable server-side encryption with the AWS-owned/managed key (SSESpecification SSEEnabled:true). Off by default \u2014 the property is omitted entirely unless this flag is passed.",\n "schema": {\n "type": "boolean"\n }\n }\n ],\n "exits": {\n "0": {\n "description": "Generation succeeded.",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/GenerateResult"\n }\n }\n },\n "1": {\n "description": "Unexpected error.",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n },\n "2": {\n "description": "Invalid arguments (missing/unreadable input or output).",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n }\n }\n },\n "generate.docs": {\n "summary": "Generate model-specification documentation (Markdown + Mermaid) from TypeScript GraphDDB definitions.",\n "description": "Loads the TypeScript entity models (and, optionally, query / command definitions), builds the serializable manifest / operations bundle, then renders human-facing model documentation \u2014 Table Overview, Entity Relationship Diagram, Physical Key Schema, Property Matrix, Entity Details, Access Patterns, Mutation Contracts, Maintained Access Paths, CDC, and CloudFormation summary \u2014 as Markdown with Mermaid diagrams. The layout is defined by bundled Handlebars templates users can override with --template-dir (per-partial) and extend with --helpers. Output is deterministic for a given set of inputs.",\n "usage": [\n "graphddb generate docs --entry models.ts --out doc.md",\n "graphddb generate docs --entry models.ts --queries definitions.ts --out doc.md",\n "graphddb generate docs --entry models.ts --out docs/",\n "graphddb generate docs --entry models.ts --out docs/ --template-dir doc-templates/ --helpers doc-helpers.js"\n ],\n "effects": {\n "risk_level": "low",\n "reads": [\n "ts-definitions"\n ],\n "writes": [\n "generated-code"\n ]\n },\n "x-agent": {\n "recommended_before_use": [\n "Ensure the TypeScript definitions type-check and lint cleanly."\n ]\n },\n "options": [\n {\n "name": "entry",\n "aliases": [\n "e"\n ],\n "required": true,\n "description": "Entry module that registers the entity models (side-effecting import). May also export queries / commands.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "queries",\n "aliases": [\n "q"\n ],\n "description": "Module exporting `queries` / `commands` (defineQueries / defineCommands maps) used for the Access Patterns / Mutation Contracts sections. Defaults to the entry module when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "out",\n "aliases": [\n "o"\n ],\n "required": true,\n "description": "Output path for the generated documentation. A file path ending in .md writes the single Markdown file at exactly that path (creating parent directories); any other value is treated as a directory and the file is written as <dir>/index.md.",\n "value_name": "path",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "format",\n "aliases": [\n "f"\n ],\n "description": "Output format. Only markdown is supported today.",\n "value_name": "format",\n "schema": {\n "type": "string",\n "enum": [\n "markdown"\n ],\n "default": "markdown"\n }\n },\n {\n "name": "split",\n "description": "Output granularity. none (default) emits a single index.md; entity is reserved for a future per-entity split.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "none",\n "entity"\n ],\n "default": "none"\n }\n },\n {\n "name": "template-dir",\n "description": "Directory of override <partial>.hbs templates. Each same-named partial (index / table-overview / er-diagram / key-schema / property-matrix / entity / access-pattern / maintained / cdc / cloudformation) overrides the bundled one; absent partials fall back to the bundled default.",\n "value_name": "dir",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "helpers",\n "description": "JS module whose default (or named `helpers`) export is a map of extra Handlebars helpers to register alongside the built-in set (eq / join / pascal / mermaidId / codeOrEmpty).",\n "value_name": "file",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "billing-mode",\n "description": "DynamoDB billing mode used in the Table Overview (shared with generate cloudformation). PAY_PER_REQUEST (default) or PROVISIONED.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "PAY_PER_REQUEST",\n "PROVISIONED"\n ],\n "default": "PAY_PER_REQUEST"\n }\n },\n {\n "name": "stream",\n "description": "DynamoDB Streams state used in the Table Overview (shared with generate cloudformation). auto (default) detects stream-based maintenance (updateMode:\'stream\'); on always enabled; off never.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "auto",\n "on",\n "off"\n ],\n "default": "auto"\n }\n }\n ],\n "exits": {\n "0": {\n "description": "Generation succeeded.",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/GenerateResult"\n }\n }\n },\n "1": {\n "description": "Unexpected error.",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n },\n "2": {\n "description": "Invalid arguments (missing/unreadable input or output).",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n }\n }\n }\n }\n }\n },\n "components": {\n "schemas": {\n "GenerateResult": {\n "type": "object",\n "required": [\n "status",\n "outDir",\n "files"\n ],\n "properties": {\n "status": {\n "type": "string",\n "enum": [\n "ok"\n ]\n },\n "outDir": {\n "type": "string",\n "description": "Output directory the files were written to."\n },\n "files": {\n "type": "array",\n "description": "Paths of the generated files (relative to outDir).",\n "items": {\n "type": "string"\n }\n }\n }\n },\n "Error": {\n "type": "object",\n "required": [\n "code",\n "message"\n ],\n "properties": {\n "code": {\n "type": "string"\n },\n "message": {\n "type": "string"\n }\n }\n }\n }\n }\n}';
|
|
607
|
+
var CONTRACT_YAML = "cli_contracts: 0.1.0\n\ninfo:\n title: GraphDDB CLI\n version: 0.1.0\n description: >-\n Contract for the graphddb code-generation CLI. The CLI reads TypeScript\n GraphDDB model / query / command definitions (the single source of truth)\n and emits Python bindings (manifest.json, operations.json, types.py,\n repositories.py, __init__.py) for the Python bridge runtime.\n license:\n name: MIT\n contact:\n name: foo-log-inc\n url: https://github.com/foo-log-inc/graphddb\n\nartifact_slots:\n ts-definitions:\n description: >-\n TypeScript GraphDDB definition modules (entity models, defineQueries,\n defineCommands) used as the single source of truth.\n direction: read\n generated-code:\n description: >-\n Generated Python bindings and JSON specs written to the output directory\n (manifest.json, operations.json, types.py, repositories.py, __init__.py).\n direction: write\n ts-app-sources:\n description: >-\n Application TypeScript sources containing `DDBModel.prepare` call sites.\n Read by the prepared-statement build lint; rewritten in place by\n `transform prepared --write` (hoist normalization, issue #206).\n direction: write\n\ncommand_sets:\n graphddb:\n summary: Graph data modeling on DynamoDB \u2014 code generation CLI.\n x-stdin: >-\n No command reads from stdin. All inputs are provided via file options.\n\n commands:\n # \u2500\u2500 generate python \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n generate.python:\n summary: Generate Python bindings from TypeScript GraphDDB definitions.\n description: >-\n Loads the TypeScript entity models, query definitions, and command\n definitions, builds the serializable manifest / operations bundle, and\n writes the Python bridge bindings (manifest.json, operations.json,\n types.py, repositories.py, __init__.py) to the output directory.\n Output is deterministic for a given set of inputs.\n usage:\n - graphddb generate python --entry models.ts --queries queries.ts --commands commands.ts --out generated/\n - graphddb generate python --entry definitions.ts --out generated/\n\n effects:\n risk_level: low\n reads:\n - ts-definitions\n writes:\n - generated-code\n\n x-agent:\n recommended_before_use:\n - \"Ensure the TypeScript definitions type-check and lint cleanly.\"\n\n options:\n - name: entry\n aliases: [e]\n required: true\n description: >-\n Entry module that registers the entity models (side-effecting\n import). May also export queries / commands.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: queries\n aliases: [q]\n description: >-\n Module exporting `queries` (a defineQueries map). Defaults to the\n entry module when omitted.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: commands\n aliases: [c]\n description: >-\n Module exporting `commands` (a defineCommands map). Defaults to the\n entry module when omitted.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: transactions\n aliases: [t]\n description: >-\n Module exporting `transactions` (a defineTransactions map).\n Defaults to the commands module (or the entry module) when omitted.\n Optional \u2014 a project with no declarative transactions omits it.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: contracts\n description: >-\n Module exporting `contracts` (a map of publicQueryModel /\n publicCommandModel results \u2014 the CQRS Contract layer). Defaults to\n the entry module when omitted. Optional \u2014 a project with no\n contracts omits it, and the output gains no `contracts` content.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: contexts\n description: >-\n Module exporting `contexts` (context-ownership: context name \u2192 the\n Model / Contract names that belong to it). Defaults to the\n contracts module (or the entry module) when omitted. Optional \u2014\n consumed by the boundary lint to tell own from foreign.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: dataclass\n description: >-\n Emit Python result / element types as @dataclass classes instead\n of TypedDict (decimal / Optional / Connection mapped accordingly).\n schema:\n type: boolean\n\n - name: out\n aliases: [o]\n required: true\n description: Output directory for the generated Python bindings.\n value_name: dir\n schema:\n type: string\n\n exits:\n '0':\n description: Generation succeeded.\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/GenerateResult'\n files:\n - path: '{options.out}/manifest.json'\n required: true\n media_type: application/json\n description: Entity / table / key / relation metadata.\n - path: '{options.out}/operations.json'\n required: true\n media_type: application/json\n description: Per-query / per-command execution specs.\n - path: '{options.out}/types.py'\n required: true\n media_type: text/x-python\n description: TypedDict result types derived from query selects.\n - path: '{options.out}/repositories.py'\n required: true\n media_type: text/x-python\n description: Per-entity repository classes (sync methods).\n - path: '{options.out}/__init__.py'\n required: true\n media_type: text/x-python\n description: Package re-exports for the generated bindings.\n\n '1':\n description: Unexpected error.\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n '2':\n description: Invalid arguments (missing/unreadable input or output).\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n # \u2500\u2500 generate cloudformation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n generate.cloudformation:\n summary: >-\n Generate an AWS CloudFormation template (DynamoDB table + GSIs) from\n TypeScript GraphDDB definitions.\n description: >-\n Loads the TypeScript entity models and builds the serializable\n manifest, then emits an AWS CloudFormation template describing the\n DynamoDB table(s) the single-table design maps onto: one\n `AWS::DynamoDB::Table` per physical table, with the base key schema\n (`PK` / `SK`), the unioned Global Secondary Indexes\n (`<indexName>PK` / `<indexName>SK`), `PAY_PER_REQUEST` billing, a\n `TableName` parameter (default = physical name), and Outputs\n (table name + ARN). Output is deterministic and, by default, YAML.\n usage:\n - graphddb generate cloudformation --entry models.ts --out table.yaml\n - graphddb generate cloudformation --entry models.ts --format json\n - graphddb generate cloudformation --entry models.ts --gsi-projection KEYS_ONLY --out table.yaml\n - graphddb generate cloudformation --entry models.ts --stream on --out table.yaml\n - graphddb generate cloudformation --entry models.ts --pitr --sse --table-class STANDARD_INFREQUENT_ACCESS --out table.yaml\n - graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --rcu 10 --wcu 5 --out table.yaml\n - graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --autoscale --autoscale-min 5 --autoscale-max 100 --autoscale-target 70 --out table.yaml\n\n effects:\n risk_level: low\n reads:\n - ts-definitions\n writes:\n - generated-code\n\n x-agent:\n recommended_before_use:\n - \"Ensure the TypeScript definitions type-check and lint cleanly.\"\n\n options:\n - name: entry\n aliases: [e]\n required: true\n description: >-\n Entry module that registers the entity models (side-effecting\n import).\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: out\n aliases: [o]\n description: >-\n Output file for the generated CloudFormation template. Written to\n stdout when omitted.\n value_name: file\n schema:\n type: string\n\n - name: format\n aliases: [f]\n description: Output serialization format (yaml or json).\n value_name: format\n schema:\n type: string\n enum: [yaml, json]\n default: yaml\n\n - name: gsi-projection\n description: >-\n GSI ProjectionType. ALL (default) projects every attribute;\n KEYS_ONLY projects only the key attributes.\n value_name: projection\n schema:\n type: string\n enum: [ALL, KEYS_ONLY]\n default: ALL\n\n - name: stream\n description: >-\n DynamoDB Streams (StreamSpecification NEW_AND_OLD_IMAGES) emission.\n auto (default) emits a stream only when the model set uses\n stream-based maintenance (updateMode:'stream'); on always emits;\n off never emits. When enabled, a StreamArn Output is added too.\n value_name: mode\n schema:\n type: string\n enum: [auto, on, off]\n default: auto\n\n - name: billing-mode\n description: >-\n DynamoDB billing mode. PAY_PER_REQUEST (default) is on-demand\n (no provisioned throughput). PROVISIONED emits BillingMode:\n PROVISIONED with ProvisionedThroughput (see --rcu / --wcu) on the\n table and every GSI, and unlocks Application Auto Scaling\n (--autoscale).\n value_name: mode\n schema:\n type: string\n enum: [PAY_PER_REQUEST, PROVISIONED]\n default: PAY_PER_REQUEST\n\n - name: rcu\n description: >-\n Provisioned ReadCapacityUnits (a positive integer) applied to the\n table and every GSI. Only valid with --billing-mode PROVISIONED\n (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: wcu\n description: >-\n Provisioned WriteCapacityUnits (a positive integer) applied to the\n table and every GSI. Only valid with --billing-mode PROVISIONED\n (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: autoscale\n description: >-\n Enable Application Auto Scaling. Emits an\n AWS::ApplicationAutoScaling::ScalableTarget +\n AWS::ApplicationAutoScaling::ScalingPolicy pair for the table read\n and write capacity AND for each GSI read and write capacity. Only\n valid with --billing-mode PROVISIONED (autoscaling is invalid on\n on-demand and is rejected). Off by default \u2014 no autoscaling\n resources are emitted unless this flag is passed.\n schema:\n type: boolean\n\n - name: autoscale-min\n description: >-\n Auto Scaling minimum capacity (MinCapacity) for every scalable\n target. Must be <= --autoscale-max. Only meaningful with\n --autoscale. Defaults to 5 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: autoscale-max\n description: >-\n Auto Scaling maximum capacity (MaxCapacity) for every scalable\n target. Must be >= --autoscale-min. Only meaningful with\n --autoscale. Defaults to 100 when omitted.\n value_name: units\n schema:\n type: integer\n minimum: 1\n\n - name: autoscale-target\n description: >-\n Auto Scaling target utilization percentage (TargetValue of the\n TargetTrackingScaling policy). Only meaningful with --autoscale.\n Defaults to 70 when omitted.\n value_name: percent\n schema:\n type: integer\n minimum: 1\n maximum: 100\n\n - name: pitr\n description: >-\n Enable point-in-time recovery (PointInTimeRecoverySpecification\n PointInTimeRecoveryEnabled:true). Off by default \u2014 the property is\n omitted entirely unless this flag is passed.\n schema:\n type: boolean\n\n - name: table-class\n description: >-\n DynamoDB table class. Omitted by default (AWS defaults to\n STANDARD); pass STANDARD or STANDARD_INFREQUENT_ACCESS to emit\n TableClass.\n value_name: class\n schema:\n type: string\n enum: [STANDARD, STANDARD_INFREQUENT_ACCESS]\n\n - name: sse\n description: >-\n Enable server-side encryption with the AWS-owned/managed key\n (SSESpecification SSEEnabled:true). Off by default \u2014 the property is\n omitted entirely unless this flag is passed.\n schema:\n type: boolean\n\n exits:\n '0':\n description: Generation succeeded.\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/GenerateResult'\n\n '1':\n description: Unexpected error.\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n '2':\n description: Invalid arguments (missing/unreadable input or output).\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n # \u2500\u2500 generate docs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n generate.docs:\n summary: >-\n Generate model-specification documentation (Markdown + Mermaid) from\n TypeScript GraphDDB definitions.\n description: >-\n Loads the TypeScript entity models (and, optionally, query / command\n definitions), builds the serializable manifest / operations bundle, then\n renders human-facing model documentation \u2014 Table Overview, Entity\n Relationship Diagram, Physical Key Schema, Property Matrix, Entity\n Details, Access Patterns, Mutation Contracts, Maintained Access Paths,\n CDC, and CloudFormation summary \u2014 as Markdown with Mermaid diagrams. The\n layout is defined by bundled Handlebars templates users can override with\n --template-dir (per-partial) and extend with --helpers. Output is\n deterministic for a given set of inputs.\n usage:\n - graphddb generate docs --entry models.ts --out doc.md\n - graphddb generate docs --entry models.ts --queries definitions.ts --out doc.md\n - graphddb generate docs --entry models.ts --out docs/\n - graphddb generate docs --entry models.ts --out docs/ --template-dir doc-templates/ --helpers doc-helpers.js\n\n effects:\n risk_level: low\n reads:\n - ts-definitions\n writes:\n - generated-code\n\n x-agent:\n recommended_before_use:\n - \"Ensure the TypeScript definitions type-check and lint cleanly.\"\n\n options:\n - name: entry\n aliases: [e]\n required: true\n description: >-\n Entry module that registers the entity models (side-effecting\n import). May also export queries / commands.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: queries\n aliases: [q]\n description: >-\n Module exporting `queries` / `commands` (defineQueries /\n defineCommands maps) used for the Access Patterns / Mutation\n Contracts sections. Defaults to the entry module when omitted.\n value_name: file\n schema:\n type: string\n file:\n mode: read\n exists: true\n media_type: application/typescript\n encoding: utf-8\n\n - name: out\n aliases: [o]\n required: true\n description: >-\n Output path for the generated documentation. A file path ending in\n .md writes the single Markdown file at exactly that path (creating\n parent directories); any other value is treated as a directory and\n the file is written as <dir>/index.md.\n value_name: path\n schema:\n type: string\n\n - name: format\n aliases: [f]\n description: Output format. Only markdown is supported today.\n value_name: format\n schema:\n type: string\n enum: [markdown]\n default: markdown\n\n - name: split\n description: >-\n Output granularity. none (default) emits a single index.md; entity\n is reserved for a future per-entity split.\n value_name: mode\n schema:\n type: string\n enum: [none, entity]\n default: none\n\n - name: template-dir\n description: >-\n Directory of override <partial>.hbs templates. Each same-named\n partial (index / table-overview / er-diagram / key-schema /\n property-matrix / entity / access-pattern / maintained / cdc /\n cloudformation) overrides the bundled one; absent partials fall back\n to the bundled default.\n value_name: dir\n schema:\n type: string\n\n - name: helpers\n description: >-\n JS module whose default (or named `helpers`) export is a map of extra\n Handlebars helpers to register alongside the built-in set (eq / join\n / pascal / mermaidId / codeOrEmpty).\n value_name: file\n schema:\n type: string\n\n - name: billing-mode\n description: >-\n DynamoDB billing mode used in the Table Overview (shared with\n generate cloudformation). PAY_PER_REQUEST (default) or PROVISIONED.\n value_name: mode\n schema:\n type: string\n enum: [PAY_PER_REQUEST, PROVISIONED]\n default: PAY_PER_REQUEST\n\n - name: stream\n description: >-\n DynamoDB Streams state used in the Table Overview (shared with\n generate cloudformation). auto (default) detects stream-based\n maintenance (updateMode:'stream'); on always enabled; off never.\n value_name: mode\n schema:\n type: string\n enum: [auto, on, off]\n default: auto\n\n exits:\n '0':\n description: Generation succeeded.\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/GenerateResult'\n\n '1':\n description: Unexpected error.\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n '2':\n description: Invalid arguments (missing/unreadable input or output).\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\n # \u2500\u2500 transform prepared \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n transform.prepared:\n summary: >-\n Lint and hoist-normalize `DDBModel.prepare` call sites (compile-time\n prepared statements, issue #206).\n description: >-\n A source-to-source build pass over application TypeScript files. It\n statically enforces the prepared-statement no-runtime-capture rule\n (a prepare body may reference only its `$` placeholder and\n module-static bindings; capturing a per-call runtime value \u2014 directly\n or via a helper call \u2014 is a loud error), and normalizes every\n verified INLINE `DDBModel.prepare(fn)` call site into a module-scope\n prepared slot so the plan compiles once per module and each call is\n a plain `.execute` (zero per-op planning / hashing \u2014 placement\n independent). Without --write it is a pure check (the build lint):\n violations exit non-zero and hoistable inline call sites are\n reported as notes. With --write, verified inline call sites are\n rewritten in place; files with violations are never rewritten.\n Idempotent: re-running on transformed output changes nothing.\n With --aot <artifact> (issue #208), the pass additionally compiles\n every verified prepare body at BUILD time into a static prepared-plan\n artifact (a generated TypeScript module): with --write it writes the\n artifact and rewrites each call site to load its static plan\n (`loadPreparedPlan(document, id, body)` \u2014 the body stays in source as\n the plan's compile source and fallback); without --write it is the\n DRIFT CHECK \u2014 it recompiles every plan and exits non-zero when the\n artifact is missing or differs (CI gate: model declarations changed\n without regenerating the artifact).\n usage:\n - graphddb transform prepared --src src/\n - graphddb transform prepared --src src/ --write\n - graphddb transform prepared --src src/app/handlers.ts --write\n - graphddb transform prepared --src src/ --aot src/graphddb.prepared.ts --write\n - graphddb transform prepared --src src/ --aot src/graphddb.prepared.ts\n\n effects:\n risk_level: medium\n reads:\n - ts-app-sources\n writes:\n - ts-app-sources\n\n x-agent:\n recommended_before_use:\n - \"Commit or stash pending changes first when using --write (it rewrites sources in place).\"\n\n options:\n - name: src\n aliases: [s]\n required: true\n description: >-\n A TypeScript source file, or a directory scanned recursively for\n .ts/.tsx/.mts/.cts files (node_modules, dist, hidden directories,\n and .d.ts files are skipped).\n value_name: path\n schema:\n type: string\n file:\n mode: read\n exists: true\n\n - name: write\n aliases: [w]\n description: >-\n Rewrite verified inline call sites in place (hoist\n normalization). Without it the command only checks and reports.\n schema:\n type: boolean\n\n - name: aot\n aliases: [a]\n description: >-\n Static prepared-plan artifact module path (issue #208). Enables\n build-time (AOT) compilation of every verified prepare body:\n with --write the artifact module is (re)generated and call sites\n are rewritten to load their static plan; without --write the\n pass recompiles and compares (drift check \u2014 a missing or\n differing artifact exits 1).\n value_name: artifact\n schema:\n type: string\n\n exits:\n '0':\n description: >-\n Check passed / transform applied. No no-runtime-capture\n violations were found.\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/TransformPreparedResult'\n\n '1':\n description: >-\n No-runtime-capture violations found, or --aot drift detected\n (reported in the result on stdout), or an unexpected error\n (reported on stderr).\n stdout:\n format: json\n schema:\n $ref: '#/components/schemas/TransformPreparedResult'\n\n '2':\n description: Invalid arguments (missing/unreadable input).\n stderr:\n format: json\n schema:\n $ref: '#/components/schemas/Error'\n\ncomponents:\n schemas:\n GenerateResult:\n type: object\n required: [status, outDir, files]\n properties:\n status:\n type: string\n enum: [ok]\n outDir:\n type: string\n description: Output directory the files were written to.\n files:\n type: array\n description: Paths of the generated files (relative to outDir).\n items:\n type: string\n Error:\n type: object\n required: [code, message]\n properties:\n code:\n type: string\n message:\n type: string\n TransformPreparedResult:\n type: object\n required: [status, files, hoisted, moduleScope, alreadyHoisted, errors, diagnostics]\n properties:\n status:\n type: string\n enum: [ok, violations, drift]\n files:\n type: array\n description: Source files scanned.\n items:\n type: string\n changedFiles:\n type: array\n description: Files rewritten in place (--write only).\n items:\n type: string\n hoisted:\n type: integer\n description: Inline call sites normalized (or normalizable in check mode).\n moduleScope:\n type: integer\n description: Call sites already evaluated once per module load (left as-is).\n alreadyHoisted:\n type: integer\n description: Call sites already normalized by a previous run.\n errors:\n type: integer\n description: No-runtime-capture violations found.\n artifact:\n type: string\n description: The static prepared-plan artifact path (--aot only).\n plans:\n type: integer\n description: Prepared plans compiled into the artifact (--aot only).\n drift:\n type: object\n description: >-\n Artifact drift detail (--aot check mode only; present when status\n is drift).\n properties:\n missing:\n type: boolean\n description: The artifact module does not exist.\n added:\n type: array\n items:\n type: string\n removed:\n type: array\n items:\n type: string\n changed:\n type: array\n items:\n type: string\n diagnostics:\n type: array\n description: Per-position lint diagnostics (errors and notes).\n items:\n type: object\n required: [code, category, message, file, line, column]\n properties:\n code:\n type: string\n category:\n type: string\n enum: [error, note]\n message:\n type: string\n file:\n type: string\n line:\n type: integer\n column:\n type: integer\n";
|
|
608
|
+
var CONTRACT_JSON_STR = '{\n "cli_contracts": "0.1.0",\n "info": {\n "title": "GraphDDB CLI",\n "version": "0.1.0",\n "description": "Contract for the graphddb code-generation CLI. The CLI reads TypeScript GraphDDB model / query / command definitions (the single source of truth) and emits Python bindings (manifest.json, operations.json, types.py, repositories.py, __init__.py) for the Python bridge runtime.",\n "license": {\n "name": "MIT"\n },\n "contact": {\n "name": "foo-log-inc",\n "url": "https://github.com/foo-log-inc/graphddb"\n }\n },\n "artifact_slots": {\n "ts-definitions": {\n "description": "TypeScript GraphDDB definition modules (entity models, defineQueries, defineCommands) used as the single source of truth.",\n "direction": "read"\n },\n "generated-code": {\n "description": "Generated Python bindings and JSON specs written to the output directory (manifest.json, operations.json, types.py, repositories.py, __init__.py).",\n "direction": "write"\n },\n "ts-app-sources": {\n "description": "Application TypeScript sources containing `DDBModel.prepare` call sites. Read by the prepared-statement build lint; rewritten in place by `transform prepared --write` (hoist normalization, issue #206).",\n "direction": "write"\n }\n },\n "command_sets": {\n "graphddb": {\n "summary": "Graph data modeling on DynamoDB \u2014 code generation CLI.",\n "x-stdin": "No command reads from stdin. All inputs are provided via file options.",\n "commands": {\n "generate.python": {\n "summary": "Generate Python bindings from TypeScript GraphDDB definitions.",\n "description": "Loads the TypeScript entity models, query definitions, and command definitions, builds the serializable manifest / operations bundle, and writes the Python bridge bindings (manifest.json, operations.json, types.py, repositories.py, __init__.py) to the output directory. Output is deterministic for a given set of inputs.",\n "usage": [\n "graphddb generate python --entry models.ts --queries queries.ts --commands commands.ts --out generated/",\n "graphddb generate python --entry definitions.ts --out generated/"\n ],\n "effects": {\n "risk_level": "low",\n "reads": [\n "ts-definitions"\n ],\n "writes": [\n "generated-code"\n ]\n },\n "x-agent": {\n "recommended_before_use": [\n "Ensure the TypeScript definitions type-check and lint cleanly."\n ]\n },\n "options": [\n {\n "name": "entry",\n "aliases": [\n "e"\n ],\n "required": true,\n "description": "Entry module that registers the entity models (side-effecting import). May also export queries / commands.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "queries",\n "aliases": [\n "q"\n ],\n "description": "Module exporting `queries` (a defineQueries map). Defaults to the entry module when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "commands",\n "aliases": [\n "c"\n ],\n "description": "Module exporting `commands` (a defineCommands map). Defaults to the entry module when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "transactions",\n "aliases": [\n "t"\n ],\n "description": "Module exporting `transactions` (a defineTransactions map). Defaults to the commands module (or the entry module) when omitted. Optional \u2014 a project with no declarative transactions omits it.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "contracts",\n "description": "Module exporting `contracts` (a map of publicQueryModel / publicCommandModel results \u2014 the CQRS Contract layer). Defaults to the entry module when omitted. Optional \u2014 a project with no contracts omits it, and the output gains no `contracts` content.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "contexts",\n "description": "Module exporting `contexts` (context-ownership: context name \u2192 the Model / Contract names that belong to it). Defaults to the contracts module (or the entry module) when omitted. Optional \u2014 consumed by the boundary lint to tell own from foreign.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "dataclass",\n "description": "Emit Python result / element types as @dataclass classes instead of TypedDict (decimal / Optional / Connection mapped accordingly).",\n "schema": {\n "type": "boolean"\n }\n },\n {\n "name": "out",\n "aliases": [\n "o"\n ],\n "required": true,\n "description": "Output directory for the generated Python bindings.",\n "value_name": "dir",\n "schema": {\n "type": "string"\n }\n }\n ],\n "exits": {\n "0": {\n "description": "Generation succeeded.",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/GenerateResult"\n }\n },\n "files": [\n {\n "path": "{options.out}/manifest.json",\n "required": true,\n "media_type": "application/json",\n "description": "Entity / table / key / relation metadata."\n },\n {\n "path": "{options.out}/operations.json",\n "required": true,\n "media_type": "application/json",\n "description": "Per-query / per-command execution specs."\n },\n {\n "path": "{options.out}/types.py",\n "required": true,\n "media_type": "text/x-python",\n "description": "TypedDict result types derived from query selects."\n },\n {\n "path": "{options.out}/repositories.py",\n "required": true,\n "media_type": "text/x-python",\n "description": "Per-entity repository classes (sync methods)."\n },\n {\n "path": "{options.out}/__init__.py",\n "required": true,\n "media_type": "text/x-python",\n "description": "Package re-exports for the generated bindings."\n }\n ]\n },\n "1": {\n "description": "Unexpected error.",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n },\n "2": {\n "description": "Invalid arguments (missing/unreadable input or output).",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n }\n }\n },\n "generate.cloudformation": {\n "summary": "Generate an AWS CloudFormation template (DynamoDB table + GSIs) from TypeScript GraphDDB definitions.",\n "description": "Loads the TypeScript entity models and builds the serializable manifest, then emits an AWS CloudFormation template describing the DynamoDB table(s) the single-table design maps onto: one `AWS::DynamoDB::Table` per physical table, with the base key schema (`PK` / `SK`), the unioned Global Secondary Indexes (`<indexName>PK` / `<indexName>SK`), `PAY_PER_REQUEST` billing, a `TableName` parameter (default = physical name), and Outputs (table name + ARN). Output is deterministic and, by default, YAML.",\n "usage": [\n "graphddb generate cloudformation --entry models.ts --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --format json",\n "graphddb generate cloudformation --entry models.ts --gsi-projection KEYS_ONLY --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --stream on --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --pitr --sse --table-class STANDARD_INFREQUENT_ACCESS --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --rcu 10 --wcu 5 --out table.yaml",\n "graphddb generate cloudformation --entry models.ts --billing-mode PROVISIONED --autoscale --autoscale-min 5 --autoscale-max 100 --autoscale-target 70 --out table.yaml"\n ],\n "effects": {\n "risk_level": "low",\n "reads": [\n "ts-definitions"\n ],\n "writes": [\n "generated-code"\n ]\n },\n "x-agent": {\n "recommended_before_use": [\n "Ensure the TypeScript definitions type-check and lint cleanly."\n ]\n },\n "options": [\n {\n "name": "entry",\n "aliases": [\n "e"\n ],\n "required": true,\n "description": "Entry module that registers the entity models (side-effecting import).",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "out",\n "aliases": [\n "o"\n ],\n "description": "Output file for the generated CloudFormation template. Written to stdout when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "format",\n "aliases": [\n "f"\n ],\n "description": "Output serialization format (yaml or json).",\n "value_name": "format",\n "schema": {\n "type": "string",\n "enum": [\n "yaml",\n "json"\n ],\n "default": "yaml"\n }\n },\n {\n "name": "gsi-projection",\n "description": "GSI ProjectionType. ALL (default) projects every attribute; KEYS_ONLY projects only the key attributes.",\n "value_name": "projection",\n "schema": {\n "type": "string",\n "enum": [\n "ALL",\n "KEYS_ONLY"\n ],\n "default": "ALL"\n }\n },\n {\n "name": "stream",\n "description": "DynamoDB Streams (StreamSpecification NEW_AND_OLD_IMAGES) emission. auto (default) emits a stream only when the model set uses stream-based maintenance (updateMode:\'stream\'); on always emits; off never emits. When enabled, a StreamArn Output is added too.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "auto",\n "on",\n "off"\n ],\n "default": "auto"\n }\n },\n {\n "name": "billing-mode",\n "description": "DynamoDB billing mode. PAY_PER_REQUEST (default) is on-demand (no provisioned throughput). PROVISIONED emits BillingMode: PROVISIONED with ProvisionedThroughput (see --rcu / --wcu) on the table and every GSI, and unlocks Application Auto Scaling (--autoscale).",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "PAY_PER_REQUEST",\n "PROVISIONED"\n ],\n "default": "PAY_PER_REQUEST"\n }\n },\n {\n "name": "rcu",\n "description": "Provisioned ReadCapacityUnits (a positive integer) applied to the table and every GSI. Only valid with --billing-mode PROVISIONED (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "wcu",\n "description": "Provisioned WriteCapacityUnits (a positive integer) applied to the table and every GSI. Only valid with --billing-mode PROVISIONED (rejected with PAY_PER_REQUEST). Defaults to 5 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "autoscale",\n "description": "Enable Application Auto Scaling. Emits an AWS::ApplicationAutoScaling::ScalableTarget + AWS::ApplicationAutoScaling::ScalingPolicy pair for the table read and write capacity AND for each GSI read and write capacity. Only valid with --billing-mode PROVISIONED (autoscaling is invalid on on-demand and is rejected). Off by default \u2014 no autoscaling resources are emitted unless this flag is passed.",\n "schema": {\n "type": "boolean"\n }\n },\n {\n "name": "autoscale-min",\n "description": "Auto Scaling minimum capacity (MinCapacity) for every scalable target. Must be <= --autoscale-max. Only meaningful with --autoscale. Defaults to 5 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "autoscale-max",\n "description": "Auto Scaling maximum capacity (MaxCapacity) for every scalable target. Must be >= --autoscale-min. Only meaningful with --autoscale. Defaults to 100 when omitted.",\n "value_name": "units",\n "schema": {\n "type": "integer",\n "minimum": 1\n }\n },\n {\n "name": "autoscale-target",\n "description": "Auto Scaling target utilization percentage (TargetValue of the TargetTrackingScaling policy). Only meaningful with --autoscale. Defaults to 70 when omitted.",\n "value_name": "percent",\n "schema": {\n "type": "integer",\n "minimum": 1,\n "maximum": 100\n }\n },\n {\n "name": "pitr",\n "description": "Enable point-in-time recovery (PointInTimeRecoverySpecification PointInTimeRecoveryEnabled:true). Off by default \u2014 the property is omitted entirely unless this flag is passed.",\n "schema": {\n "type": "boolean"\n }\n },\n {\n "name": "table-class",\n "description": "DynamoDB table class. Omitted by default (AWS defaults to STANDARD); pass STANDARD or STANDARD_INFREQUENT_ACCESS to emit TableClass.",\n "value_name": "class",\n "schema": {\n "type": "string",\n "enum": [\n "STANDARD",\n "STANDARD_INFREQUENT_ACCESS"\n ]\n }\n },\n {\n "name": "sse",\n "description": "Enable server-side encryption with the AWS-owned/managed key (SSESpecification SSEEnabled:true). Off by default \u2014 the property is omitted entirely unless this flag is passed.",\n "schema": {\n "type": "boolean"\n }\n }\n ],\n "exits": {\n "0": {\n "description": "Generation succeeded.",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/GenerateResult"\n }\n }\n },\n "1": {\n "description": "Unexpected error.",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n },\n "2": {\n "description": "Invalid arguments (missing/unreadable input or output).",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n }\n }\n },\n "generate.docs": {\n "summary": "Generate model-specification documentation (Markdown + Mermaid) from TypeScript GraphDDB definitions.",\n "description": "Loads the TypeScript entity models (and, optionally, query / command definitions), builds the serializable manifest / operations bundle, then renders human-facing model documentation \u2014 Table Overview, Entity Relationship Diagram, Physical Key Schema, Property Matrix, Entity Details, Access Patterns, Mutation Contracts, Maintained Access Paths, CDC, and CloudFormation summary \u2014 as Markdown with Mermaid diagrams. The layout is defined by bundled Handlebars templates users can override with --template-dir (per-partial) and extend with --helpers. Output is deterministic for a given set of inputs.",\n "usage": [\n "graphddb generate docs --entry models.ts --out doc.md",\n "graphddb generate docs --entry models.ts --queries definitions.ts --out doc.md",\n "graphddb generate docs --entry models.ts --out docs/",\n "graphddb generate docs --entry models.ts --out docs/ --template-dir doc-templates/ --helpers doc-helpers.js"\n ],\n "effects": {\n "risk_level": "low",\n "reads": [\n "ts-definitions"\n ],\n "writes": [\n "generated-code"\n ]\n },\n "x-agent": {\n "recommended_before_use": [\n "Ensure the TypeScript definitions type-check and lint cleanly."\n ]\n },\n "options": [\n {\n "name": "entry",\n "aliases": [\n "e"\n ],\n "required": true,\n "description": "Entry module that registers the entity models (side-effecting import). May also export queries / commands.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "queries",\n "aliases": [\n "q"\n ],\n "description": "Module exporting `queries` / `commands` (defineQueries / defineCommands maps) used for the Access Patterns / Mutation Contracts sections. Defaults to the entry module when omitted.",\n "value_name": "file",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true,\n "media_type": "application/typescript",\n "encoding": "utf-8"\n }\n },\n {\n "name": "out",\n "aliases": [\n "o"\n ],\n "required": true,\n "description": "Output path for the generated documentation. A file path ending in .md writes the single Markdown file at exactly that path (creating parent directories); any other value is treated as a directory and the file is written as <dir>/index.md.",\n "value_name": "path",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "format",\n "aliases": [\n "f"\n ],\n "description": "Output format. Only markdown is supported today.",\n "value_name": "format",\n "schema": {\n "type": "string",\n "enum": [\n "markdown"\n ],\n "default": "markdown"\n }\n },\n {\n "name": "split",\n "description": "Output granularity. none (default) emits a single index.md; entity is reserved for a future per-entity split.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "none",\n "entity"\n ],\n "default": "none"\n }\n },\n {\n "name": "template-dir",\n "description": "Directory of override <partial>.hbs templates. Each same-named partial (index / table-overview / er-diagram / key-schema / property-matrix / entity / access-pattern / maintained / cdc / cloudformation) overrides the bundled one; absent partials fall back to the bundled default.",\n "value_name": "dir",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "helpers",\n "description": "JS module whose default (or named `helpers`) export is a map of extra Handlebars helpers to register alongside the built-in set (eq / join / pascal / mermaidId / codeOrEmpty).",\n "value_name": "file",\n "schema": {\n "type": "string"\n }\n },\n {\n "name": "billing-mode",\n "description": "DynamoDB billing mode used in the Table Overview (shared with generate cloudformation). PAY_PER_REQUEST (default) or PROVISIONED.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "PAY_PER_REQUEST",\n "PROVISIONED"\n ],\n "default": "PAY_PER_REQUEST"\n }\n },\n {\n "name": "stream",\n "description": "DynamoDB Streams state used in the Table Overview (shared with generate cloudformation). auto (default) detects stream-based maintenance (updateMode:\'stream\'); on always enabled; off never.",\n "value_name": "mode",\n "schema": {\n "type": "string",\n "enum": [\n "auto",\n "on",\n "off"\n ],\n "default": "auto"\n }\n }\n ],\n "exits": {\n "0": {\n "description": "Generation succeeded.",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/GenerateResult"\n }\n }\n },\n "1": {\n "description": "Unexpected error.",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n },\n "2": {\n "description": "Invalid arguments (missing/unreadable input or output).",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n }\n }\n },\n "transform.prepared": {\n "summary": "Lint and hoist-normalize `DDBModel.prepare` call sites (compile-time prepared statements, issue #206).",\n "description": "A source-to-source build pass over application TypeScript files. It statically enforces the prepared-statement no-runtime-capture rule (a prepare body may reference only its `$` placeholder and module-static bindings; capturing a per-call runtime value \u2014 directly or via a helper call \u2014 is a loud error), and normalizes every verified INLINE `DDBModel.prepare(fn)` call site into a module-scope prepared slot so the plan compiles once per module and each call is a plain `.execute` (zero per-op planning / hashing \u2014 placement independent). Without --write it is a pure check (the build lint): violations exit non-zero and hoistable inline call sites are reported as notes. With --write, verified inline call sites are rewritten in place; files with violations are never rewritten. Idempotent: re-running on transformed output changes nothing. With --aot <artifact> (issue #208), the pass additionally compiles every verified prepare body at BUILD time into a static prepared-plan artifact (a generated TypeScript module): with --write it writes the artifact and rewrites each call site to load its static plan (`loadPreparedPlan(document, id, body)` \u2014 the body stays in source as the plan\'s compile source and fallback); without --write it is the DRIFT CHECK \u2014 it recompiles every plan and exits non-zero when the artifact is missing or differs (CI gate: model declarations changed without regenerating the artifact).",\n "usage": [\n "graphddb transform prepared --src src/",\n "graphddb transform prepared --src src/ --write",\n "graphddb transform prepared --src src/app/handlers.ts --write",\n "graphddb transform prepared --src src/ --aot src/graphddb.prepared.ts --write",\n "graphddb transform prepared --src src/ --aot src/graphddb.prepared.ts"\n ],\n "effects": {\n "risk_level": "medium",\n "reads": [\n "ts-app-sources"\n ],\n "writes": [\n "ts-app-sources"\n ]\n },\n "x-agent": {\n "recommended_before_use": [\n "Commit or stash pending changes first when using --write (it rewrites sources in place)."\n ]\n },\n "options": [\n {\n "name": "src",\n "aliases": [\n "s"\n ],\n "required": true,\n "description": "A TypeScript source file, or a directory scanned recursively for .ts/.tsx/.mts/.cts files (node_modules, dist, hidden directories, and .d.ts files are skipped).",\n "value_name": "path",\n "schema": {\n "type": "string"\n },\n "file": {\n "mode": "read",\n "exists": true\n }\n },\n {\n "name": "write",\n "aliases": [\n "w"\n ],\n "description": "Rewrite verified inline call sites in place (hoist normalization). Without it the command only checks and reports.",\n "schema": {\n "type": "boolean"\n }\n },\n {\n "name": "aot",\n "aliases": [\n "a"\n ],\n "description": "Static prepared-plan artifact module path (issue #208). Enables build-time (AOT) compilation of every verified prepare body: with --write the artifact module is (re)generated and call sites are rewritten to load their static plan; without --write the pass recompiles and compares (drift check \u2014 a missing or differing artifact exits 1).",\n "value_name": "artifact",\n "schema": {\n "type": "string"\n }\n }\n ],\n "exits": {\n "0": {\n "description": "Check passed / transform applied. No no-runtime-capture violations were found.",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/TransformPreparedResult"\n }\n }\n },\n "1": {\n "description": "No-runtime-capture violations found, or --aot drift detected (reported in the result on stdout), or an unexpected error (reported on stderr).",\n "stdout": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/TransformPreparedResult"\n }\n }\n },\n "2": {\n "description": "Invalid arguments (missing/unreadable input).",\n "stderr": {\n "format": "json",\n "schema": {\n "$ref": "#/components/schemas/Error"\n }\n }\n }\n }\n }\n }\n }\n },\n "components": {\n "schemas": {\n "GenerateResult": {\n "type": "object",\n "required": [\n "status",\n "outDir",\n "files"\n ],\n "properties": {\n "status": {\n "type": "string",\n "enum": [\n "ok"\n ]\n },\n "outDir": {\n "type": "string",\n "description": "Output directory the files were written to."\n },\n "files": {\n "type": "array",\n "description": "Paths of the generated files (relative to outDir).",\n "items": {\n "type": "string"\n }\n }\n }\n },\n "Error": {\n "type": "object",\n "required": [\n "code",\n "message"\n ],\n "properties": {\n "code": {\n "type": "string"\n },\n "message": {\n "type": "string"\n }\n }\n },\n "TransformPreparedResult": {\n "type": "object",\n "required": [\n "status",\n "files",\n "hoisted",\n "moduleScope",\n "alreadyHoisted",\n "errors",\n "diagnostics"\n ],\n "properties": {\n "status": {\n "type": "string",\n "enum": [\n "ok",\n "violations",\n "drift"\n ]\n },\n "files": {\n "type": "array",\n "description": "Source files scanned.",\n "items": {\n "type": "string"\n }\n },\n "changedFiles": {\n "type": "array",\n "description": "Files rewritten in place (--write only).",\n "items": {\n "type": "string"\n }\n },\n "hoisted": {\n "type": "integer",\n "description": "Inline call sites normalized (or normalizable in check mode)."\n },\n "moduleScope": {\n "type": "integer",\n "description": "Call sites already evaluated once per module load (left as-is)."\n },\n "alreadyHoisted": {\n "type": "integer",\n "description": "Call sites already normalized by a previous run."\n },\n "errors": {\n "type": "integer",\n "description": "No-runtime-capture violations found."\n },\n "artifact": {\n "type": "string",\n "description": "The static prepared-plan artifact path (--aot only)."\n },\n "plans": {\n "type": "integer",\n "description": "Prepared plans compiled into the artifact (--aot only)."\n },\n "drift": {\n "type": "object",\n "description": "Artifact drift detail (--aot check mode only; present when status is drift).",\n "properties": {\n "missing": {\n "type": "boolean",\n "description": "The artifact module does not exist."\n },\n "added": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "removed": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "changed": {\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n }\n },\n "diagnostics": {\n "type": "array",\n "description": "Per-position lint diagnostics (errors and notes).",\n "items": {\n "type": "object",\n "required": [\n "code",\n "category",\n "message",\n "file",\n "line",\n "column"\n ],\n "properties": {\n "code": {\n "type": "string"\n },\n "category": {\n "type": "string",\n "enum": [\n "error",\n "note"\n ]\n },\n "message": {\n "type": "string"\n },\n "file": {\n "type": "string"\n },\n "line": {\n "type": "integer"\n },\n "column": {\n "type": "integer"\n }\n }\n }\n }\n }\n }\n }\n }\n}';
|
|
574
609
|
|
|
575
610
|
// src/generated/program.ts
|
|
576
611
|
function createProgram(handlers2, version) {
|
|
@@ -605,6 +640,16 @@ function createProgram(handlers2, version) {
|
|
|
605
640
|
}
|
|
606
641
|
await handlers2.generateDocs(opts, globalOpts);
|
|
607
642
|
});
|
|
643
|
+
const __cmd_transform = program.command("transform");
|
|
644
|
+
__cmd_transform.command("prepared").description("Lint and hoist-normalize `DDBModel.prepare` call sites (compile-time prepared statements, issue #206).").option("-s, --src <path>", "A TypeScript source file, or a directory scanned recursively for .ts/.tsx/.mts/.cts files (node_modules, dist, hidden directories, and .d.ts files are skipped).").option("-w, --write", "Rewrite verified inline call sites in place (hoist normalization). Without it the command only checks and reports.").option("-a, --aot <artifact>", "Static prepared-plan artifact module path (issue #208). Enables build-time (AOT) compilation of every verified prepare body: with --write the artifact module is (re)generated and call sites are rewritten to load their static plan; without --write the pass recompiles and compares (drift check \u2014 a missing or differing artifact exits 1).").action(async (opts, cmd) => {
|
|
645
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
646
|
+
if (globalOpts.introspect) {
|
|
647
|
+
const policy = deriveCommandPolicy("transform.prepared", opts);
|
|
648
|
+
console.log(JSON.stringify(policy, null, 2));
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
await handlers2.transformPrepared(opts, globalOpts);
|
|
652
|
+
});
|
|
608
653
|
program.command("extract").description("Extract contract specification for this CLI tool.").argument("[commands...]", "Command IDs to extract. Use dot notation.").option("-a, --all", "Extract all commands.", false).option("--include-meta", "Include extraction metadata.", true).option("-F, --format <format>", "Output format (yaml or json).", "yaml").action(async (commands, opts, cmd) => {
|
|
609
654
|
if (commands.length === 0 && !opts.all) {
|
|
610
655
|
process.stderr.write(JSON.stringify({ code: "INVALID_ARGS", message: "Specify command IDs or use --all" }) + "\n");
|
|
@@ -622,7 +667,7 @@ function createProgram(handlers2, version) {
|
|
|
622
667
|
type: "cli-contracts/extract",
|
|
623
668
|
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
624
669
|
specVersion: doc.cli_contracts ?? "0.1.0",
|
|
625
|
-
commands: ["graphddb.generate.python", "graphddb.generate.cloudformation", "graphddb.generate.docs"]
|
|
670
|
+
commands: ["graphddb.generate.python", "graphddb.generate.cloudformation", "graphddb.generate.docs", "graphddb.transform.prepared"]
|
|
626
671
|
};
|
|
627
672
|
}
|
|
628
673
|
Object.assign(out, doc);
|
|
@@ -639,7 +684,7 @@ function createProgram(handlers2, version) {
|
|
|
639
684
|
yamlLines.push("extractedAt: " + (/* @__PURE__ */ new Date()).toISOString());
|
|
640
685
|
yamlLines.push("spec_version: " + (doc.cli_contracts ?? "0.1.0"));
|
|
641
686
|
yamlLines.push("commands:");
|
|
642
|
-
for (const id of ["graphddb.generate.python", "graphddb.generate.cloudformation", "graphddb.generate.docs"]) {
|
|
687
|
+
for (const id of ["graphddb.generate.python", "graphddb.generate.cloudformation", "graphddb.generate.docs", "graphddb.transform.prepared"]) {
|
|
643
688
|
yamlLines.push(" - " + id);
|
|
644
689
|
}
|
|
645
690
|
}
|
|
@@ -679,7 +724,7 @@ function createProgram(handlers2, version) {
|
|
|
679
724
|
}
|
|
680
725
|
|
|
681
726
|
// src/cli/handlers.ts
|
|
682
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
727
|
+
import { mkdirSync, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "fs";
|
|
683
728
|
import { dirname, relative } from "path";
|
|
684
729
|
import { pathToFileURL } from "url";
|
|
685
730
|
import { isAbsolute, resolve as resolve2 } from "path";
|
|
@@ -744,11 +789,6 @@ function collectResultTypes(manifest, entity, select, typeName, out, seen) {
|
|
|
744
789
|
const value = select[field];
|
|
745
790
|
const relation = meta?.relations[field];
|
|
746
791
|
if (relation) {
|
|
747
|
-
if (relation.type === "refs") {
|
|
748
|
-
throw new Error(
|
|
749
|
-
`Relation '${field}' on '${relation.target}' is a 'refs' relation (issue #197): it is resolvable only through the TS in-process runtime, not the Python runtime (its parent-list BatchGet fan-out has no static-spec / Python representation). Remove '${field}' from selects compiled to generated Python types.`
|
|
750
|
-
);
|
|
751
|
-
}
|
|
752
792
|
const spec = normalizeSelectSpec(value);
|
|
753
793
|
const childSelect = spec.select ?? {};
|
|
754
794
|
const itemTypeName = `${typeName}${toPascalCase(field)}Item`;
|
|
@@ -760,7 +800,7 @@ function collectResultTypes(manifest, entity, select, typeName, out, seen) {
|
|
|
760
800
|
out,
|
|
761
801
|
seen
|
|
762
802
|
);
|
|
763
|
-
if (relation.type === "hasMany") {
|
|
803
|
+
if (relation.type === "hasMany" || relation.type === "refs") {
|
|
764
804
|
const connName = `${itemTypeName}Connection`;
|
|
765
805
|
if (!seen.has(connName)) {
|
|
766
806
|
seen.add(connName);
|
|
@@ -2545,6 +2585,176 @@ async function loadOptionalMap(file, exportName) {
|
|
|
2545
2585
|
if (value === void 0 || value === null) return {};
|
|
2546
2586
|
return value;
|
|
2547
2587
|
}
|
|
2588
|
+
async function transformPreparedAot(files, aotPath, write, transform) {
|
|
2589
|
+
const { unlinkSync } = await import("fs");
|
|
2590
|
+
const { basename, join, relative: rel, sep } = await import("path");
|
|
2591
|
+
const posix = (p) => p.split(sep).join("/");
|
|
2592
|
+
const artifactAbs = isAbsolute(aotPath) ? aotPath : resolve2(process.cwd(), aotPath);
|
|
2593
|
+
const spec = await import("./spec/index.js");
|
|
2594
|
+
try {
|
|
2595
|
+
const diagnostics = [];
|
|
2596
|
+
const scanned = [];
|
|
2597
|
+
const work = [];
|
|
2598
|
+
let errors = 0;
|
|
2599
|
+
for (const file of files) {
|
|
2600
|
+
if (file === artifactAbs) continue;
|
|
2601
|
+
const relPath = posix(rel(process.cwd(), file) || file);
|
|
2602
|
+
scanned.push(relPath);
|
|
2603
|
+
const source = readFileSync2(file, "utf8");
|
|
2604
|
+
if (!source.includes(".prepare(") && !source.includes(`${transform.AOT_LOADER_LOCAL}(`)) {
|
|
2605
|
+
continue;
|
|
2606
|
+
}
|
|
2607
|
+
let artifactImport = posix(rel(dirname(file), artifactAbs)).replace(
|
|
2608
|
+
/\.[mc]?ts$/,
|
|
2609
|
+
".js"
|
|
2610
|
+
);
|
|
2611
|
+
if (!artifactImport.startsWith(".")) artifactImport = `./${artifactImport}`;
|
|
2612
|
+
const result = transform.transformPreparedSourceAot(relPath, source, {
|
|
2613
|
+
mode: write ? "transform" : "check",
|
|
2614
|
+
planIdPrefix: relPath,
|
|
2615
|
+
artifactImport
|
|
2616
|
+
});
|
|
2617
|
+
diagnostics.push(...result.diagnostics);
|
|
2618
|
+
errors += result.errorCount;
|
|
2619
|
+
if (result.sites.length > 0) work.push({ file, result });
|
|
2620
|
+
}
|
|
2621
|
+
if (errors > 0) {
|
|
2622
|
+
process.stdout.write(
|
|
2623
|
+
JSON.stringify({
|
|
2624
|
+
status: "violations",
|
|
2625
|
+
files: scanned,
|
|
2626
|
+
hoisted: 0,
|
|
2627
|
+
moduleScope: 0,
|
|
2628
|
+
alreadyHoisted: 0,
|
|
2629
|
+
errors,
|
|
2630
|
+
diagnostics,
|
|
2631
|
+
artifact: aotPath
|
|
2632
|
+
}) + "\n"
|
|
2633
|
+
);
|
|
2634
|
+
process.exit(1);
|
|
2635
|
+
}
|
|
2636
|
+
await ensureTsx();
|
|
2637
|
+
if (MetadataRegistry.linter === null) {
|
|
2638
|
+
MetadataRegistry.linter = createDefaultLinter();
|
|
2639
|
+
}
|
|
2640
|
+
const plans = {};
|
|
2641
|
+
for (const { file, result } of work) {
|
|
2642
|
+
if (result.shimSource === void 0) continue;
|
|
2643
|
+
const shimPath = join(
|
|
2644
|
+
dirname(file),
|
|
2645
|
+
`.${basename(file).replace(/\.[^.]+$/, "")}.gddb-aot-${process.pid}.mts`
|
|
2646
|
+
);
|
|
2647
|
+
writeFileSync(shimPath, result.shimSource, "utf8");
|
|
2648
|
+
try {
|
|
2649
|
+
const mod = await import(toUrl(shimPath));
|
|
2650
|
+
const bodies = mod[transform.AOT_SHIM_EXPORT];
|
|
2651
|
+
for (const site of result.sites) {
|
|
2652
|
+
const body = bodies?.[site.index];
|
|
2653
|
+
if (typeof body !== "function") {
|
|
2654
|
+
throw new Error(
|
|
2655
|
+
`internal: evaluation shim for '${result.fileName}' carries no body #${site.index}.`
|
|
2656
|
+
);
|
|
2657
|
+
}
|
|
2658
|
+
plans[site.planId] = spec.compilePreparedPlan(body, site.planId);
|
|
2659
|
+
}
|
|
2660
|
+
} finally {
|
|
2661
|
+
try {
|
|
2662
|
+
unlinkSync(shimPath);
|
|
2663
|
+
} catch {
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
const doc = spec.buildPreparedPlanDocument(plans);
|
|
2668
|
+
const artifactText = "/* eslint-disable */\n// Generated by `graphddb transform prepared --aot` (issue #208). DO NOT EDIT.\n// Static prepared plans: build-time-compiled `DDBModel.prepare` bodies,\n// loaded by `loadPreparedPlan` (a stale plan \u2014 model declarations changed\n// since generation \u2014 is loudly rejected at load; regenerate via the same\n// command, and run the no-`--write` check mode in CI to catch drift).\nimport type { PreparedPlanDocument } from 'graphddb';\n\nconst document = " + JSON.stringify(doc, null, 2) + " as unknown as PreparedPlanDocument;\n\nexport default document;\n";
|
|
2669
|
+
if (write) {
|
|
2670
|
+
const changedFiles = [];
|
|
2671
|
+
mkdirSync(dirname(artifactAbs), { recursive: true });
|
|
2672
|
+
const existing = (() => {
|
|
2673
|
+
try {
|
|
2674
|
+
return readFileSync2(artifactAbs, "utf8");
|
|
2675
|
+
} catch {
|
|
2676
|
+
return void 0;
|
|
2677
|
+
}
|
|
2678
|
+
})();
|
|
2679
|
+
if (existing !== artifactText) {
|
|
2680
|
+
writeFileSync(artifactAbs, artifactText, "utf8");
|
|
2681
|
+
changedFiles.push(aotPath);
|
|
2682
|
+
}
|
|
2683
|
+
for (const { file, result } of work) {
|
|
2684
|
+
if (result.changed) {
|
|
2685
|
+
writeFileSync(file, result.text, "utf8");
|
|
2686
|
+
changedFiles.push(posix(rel(process.cwd(), file) || file));
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
process.stdout.write(
|
|
2690
|
+
JSON.stringify({
|
|
2691
|
+
status: "ok",
|
|
2692
|
+
files: scanned,
|
|
2693
|
+
changedFiles,
|
|
2694
|
+
hoisted: 0,
|
|
2695
|
+
moduleScope: 0,
|
|
2696
|
+
alreadyHoisted: 0,
|
|
2697
|
+
errors: 0,
|
|
2698
|
+
diagnostics,
|
|
2699
|
+
artifact: aotPath,
|
|
2700
|
+
plans: Object.keys(plans).length
|
|
2701
|
+
}) + "\n"
|
|
2702
|
+
);
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
let existingDoc;
|
|
2706
|
+
try {
|
|
2707
|
+
statSync(artifactAbs);
|
|
2708
|
+
const mod = await import(toUrl(artifactAbs));
|
|
2709
|
+
existingDoc = mod.default;
|
|
2710
|
+
} catch {
|
|
2711
|
+
existingDoc = void 0;
|
|
2712
|
+
}
|
|
2713
|
+
if (existingDoc === void 0) {
|
|
2714
|
+
process.stdout.write(
|
|
2715
|
+
JSON.stringify({
|
|
2716
|
+
status: "drift",
|
|
2717
|
+
files: scanned,
|
|
2718
|
+
hoisted: 0,
|
|
2719
|
+
moduleScope: 0,
|
|
2720
|
+
alreadyHoisted: 0,
|
|
2721
|
+
errors: 0,
|
|
2722
|
+
diagnostics,
|
|
2723
|
+
artifact: aotPath,
|
|
2724
|
+
plans: Object.keys(plans).length,
|
|
2725
|
+
drift: { missing: true, added: Object.keys(plans).sort(), removed: [], changed: [] }
|
|
2726
|
+
}) + "\n"
|
|
2727
|
+
);
|
|
2728
|
+
process.exit(1);
|
|
2729
|
+
}
|
|
2730
|
+
const oldPlans = existingDoc.plans ?? {};
|
|
2731
|
+
const added = Object.keys(plans).filter((id) => !(id in oldPlans)).sort();
|
|
2732
|
+
const removed = Object.keys(oldPlans).filter((id) => !(id in plans)).sort();
|
|
2733
|
+
const changed = Object.keys(plans).filter(
|
|
2734
|
+
(id) => id in oldPlans && spec.canonicalJson(plans[id]) !== spec.canonicalJson(oldPlans[id])
|
|
2735
|
+
).sort();
|
|
2736
|
+
const versionSkew = existingDoc.formatVersion !== doc.formatVersion || existingDoc.specVersion !== doc.specVersion;
|
|
2737
|
+
const hasDrift = versionSkew || added.length > 0 || removed.length > 0 || changed.length > 0;
|
|
2738
|
+
process.stdout.write(
|
|
2739
|
+
JSON.stringify({
|
|
2740
|
+
status: hasDrift ? "drift" : "ok",
|
|
2741
|
+
files: scanned,
|
|
2742
|
+
hoisted: 0,
|
|
2743
|
+
moduleScope: 0,
|
|
2744
|
+
alreadyHoisted: 0,
|
|
2745
|
+
errors: 0,
|
|
2746
|
+
diagnostics,
|
|
2747
|
+
artifact: aotPath,
|
|
2748
|
+
plans: Object.keys(plans).length,
|
|
2749
|
+
...hasDrift ? { drift: { missing: false, added, removed, changed } } : {}
|
|
2750
|
+
}) + "\n"
|
|
2751
|
+
);
|
|
2752
|
+
if (hasDrift) process.exit(1);
|
|
2753
|
+
} catch (err) {
|
|
2754
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2755
|
+
fail("TRANSFORM_FAILED", message, 1);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2548
2758
|
var handlers = {
|
|
2549
2759
|
async generatePython(options) {
|
|
2550
2760
|
const {
|
|
@@ -2921,6 +3131,120 @@ var handlers = {
|
|
|
2921
3131
|
const message = err instanceof Error ? err.message : String(err);
|
|
2922
3132
|
fail("GENERATION_FAILED", message, 1);
|
|
2923
3133
|
}
|
|
3134
|
+
},
|
|
3135
|
+
/**
|
|
3136
|
+
* `transform prepared` (issue #206, design #203 — phase 2).
|
|
3137
|
+
*
|
|
3138
|
+
* The compile-time prepared-statement pass: statically enforce the
|
|
3139
|
+
* no-runtime-capture rule on every `DDBModel.prepare` body (the build lint —
|
|
3140
|
+
* violations exit 1, loudly, with positions), and normalize verified INLINE
|
|
3141
|
+
* call sites to a module-scope lazy prepared slot (`--write`), so the plan
|
|
3142
|
+
* compiles once per module and each call is a plain `.execute` — zero per-op
|
|
3143
|
+
* planning / hashing, placement-independent. Check mode (no `--write`)
|
|
3144
|
+
* reports hoistable inline call sites as notes so a silent runtime-fallback
|
|
3145
|
+
* build stays visible.
|
|
3146
|
+
*
|
|
3147
|
+
* Purely syntactic (TS compiler API over the source text — no module
|
|
3148
|
+
* loading, no type checker). The `typescript` optional dependency is loaded
|
|
3149
|
+
* lazily via the `graphddb/transform` module, mirroring the JSDoc codegen
|
|
3150
|
+
* pass; files with violations are never rewritten.
|
|
3151
|
+
*/
|
|
3152
|
+
async transformPrepared(options) {
|
|
3153
|
+
const { src, write, aot } = options;
|
|
3154
|
+
if (!src) fail("INVALID_ARGS", "Missing required option --src.", 2);
|
|
3155
|
+
const srcAbs = isAbsolute(src) ? src : resolve2(process.cwd(), src);
|
|
3156
|
+
let rootIsFile;
|
|
3157
|
+
let rootIsDir;
|
|
3158
|
+
try {
|
|
3159
|
+
const st = statSync(srcAbs);
|
|
3160
|
+
rootIsFile = st.isFile();
|
|
3161
|
+
rootIsDir = st.isDirectory();
|
|
3162
|
+
} catch {
|
|
3163
|
+
fail("INVALID_ARGS", `--src '${src}' does not exist.`, 2);
|
|
3164
|
+
}
|
|
3165
|
+
const files = [];
|
|
3166
|
+
const isSourceFile = (name) => /\.(?:ts|tsx|mts|cts)$/.test(name) && !/\.d\.[mc]?ts$/.test(name);
|
|
3167
|
+
if (rootIsFile) {
|
|
3168
|
+
files.push(srcAbs);
|
|
3169
|
+
} else if (rootIsDir) {
|
|
3170
|
+
const walk = (dir) => {
|
|
3171
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3172
|
+
if (entry.name.startsWith(".")) continue;
|
|
3173
|
+
const full = resolve2(dir, entry.name);
|
|
3174
|
+
if (entry.isDirectory()) {
|
|
3175
|
+
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
3176
|
+
walk(full);
|
|
3177
|
+
} else if (entry.isFile() && isSourceFile(entry.name)) {
|
|
3178
|
+
files.push(full);
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
};
|
|
3182
|
+
walk(srcAbs);
|
|
3183
|
+
files.sort();
|
|
3184
|
+
} else {
|
|
3185
|
+
fail("INVALID_ARGS", `--src '${src}' is neither a file nor a directory.`, 2);
|
|
3186
|
+
}
|
|
3187
|
+
let transform;
|
|
3188
|
+
try {
|
|
3189
|
+
transform = await import("./transform/index.js");
|
|
3190
|
+
} catch {
|
|
3191
|
+
fail(
|
|
3192
|
+
"INVALID_ARGS",
|
|
3193
|
+
"The prepared-statement transform requires the `typescript` package. Install it as a devDependency (npm i -D typescript).",
|
|
3194
|
+
2
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
if (aot) {
|
|
3198
|
+
await transformPreparedAot(files, aot, Boolean(write), transform);
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
try {
|
|
3202
|
+
const mode = write ? "transform" : "lint";
|
|
3203
|
+
const diagnostics = [];
|
|
3204
|
+
const changedFiles = [];
|
|
3205
|
+
const scanned = [];
|
|
3206
|
+
let hoisted = 0;
|
|
3207
|
+
let moduleScope = 0;
|
|
3208
|
+
let alreadyHoisted = 0;
|
|
3209
|
+
let errors = 0;
|
|
3210
|
+
for (const file of files) {
|
|
3211
|
+
const rel = relative(process.cwd(), file) || file;
|
|
3212
|
+
scanned.push(rel);
|
|
3213
|
+
const source = readFileSync2(file, "utf8");
|
|
3214
|
+
if (!source.includes(".prepare(")) continue;
|
|
3215
|
+
const result = transform.transformPreparedSource(rel, source, { mode });
|
|
3216
|
+
diagnostics.push(...result.diagnostics);
|
|
3217
|
+
errors += result.errorCount;
|
|
3218
|
+
moduleScope += result.moduleScopeCount;
|
|
3219
|
+
alreadyHoisted += result.alreadyHoistedCount;
|
|
3220
|
+
if (mode === "lint") {
|
|
3221
|
+
hoisted += result.diagnostics.filter((d) => d.code === "hoistable").length;
|
|
3222
|
+
} else {
|
|
3223
|
+
hoisted += result.hoistedCount;
|
|
3224
|
+
if (result.changed) {
|
|
3225
|
+
writeFileSync(file, result.text, "utf8");
|
|
3226
|
+
changedFiles.push(rel);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
const status = errors > 0 ? "violations" : "ok";
|
|
3231
|
+
process.stdout.write(
|
|
3232
|
+
JSON.stringify({
|
|
3233
|
+
status,
|
|
3234
|
+
files: scanned,
|
|
3235
|
+
...write ? { changedFiles } : {},
|
|
3236
|
+
hoisted,
|
|
3237
|
+
moduleScope,
|
|
3238
|
+
alreadyHoisted,
|
|
3239
|
+
errors,
|
|
3240
|
+
diagnostics
|
|
3241
|
+
}) + "\n"
|
|
3242
|
+
);
|
|
3243
|
+
if (errors > 0) process.exit(1);
|
|
3244
|
+
} catch (err) {
|
|
3245
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3246
|
+
fail("TRANSFORM_FAILED", message, 1);
|
|
3247
|
+
}
|
|
2924
3248
|
}
|
|
2925
3249
|
};
|
|
2926
3250
|
|