graphddb 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/cdc/index.d.ts +37 -0
- package/dist/cdc/index.js +29 -0
- package/dist/{chunk-W3GEJPPV.js → chunk-CCIVET5K.js} +66 -1024
- package/dist/{chunk-QBXLQNXY.js → chunk-M6URQOAW.js} +4 -1
- package/dist/chunk-MMVHOUM4.js +24 -0
- package/dist/chunk-PDUVTYC5.js +992 -0
- package/dist/{chunk-H5TUW2WR.js → chunk-Y7XV5QL2.js} +3556 -3547
- package/dist/cli.js +917 -19
- package/dist/from-change-DQK2Jm9R.d.ts +327 -0
- package/dist/index-CtDBo8Se.d.ts +690 -0
- package/dist/index.d.ts +7 -1112
- package/dist/index.js +22 -39
- package/dist/linter/index.d.ts +126 -0
- package/dist/linter/index.js +36 -0
- package/dist/{types-m1Ect6hG.d.ts → maintenance-view-adapter-D5t9taTE.d.ts} +234 -182
- package/dist/registry-BD_5Rm5C.d.ts +76 -0
- package/dist/relation-depth-DLkhG0xX.d.ts +36 -0
- package/dist/spec/index.d.ts +4 -0
- package/dist/spec/index.js +54 -0
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +5 -4
- package/docs/doc-sample.md +263 -0
- package/docs/docs-generation.md +183 -0
- package/package.json +46 -3
- package/dist/chunk-MCKGQKYU.js +0 -15
- package/dist/typescript-ZUQEBJRV.js +0 -210764
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildBridgeBundle,
|
|
4
|
-
buildManifest
|
|
5
|
-
|
|
4
|
+
buildManifest,
|
|
5
|
+
buildOperations
|
|
6
|
+
} from "./chunk-Y7XV5QL2.js";
|
|
6
7
|
import {
|
|
7
8
|
MetadataRegistry,
|
|
8
9
|
normalizeSelectSpec
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import
|
|
10
|
+
} from "./chunk-CCIVET5K.js";
|
|
11
|
+
import {
|
|
12
|
+
createDefaultLinter
|
|
13
|
+
} from "./chunk-PDUVTYC5.js";
|
|
11
14
|
|
|
12
15
|
// src/cli.ts
|
|
13
16
|
import { createRequire } from "module";
|
|
@@ -441,6 +444,105 @@ var commandDefinitions = {
|
|
|
441
444
|
}
|
|
442
445
|
}
|
|
443
446
|
]
|
|
447
|
+
},
|
|
448
|
+
"generate.docs": {
|
|
449
|
+
"effects": {
|
|
450
|
+
"risk_level": "low",
|
|
451
|
+
"reads": [
|
|
452
|
+
"ts-definitions"
|
|
453
|
+
],
|
|
454
|
+
"writes": [
|
|
455
|
+
"generated-code"
|
|
456
|
+
]
|
|
457
|
+
},
|
|
458
|
+
"options": [
|
|
459
|
+
{
|
|
460
|
+
"name": "entry",
|
|
461
|
+
"schema": {
|
|
462
|
+
"type": "string"
|
|
463
|
+
},
|
|
464
|
+
"file": {
|
|
465
|
+
"mode": "read",
|
|
466
|
+
"exists": true,
|
|
467
|
+
"media_type": "application/typescript",
|
|
468
|
+
"encoding": "utf-8"
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
"name": "queries",
|
|
473
|
+
"schema": {
|
|
474
|
+
"type": "string"
|
|
475
|
+
},
|
|
476
|
+
"file": {
|
|
477
|
+
"mode": "read",
|
|
478
|
+
"exists": true,
|
|
479
|
+
"media_type": "application/typescript",
|
|
480
|
+
"encoding": "utf-8"
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
"name": "out",
|
|
485
|
+
"schema": {
|
|
486
|
+
"type": "string"
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"name": "format",
|
|
491
|
+
"schema": {
|
|
492
|
+
"type": "string",
|
|
493
|
+
"enum": [
|
|
494
|
+
"markdown"
|
|
495
|
+
],
|
|
496
|
+
"default": "markdown"
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
"name": "split",
|
|
501
|
+
"schema": {
|
|
502
|
+
"type": "string",
|
|
503
|
+
"enum": [
|
|
504
|
+
"none",
|
|
505
|
+
"entity"
|
|
506
|
+
],
|
|
507
|
+
"default": "none"
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
"name": "template-dir",
|
|
512
|
+
"schema": {
|
|
513
|
+
"type": "string"
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
"name": "helpers",
|
|
518
|
+
"schema": {
|
|
519
|
+
"type": "string"
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"name": "billing-mode",
|
|
524
|
+
"schema": {
|
|
525
|
+
"type": "string",
|
|
526
|
+
"enum": [
|
|
527
|
+
"PAY_PER_REQUEST",
|
|
528
|
+
"PROVISIONED"
|
|
529
|
+
],
|
|
530
|
+
"default": "PAY_PER_REQUEST"
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
"name": "stream",
|
|
535
|
+
"schema": {
|
|
536
|
+
"type": "string",
|
|
537
|
+
"enum": [
|
|
538
|
+
"auto",
|
|
539
|
+
"on",
|
|
540
|
+
"off"
|
|
541
|
+
],
|
|
542
|
+
"default": "auto"
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
]
|
|
444
546
|
}
|
|
445
547
|
};
|
|
446
548
|
function deriveCommandPolicy(command_id, optionValues) {
|
|
@@ -467,8 +569,8 @@ function deriveCommandPolicy(command_id, optionValues) {
|
|
|
467
569
|
}
|
|
468
570
|
|
|
469
571
|
// src/generated/contract.ts
|
|
470
|
-
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\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";
|
|
471
|
-
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 }\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}';
|
|
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}';
|
|
472
574
|
|
|
473
575
|
// src/generated/program.ts
|
|
474
576
|
function createProgram(handlers2, version) {
|
|
@@ -494,6 +596,15 @@ function createProgram(handlers2, version) {
|
|
|
494
596
|
}
|
|
495
597
|
await handlers2.generateCloudformation(opts, globalOpts);
|
|
496
598
|
});
|
|
599
|
+
__cmd_generate.command("docs").description("Generate model-specification documentation (Markdown + Mermaid) from TypeScript GraphDDB definitions.").option("-e, --entry <file>", "Entry module that registers the entity models (side-effecting import). May also export queries / commands.").option("-q, --queries <file>", "Module exporting `queries` / `commands` (defineQueries / defineCommands maps) used for the Access Patterns / Mutation Contracts sections. Defaults to the entry module when omitted.").option("-o, --out <path>", "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.").option("-f, --format <format>", "Output format. Only markdown is supported today.", "markdown").option("--split <mode>", "Output granularity. none (default) emits a single index.md; entity is reserved for a future per-entity split.", "none").option("--template-dir <dir>", "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.").option("--helpers <file>", "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).").option("--billing-mode <mode>", "DynamoDB billing mode used in the Table Overview (shared with generate cloudformation). PAY_PER_REQUEST (default) or PROVISIONED.", "PAY_PER_REQUEST").option("--stream <mode>", "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.", "auto").action(async (opts, cmd) => {
|
|
600
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
601
|
+
if (globalOpts.introspect) {
|
|
602
|
+
const policy = deriveCommandPolicy("generate.docs", opts);
|
|
603
|
+
console.log(JSON.stringify(policy, null, 2));
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
await handlers2.generateDocs(opts, globalOpts);
|
|
607
|
+
});
|
|
497
608
|
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) => {
|
|
498
609
|
if (commands.length === 0 && !opts.all) {
|
|
499
610
|
process.stderr.write(JSON.stringify({ code: "INVALID_ARGS", message: "Specify command IDs or use --all" }) + "\n");
|
|
@@ -511,7 +622,7 @@ function createProgram(handlers2, version) {
|
|
|
511
622
|
type: "cli-contracts/extract",
|
|
512
623
|
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
513
624
|
specVersion: doc.cli_contracts ?? "0.1.0",
|
|
514
|
-
commands: ["graphddb.generate.python", "graphddb.generate.cloudformation"]
|
|
625
|
+
commands: ["graphddb.generate.python", "graphddb.generate.cloudformation", "graphddb.generate.docs"]
|
|
515
626
|
};
|
|
516
627
|
}
|
|
517
628
|
Object.assign(out, doc);
|
|
@@ -528,7 +639,7 @@ function createProgram(handlers2, version) {
|
|
|
528
639
|
yamlLines.push("extractedAt: " + (/* @__PURE__ */ new Date()).toISOString());
|
|
529
640
|
yamlLines.push("spec_version: " + (doc.cli_contracts ?? "0.1.0"));
|
|
530
641
|
yamlLines.push("commands:");
|
|
531
|
-
for (const id of ["graphddb.generate.python", "graphddb.generate.cloudformation"]) {
|
|
642
|
+
for (const id of ["graphddb.generate.python", "graphddb.generate.cloudformation", "graphddb.generate.docs"]) {
|
|
532
643
|
yamlLines.push(" - " + id);
|
|
533
644
|
}
|
|
534
645
|
}
|
|
@@ -571,7 +682,7 @@ function createProgram(handlers2, version) {
|
|
|
571
682
|
import { mkdirSync, writeFileSync } from "fs";
|
|
572
683
|
import { dirname, relative } from "path";
|
|
573
684
|
import { pathToFileURL } from "url";
|
|
574
|
-
import { isAbsolute, resolve } from "path";
|
|
685
|
+
import { isAbsolute, resolve as resolve2 } from "path";
|
|
575
686
|
|
|
576
687
|
// src/codegen/python.ts
|
|
577
688
|
var PY_HEADER = "# DO NOT EDIT. Generated by GraphDDB.";
|
|
@@ -1463,6 +1574,662 @@ function generateCdcRegistry(options = {}) {
|
|
|
1463
1574
|
return lines.join("\n");
|
|
1464
1575
|
}
|
|
1465
1576
|
|
|
1577
|
+
// src/codegen/docs-model.ts
|
|
1578
|
+
var AS_CONFIGURED = "as configured at deployment";
|
|
1579
|
+
function fieldTypeLabel(entity, field) {
|
|
1580
|
+
const f = entity.fields[field];
|
|
1581
|
+
if (!f) return "";
|
|
1582
|
+
if (f.format === "datetime") return "datetime";
|
|
1583
|
+
if (f.format === "date") return "date";
|
|
1584
|
+
return f.type;
|
|
1585
|
+
}
|
|
1586
|
+
function collectGsiColumns(manifest) {
|
|
1587
|
+
const names = /* @__PURE__ */ new Set();
|
|
1588
|
+
for (const entity of Object.values(manifest.entities)) {
|
|
1589
|
+
for (const gsi of entity.gsis) names.add(gsi.indexName);
|
|
1590
|
+
}
|
|
1591
|
+
return [...names].sort();
|
|
1592
|
+
}
|
|
1593
|
+
function gsiByName(entity, indexName) {
|
|
1594
|
+
return entity.gsis.find((g) => g.indexName === indexName);
|
|
1595
|
+
}
|
|
1596
|
+
function propertyRole(entity, property) {
|
|
1597
|
+
const key = entity.key;
|
|
1598
|
+
if (key) {
|
|
1599
|
+
if (referencesField(key.pkTemplate, property)) return "PK";
|
|
1600
|
+
if (key.skTemplate && referencesField(key.skTemplate, property)) return "SK";
|
|
1601
|
+
}
|
|
1602
|
+
for (const gsi of entity.gsis) {
|
|
1603
|
+
if (referencesField(gsi.pkTemplate, property)) return `${gsi.indexName} PK`;
|
|
1604
|
+
if (gsi.skTemplate && referencesField(gsi.skTemplate, property)) {
|
|
1605
|
+
return `${gsi.indexName} SK`;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
if (entity.fields[property]) return "\u2713";
|
|
1609
|
+
return "";
|
|
1610
|
+
}
|
|
1611
|
+
function referencesField(template, field) {
|
|
1612
|
+
return template.includes(`{${field}}`);
|
|
1613
|
+
}
|
|
1614
|
+
function keyLabel(prefix, template) {
|
|
1615
|
+
return template ? `${prefix} = ${template}` : "";
|
|
1616
|
+
}
|
|
1617
|
+
function rootIndex(spec) {
|
|
1618
|
+
const root = spec.operations[0];
|
|
1619
|
+
if (!root) return "";
|
|
1620
|
+
return root.indexName ?? "PK";
|
|
1621
|
+
}
|
|
1622
|
+
function isRelation(spec) {
|
|
1623
|
+
return spec.operations.length > 1;
|
|
1624
|
+
}
|
|
1625
|
+
function usesBatchGet(spec) {
|
|
1626
|
+
return spec.operations.some((op) => op.type === "BatchGetItem");
|
|
1627
|
+
}
|
|
1628
|
+
function usesCursor(spec) {
|
|
1629
|
+
return (spec.cardinality ?? "many") === "many";
|
|
1630
|
+
}
|
|
1631
|
+
function inputShape(spec) {
|
|
1632
|
+
const parts = [];
|
|
1633
|
+
for (const name of Object.keys(spec.params).sort()) {
|
|
1634
|
+
const p = spec.params[name];
|
|
1635
|
+
const opt = p.required ? "" : "?";
|
|
1636
|
+
parts.push(`${name}${opt}: ${p.type}`);
|
|
1637
|
+
}
|
|
1638
|
+
if ("cardinality" in spec && (spec.cardinality ?? "many") === "many") {
|
|
1639
|
+
parts.push("limit?: number", "cursor?: string");
|
|
1640
|
+
}
|
|
1641
|
+
return `{ ${parts.join(", ")} }`;
|
|
1642
|
+
}
|
|
1643
|
+
function opPkTemplate(op) {
|
|
1644
|
+
if (op.indexName) {
|
|
1645
|
+
return op.keyCondition[`${op.indexName}PK`] ?? op.keyCondition["PK"] ?? op.keyCondition["pk"] ?? "";
|
|
1646
|
+
}
|
|
1647
|
+
return op.keyCondition["PK"] ?? op.keyCondition["pk"] ?? "";
|
|
1648
|
+
}
|
|
1649
|
+
function rootEntityName(spec, manifest) {
|
|
1650
|
+
const root = spec.operations[0];
|
|
1651
|
+
if (!root) return "";
|
|
1652
|
+
const pk = opPkTemplate(root);
|
|
1653
|
+
for (const [name, entity] of Object.entries(manifest.entities)) {
|
|
1654
|
+
const cand = root.indexName ? gsiByName(entity, root.indexName)?.pkTemplate : entity.key?.pkTemplate;
|
|
1655
|
+
if (cand && pk && cand === pk) return name;
|
|
1656
|
+
}
|
|
1657
|
+
return "";
|
|
1658
|
+
}
|
|
1659
|
+
function outputShape(spec, entityName) {
|
|
1660
|
+
const many = (spec.cardinality ?? "many") === "many";
|
|
1661
|
+
const base = entityName || "Result";
|
|
1662
|
+
return many ? `Page<${base}>` : base;
|
|
1663
|
+
}
|
|
1664
|
+
function buildResolution(spec, manifest) {
|
|
1665
|
+
const root = spec.operations[0];
|
|
1666
|
+
if (!root) return { resolve: "", diagram: [] };
|
|
1667
|
+
if (spec.operations.length === 1) {
|
|
1668
|
+
const keyLabelStr = rootKeyLabel(root);
|
|
1669
|
+
const proj = root.projection.length > 0 ? `Projection(${root.projection.slice(0, 4).join(", ")})` : "Projection";
|
|
1670
|
+
const resolve3 = `${keyLabelStr} \u2192 ${root.type} \u2192 ${proj}`;
|
|
1671
|
+
const inParam = Object.keys(spec.params).sort()[0] ?? "key";
|
|
1672
|
+
const outEntity = rootEntityName(spec, manifest) || "Result";
|
|
1673
|
+
const diagram2 = [
|
|
1674
|
+
{ label: inParam, edge: "" },
|
|
1675
|
+
{ label: keyLabelStr, edge: "" },
|
|
1676
|
+
{ label: root.type, edge: "" },
|
|
1677
|
+
{ label: outEntity, edge: "" }
|
|
1678
|
+
];
|
|
1679
|
+
return { resolve: resolve3, diagram: diagram2 };
|
|
1680
|
+
}
|
|
1681
|
+
const entityHops = [];
|
|
1682
|
+
const diagram = [];
|
|
1683
|
+
for (let i = 0; i < spec.operations.length; i++) {
|
|
1684
|
+
const op = spec.operations[i];
|
|
1685
|
+
const entity = opEntityName(op, manifest) || op.tableName;
|
|
1686
|
+
entityHops.push(`${op.type}(${entity}${op.indexName ? ` by ${op.indexName}` : ""})`);
|
|
1687
|
+
const edge = i < spec.operations.length - 1 ? spec.operations[i + 1].type === "BatchGetItem" ? "BatchGet" : op.indexName ?? "PK" : "";
|
|
1688
|
+
diagram.push({ label: entity, edge });
|
|
1689
|
+
}
|
|
1690
|
+
return { resolve: entityHops.join(" \u2192 "), diagram };
|
|
1691
|
+
}
|
|
1692
|
+
function rootKeyLabel(op) {
|
|
1693
|
+
const pk = opPkTemplate(op);
|
|
1694
|
+
const prefix = op.indexName ? `${op.indexName} PK` : "PK";
|
|
1695
|
+
return `${prefix} = ${pk}`;
|
|
1696
|
+
}
|
|
1697
|
+
function opEntityName(op, manifest) {
|
|
1698
|
+
const pk = opPkTemplate(op);
|
|
1699
|
+
for (const [name, entity] of Object.entries(manifest.entities)) {
|
|
1700
|
+
const cand = op.indexName ? gsiByName(entity, op.indexName)?.pkTemplate : entity.key?.pkTemplate;
|
|
1701
|
+
if (cand && pk) {
|
|
1702
|
+
if (literalPrefix(cand) === literalPrefix(pk)) return name;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
return "";
|
|
1706
|
+
}
|
|
1707
|
+
function literalPrefix(template) {
|
|
1708
|
+
const idx = template.indexOf("{");
|
|
1709
|
+
return idx === -1 ? template : template.slice(0, idx);
|
|
1710
|
+
}
|
|
1711
|
+
function commandSignature(_name, spec) {
|
|
1712
|
+
const verb = spec.type === "PutItem" ? "create" : spec.type === "UpdateItem" ? "update" : "delete";
|
|
1713
|
+
return `${spec.entity}.${verb}()`;
|
|
1714
|
+
}
|
|
1715
|
+
function buildMaintainedPaths(registry) {
|
|
1716
|
+
const rows = [];
|
|
1717
|
+
const entities = registry.getAll();
|
|
1718
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1719
|
+
for (const [cls, meta] of entities) byName.set(cls.name, meta);
|
|
1720
|
+
for (const name of [...byName.keys()].sort()) {
|
|
1721
|
+
const meta = byName.get(name);
|
|
1722
|
+
for (const rel of meta.relations) {
|
|
1723
|
+
const row = maintainedRowForRelation(name, rel);
|
|
1724
|
+
if (row) rows.push(row);
|
|
1725
|
+
}
|
|
1726
|
+
for (const agg of meta.aggregates) {
|
|
1727
|
+
const row = maintainedRowForAggregate(name, agg);
|
|
1728
|
+
if (row) rows.push(row);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
return rows;
|
|
1732
|
+
}
|
|
1733
|
+
function maintainedRowForRelation(ownerName, rel) {
|
|
1734
|
+
const pattern = rel.options?.pattern;
|
|
1735
|
+
if (!pattern) return null;
|
|
1736
|
+
const triggers = rel.options?.write?.maintainedOn ?? [];
|
|
1737
|
+
const source = sourceFromTriggers(triggers) || rel.targetFactory().name;
|
|
1738
|
+
const target = `${ownerName}.${rel.propertyName}`;
|
|
1739
|
+
const update = pattern === "counter" ? "ADD +1 / \u22121 on create / remove" : `project ${source} fields into ${ownerName}`;
|
|
1740
|
+
return { source, target, pattern, update };
|
|
1741
|
+
}
|
|
1742
|
+
function maintainedRowForAggregate(ownerName, agg) {
|
|
1743
|
+
const pattern = agg.options.pattern;
|
|
1744
|
+
if (!pattern) return null;
|
|
1745
|
+
const triggers = agg.options.write?.maintainedOn ?? [];
|
|
1746
|
+
const source = sourceFromTriggers(triggers) || agg.targetFactory().name;
|
|
1747
|
+
const target = `${ownerName}.${agg.propertyName}`;
|
|
1748
|
+
const update = pattern === "counter" ? "ADD +1 / \u22121 on create / remove" : `project ${source} into ${ownerName}`;
|
|
1749
|
+
return { source, target, pattern, update };
|
|
1750
|
+
}
|
|
1751
|
+
function sourceFromTriggers(triggers) {
|
|
1752
|
+
for (const t of triggers) {
|
|
1753
|
+
const dot = t.indexOf(".");
|
|
1754
|
+
if (dot > 0) return t.slice(0, dot);
|
|
1755
|
+
}
|
|
1756
|
+
return "";
|
|
1757
|
+
}
|
|
1758
|
+
function buildCdcEntries(registry) {
|
|
1759
|
+
const names = [];
|
|
1760
|
+
for (const [cls, meta] of registry.getAll()) {
|
|
1761
|
+
if (meta.cdcProjected) names.push(cls.name);
|
|
1762
|
+
}
|
|
1763
|
+
return names.sort().map((model) => ({ model }));
|
|
1764
|
+
}
|
|
1765
|
+
function buildCfnResources(manifest, streamsEnabled) {
|
|
1766
|
+
const rows = [
|
|
1767
|
+
{ resource: "AWS::DynamoDB::Table", description: "Physical table" }
|
|
1768
|
+
];
|
|
1769
|
+
const gsiCols = collectGsiColumns(manifest);
|
|
1770
|
+
if (gsiCols.length > 0) {
|
|
1771
|
+
rows.push({
|
|
1772
|
+
resource: "GlobalSecondaryIndexes",
|
|
1773
|
+
description: `GSI definitions (${gsiCols.join(", ")})`
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
rows.push({
|
|
1777
|
+
resource: "StreamSpecification",
|
|
1778
|
+
description: streamsEnabled === void 0 ? AS_CONFIGURED : streamsEnabled ? "NEW_AND_OLD_IMAGES" : "disabled"
|
|
1779
|
+
});
|
|
1780
|
+
const hasTtl = Object.values(manifest.entities).some((e) => e.ttlAttribute);
|
|
1781
|
+
if (hasTtl) {
|
|
1782
|
+
rows.push({
|
|
1783
|
+
resource: "TimeToLiveSpecification",
|
|
1784
|
+
description: "TTL (when a TTL attribute is declared)"
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
rows.push(
|
|
1788
|
+
{ resource: "PointInTimeRecovery", description: "PITR" },
|
|
1789
|
+
{ resource: "Auto Scaling", description: "Optional (PROVISIONED mode)" }
|
|
1790
|
+
);
|
|
1791
|
+
return rows;
|
|
1792
|
+
}
|
|
1793
|
+
function buildDocModel(manifest, operations, registry = MetadataRegistry, options = {}) {
|
|
1794
|
+
const entityNames = Object.keys(manifest.entities).sort();
|
|
1795
|
+
const table = entityNames.length > 0 ? manifest.entities[entityNames[0]].table : "";
|
|
1796
|
+
const title = options.title ?? `${table} Table Specification`;
|
|
1797
|
+
const cfnFile = options.cfnFile ?? "cloudformation.yaml";
|
|
1798
|
+
const overview = [
|
|
1799
|
+
{ item: "Table", value: `\`${table}\`` },
|
|
1800
|
+
{
|
|
1801
|
+
item: "Billing Mode",
|
|
1802
|
+
value: options.billingMode ?? "PAY_PER_REQUEST"
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
item: "Streams",
|
|
1806
|
+
value: options.streamsEnabled === void 0 ? AS_CONFIGURED : options.streamsEnabled ? "NEW_AND_OLD_IMAGES" : "Disabled"
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
item: "TTL",
|
|
1810
|
+
value: Object.values(manifest.entities).some((e) => e.ttlAttribute) ? "Enabled" : "Disabled"
|
|
1811
|
+
},
|
|
1812
|
+
{ item: "PITR", value: AS_CONFIGURED },
|
|
1813
|
+
{ item: "Table Class", value: AS_CONFIGURED },
|
|
1814
|
+
{ item: "Encryption", value: AS_CONFIGURED },
|
|
1815
|
+
{ item: "CloudFormation", value: `\`${cfnFile}\`` }
|
|
1816
|
+
];
|
|
1817
|
+
const relationEdges = [];
|
|
1818
|
+
for (const name of entityNames) {
|
|
1819
|
+
const entity = manifest.entities[name];
|
|
1820
|
+
for (const propName of Object.keys(entity.relations).sort()) {
|
|
1821
|
+
const rel = entity.relations[propName];
|
|
1822
|
+
relationEdges.push({
|
|
1823
|
+
source: name,
|
|
1824
|
+
target: rel.target,
|
|
1825
|
+
label: rel.type
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
const gsiColumns = collectGsiColumns(manifest);
|
|
1830
|
+
const keySchema = entityNames.map((name) => {
|
|
1831
|
+
const entity = manifest.entities[name];
|
|
1832
|
+
return {
|
|
1833
|
+
model: name,
|
|
1834
|
+
pk: entity.key?.pkTemplate ?? "",
|
|
1835
|
+
sk: entity.key?.skTemplate ?? "",
|
|
1836
|
+
gsis: gsiColumns.map((col) => {
|
|
1837
|
+
const g = gsiByName(entity, col);
|
|
1838
|
+
return { pk: g?.pkTemplate ?? "", sk: g?.skTemplate ?? "" };
|
|
1839
|
+
})
|
|
1840
|
+
};
|
|
1841
|
+
});
|
|
1842
|
+
const propertyOrder = [];
|
|
1843
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1844
|
+
for (const name of entityNames) {
|
|
1845
|
+
const entity = manifest.entities[name];
|
|
1846
|
+
for (const field of Object.keys(entity.fields).sort()) {
|
|
1847
|
+
if (!seen.has(field)) {
|
|
1848
|
+
seen.add(field);
|
|
1849
|
+
propertyOrder.push(field);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
const propertyMatrix = propertyOrder.map((property) => {
|
|
1854
|
+
let type = "";
|
|
1855
|
+
let description = "";
|
|
1856
|
+
for (const name of entityNames) {
|
|
1857
|
+
const entity = manifest.entities[name];
|
|
1858
|
+
const field = entity.fields[property];
|
|
1859
|
+
if (!field) continue;
|
|
1860
|
+
if (!type) type = fieldTypeLabel(entity, property);
|
|
1861
|
+
if (!description && field.description) description = field.description;
|
|
1862
|
+
}
|
|
1863
|
+
const cells = entityNames.map(
|
|
1864
|
+
(name) => propertyRole(manifest.entities[name], property)
|
|
1865
|
+
);
|
|
1866
|
+
return { property, type, description, cells };
|
|
1867
|
+
});
|
|
1868
|
+
const propertyMatrixHasDescriptions = propertyMatrix.some(
|
|
1869
|
+
(row) => row.description !== ""
|
|
1870
|
+
);
|
|
1871
|
+
const maintainedPaths = buildMaintainedPaths(registry);
|
|
1872
|
+
const entities = entityNames.map((name) => {
|
|
1873
|
+
const entity = manifest.entities[name];
|
|
1874
|
+
const relations = Object.keys(entity.relations).sort().map((propName) => {
|
|
1875
|
+
const rel = entity.relations[propName];
|
|
1876
|
+
return { property: propName, target: rel.target, type: rel.type };
|
|
1877
|
+
});
|
|
1878
|
+
const maintained = maintainedPaths.filter((row) => row.target.startsWith(`${name}.`)).map((row) => ({
|
|
1879
|
+
field: row.target.slice(name.length + 1),
|
|
1880
|
+
pattern: row.pattern
|
|
1881
|
+
}));
|
|
1882
|
+
return {
|
|
1883
|
+
name,
|
|
1884
|
+
description: entity.description ?? "",
|
|
1885
|
+
pk: keyLabel("PK", entity.key?.pkTemplate ?? null),
|
|
1886
|
+
sk: keyLabel("SK", entity.key?.skTemplate ?? null),
|
|
1887
|
+
gsis: entity.gsis.map((g) => ({
|
|
1888
|
+
indexName: g.indexName,
|
|
1889
|
+
pk: g.pkTemplate,
|
|
1890
|
+
sk: g.skTemplate ?? "",
|
|
1891
|
+
unique: g.unique,
|
|
1892
|
+
description: g.description ?? ""
|
|
1893
|
+
})),
|
|
1894
|
+
relations,
|
|
1895
|
+
maintained,
|
|
1896
|
+
fields: Object.keys(entity.fields).sort().map((field) => ({
|
|
1897
|
+
field,
|
|
1898
|
+
type: fieldTypeLabel(entity, field),
|
|
1899
|
+
description: entity.fields[field].description ?? ""
|
|
1900
|
+
})),
|
|
1901
|
+
hasFieldDescriptions: Object.values(entity.fields).some(
|
|
1902
|
+
(f) => (f.description ?? "") !== ""
|
|
1903
|
+
)
|
|
1904
|
+
};
|
|
1905
|
+
});
|
|
1906
|
+
const accessPatterns = [];
|
|
1907
|
+
const accessMatrix = [];
|
|
1908
|
+
for (const name of Object.keys(operations.queries).sort()) {
|
|
1909
|
+
const spec = operations.queries[name];
|
|
1910
|
+
const entityName = rootEntityName(spec, manifest);
|
|
1911
|
+
const { resolve: resolve3, diagram } = buildResolution(spec, manifest);
|
|
1912
|
+
const relation = isRelation(spec);
|
|
1913
|
+
const batchGet = usesBatchGet(spec);
|
|
1914
|
+
const cursor = usesCursor(spec);
|
|
1915
|
+
const index = rootIndex(spec);
|
|
1916
|
+
const signature = `${entityName || "Query"}.${name}(${Object.keys(spec.params).sort().join(", ")})`;
|
|
1917
|
+
accessPatterns.push({
|
|
1918
|
+
signature,
|
|
1919
|
+
description: spec.description ?? "",
|
|
1920
|
+
index,
|
|
1921
|
+
input: inputShape(spec),
|
|
1922
|
+
output: outputShape(spec, entityName),
|
|
1923
|
+
resolve: resolve3,
|
|
1924
|
+
relation,
|
|
1925
|
+
batchGet,
|
|
1926
|
+
cursor,
|
|
1927
|
+
diagram
|
|
1928
|
+
});
|
|
1929
|
+
accessMatrix.push({ query: signature, index, relation, batchGet, cursor });
|
|
1930
|
+
}
|
|
1931
|
+
const mutationContracts = [];
|
|
1932
|
+
for (const name of Object.keys(operations.commands).sort()) {
|
|
1933
|
+
const spec = operations.commands[name];
|
|
1934
|
+
const verb = spec.type === "PutItem" ? "TransactWrite \u2192 " + spec.entity + " item" : spec.type === "UpdateItem" ? "TransactWrite \u2192 " + spec.entity + " item" : "TransactWrite \u2192 delete " + spec.entity + " item";
|
|
1935
|
+
mutationContracts.push({
|
|
1936
|
+
signature: commandSignature(name, spec),
|
|
1937
|
+
description: spec.description ?? "",
|
|
1938
|
+
input: inputShape(spec),
|
|
1939
|
+
output: spec.entity,
|
|
1940
|
+
resolve: verb
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
const cdcEntries = buildCdcEntries(registry);
|
|
1944
|
+
const cfnResources = buildCfnResources(manifest, options.streamsEnabled);
|
|
1945
|
+
return {
|
|
1946
|
+
table,
|
|
1947
|
+
title,
|
|
1948
|
+
overview,
|
|
1949
|
+
entityNames,
|
|
1950
|
+
relationEdges,
|
|
1951
|
+
gsiColumns,
|
|
1952
|
+
keySchema,
|
|
1953
|
+
propertyMatrix,
|
|
1954
|
+
propertyMatrixHasDescriptions,
|
|
1955
|
+
entities,
|
|
1956
|
+
accessPatterns,
|
|
1957
|
+
accessMatrix,
|
|
1958
|
+
mutationContracts,
|
|
1959
|
+
maintainedPaths,
|
|
1960
|
+
cdcEntries,
|
|
1961
|
+
cfnResources,
|
|
1962
|
+
cfnFile
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// src/codegen/docs.ts
|
|
1967
|
+
import { existsSync, readFileSync } from "fs";
|
|
1968
|
+
import { resolve } from "path";
|
|
1969
|
+
import Handlebars from "handlebars";
|
|
1970
|
+
|
|
1971
|
+
// src/codegen/docs-templates.ts
|
|
1972
|
+
var PARTIAL_NAMES = [
|
|
1973
|
+
"index",
|
|
1974
|
+
"table-overview",
|
|
1975
|
+
"er-diagram",
|
|
1976
|
+
"key-schema",
|
|
1977
|
+
"property-matrix",
|
|
1978
|
+
"entity",
|
|
1979
|
+
"access-pattern",
|
|
1980
|
+
"maintained",
|
|
1981
|
+
"cdc",
|
|
1982
|
+
"cloudformation"
|
|
1983
|
+
];
|
|
1984
|
+
var INDEX = `# {{title}}
|
|
1985
|
+
|
|
1986
|
+
> Generated by \`graphddb generate docs\`
|
|
1987
|
+
|
|
1988
|
+
---
|
|
1989
|
+
|
|
1990
|
+
{{> table-overview }}
|
|
1991
|
+
|
|
1992
|
+
---
|
|
1993
|
+
|
|
1994
|
+
{{> er-diagram }}
|
|
1995
|
+
{{> key-schema }}
|
|
1996
|
+
|
|
1997
|
+
---
|
|
1998
|
+
|
|
1999
|
+
{{> property-matrix }}
|
|
2000
|
+
|
|
2001
|
+
---
|
|
2002
|
+
|
|
2003
|
+
## Entity Details
|
|
2004
|
+
|
|
2005
|
+
{{#each entities}}
|
|
2006
|
+
{{> entity this }}
|
|
2007
|
+
{{/each}}
|
|
2008
|
+
{{#if accessMatrix.length}}---
|
|
2009
|
+
|
|
2010
|
+
## Access Patterns
|
|
2011
|
+
|
|
2012
|
+
Each query resolves to a physical operation, an index (PK or GSI), and a projection.
|
|
2013
|
+
The matrix summarizes; the contracts below give the input/output and execution for each.
|
|
2014
|
+
|
|
2015
|
+
| Query | Index | Relation | BatchGet | Cursor |
|
|
2016
|
+
|------|------|------|------|------|
|
|
2017
|
+
{{#each accessMatrix}}| \`{{query}}\` | {{index}} | {{#if relation}}\u2713{{/if}} | {{#if batchGet}}\u2713{{/if}} | {{#if cursor}}\u2713{{/if}} |
|
|
2018
|
+
{{/each}}
|
|
2019
|
+
|
|
2020
|
+
{{#each accessPatterns}}
|
|
2021
|
+
{{> access-pattern this }}
|
|
2022
|
+
{{/each}}
|
|
2023
|
+
{{/if}}
|
|
2024
|
+
{{#if mutationContracts.length}}---
|
|
2025
|
+
|
|
2026
|
+
## Mutation Contracts
|
|
2027
|
+
|
|
2028
|
+
{{#each mutationContracts}}
|
|
2029
|
+
### \`{{signature}}\`
|
|
2030
|
+
{{#if description}}
|
|
2031
|
+
|
|
2032
|
+
{{description}}
|
|
2033
|
+
{{/if}}
|
|
2034
|
+
|
|
2035
|
+
\`\`\`yaml
|
|
2036
|
+
Input: {{input}}
|
|
2037
|
+
Output: {{output}}
|
|
2038
|
+
Resolve: {{resolve}}
|
|
2039
|
+
\`\`\`
|
|
2040
|
+
|
|
2041
|
+
{{/each}}
|
|
2042
|
+
{{/if}}
|
|
2043
|
+
{{> maintained }}
|
|
2044
|
+
{{> cdc }}
|
|
2045
|
+
---
|
|
2046
|
+
|
|
2047
|
+
{{> cloudformation }}
|
|
2048
|
+
`;
|
|
2049
|
+
var TABLE_OVERVIEW = `## Table Overview
|
|
2050
|
+
|
|
2051
|
+
| Item | Value |
|
|
2052
|
+
|------|-------|
|
|
2053
|
+
{{#each overview}}| {{item}} | {{value}} |
|
|
2054
|
+
{{/each}}`;
|
|
2055
|
+
var ER_DIAGRAM = `{{#if relationEdges.length}}## Entity Relationship Diagram
|
|
2056
|
+
|
|
2057
|
+
\`\`\`mermaid
|
|
2058
|
+
graph TD
|
|
2059
|
+
{{#each relationEdges}} {{mermaidId source}} -->|{{label}}| {{mermaidId target}}
|
|
2060
|
+
{{/each}}\`\`\`
|
|
2061
|
+
|
|
2062
|
+
---
|
|
2063
|
+
|
|
2064
|
+
{{/if}}`;
|
|
2065
|
+
var KEY_SCHEMA = `## Physical Key Schema
|
|
2066
|
+
|
|
2067
|
+
| Model | PK | SK |{{#each gsiColumns}} {{this}} PK | {{this}} SK |{{/each}}
|
|
2068
|
+
|------|------|------|{{#each gsiColumns}}------|------|{{/each}}
|
|
2069
|
+
{{#each keySchema}}| {{model}} | {{codeOrEmpty pk}} | {{codeOrEmpty sk}} |{{#each gsis}} {{codeOrEmpty pk}} | {{codeOrEmpty sk}} |{{/each}}
|
|
2070
|
+
{{/each}}`;
|
|
2071
|
+
var PROPERTY_MATRIX = `## Property Matrix
|
|
2072
|
+
|
|
2073
|
+
| Property | Type |{{#if propertyMatrixHasDescriptions}} Description |{{/if}}{{#each entityNames}} {{this}} |{{/each}}
|
|
2074
|
+
|----------|------|{{#if propertyMatrixHasDescriptions}}-------------|{{/if}}{{#each entityNames}}------|{{/each}}
|
|
2075
|
+
{{#each propertyMatrix}}| {{property}} | {{type}} |{{#if ../propertyMatrixHasDescriptions}} {{description}} |{{/if}}{{#each cells}} {{this}} |{{/each}}
|
|
2076
|
+
{{/each}}
|
|
2077
|
+
|
|
2078
|
+
Legend: **PK** primary partition key \xB7 **SK** primary sort key \xB7 GSI keys \xB7 **\u2713** normal attribute`;
|
|
2079
|
+
var ENTITY = `### {{name}}
|
|
2080
|
+
{{#if description}}
|
|
2081
|
+
|
|
2082
|
+
{{description}}
|
|
2083
|
+
{{/if}}
|
|
2084
|
+
|
|
2085
|
+
- **Key**: \`{{pk}}\`{{#if sk}} / \`{{sk}}\`{{/if}}
|
|
2086
|
+
{{#each gsis}}- **{{indexName}}**: \`PK = {{pk}}\`{{#if sk}} / \`SK = {{sk}}\`{{/if}}{{#if unique}} (unique){{/if}}
|
|
2087
|
+
{{/each}}
|
|
2088
|
+
{{#if relations.length}}- **Relations**: {{#each relations}}\`{{property}}\` \u2192 {{target}} ({{type}}){{#unless @last}}, {{/unless}}{{/each}}
|
|
2089
|
+
{{/if}}
|
|
2090
|
+
{{#if maintained.length}}- **Maintained**: {{#each maintained}}\`{{field}}\` ({{pattern}}){{#unless @last}}, {{/unless}}{{/each}}
|
|
2091
|
+
{{/if}}
|
|
2092
|
+
|
|
2093
|
+
| Field | Type |{{#if hasFieldDescriptions}} Description |{{/if}}
|
|
2094
|
+
|------|------|{{#if hasFieldDescriptions}}------|{{/if}}
|
|
2095
|
+
{{#each fields}}| {{field}} | {{type}} |{{#if ../hasFieldDescriptions}} {{description}} |{{/if}}
|
|
2096
|
+
{{/each}}
|
|
2097
|
+
`;
|
|
2098
|
+
var ACCESS_PATTERN = `### \`{{signature}}\`
|
|
2099
|
+
{{#if description}}
|
|
2100
|
+
|
|
2101
|
+
{{description}}
|
|
2102
|
+
{{/if}}
|
|
2103
|
+
|
|
2104
|
+
\`\`\`yaml
|
|
2105
|
+
Input: {{input}}
|
|
2106
|
+
Output: {{output}}
|
|
2107
|
+
Resolve: {{resolve}}
|
|
2108
|
+
\`\`\`
|
|
2109
|
+
|
|
2110
|
+
\`\`\`mermaid
|
|
2111
|
+
flowchart LR
|
|
2112
|
+
{{#each diagram}}{{mermaidId label}}["{{label}}"]{{#unless @last}} {{#if edge}}-- "{{edge}}" {{/if}}--> {{/unless}}{{/each}}
|
|
2113
|
+
\`\`\`
|
|
2114
|
+
|
|
2115
|
+
`;
|
|
2116
|
+
var MAINTAINED = `{{#if maintainedPaths.length}}---
|
|
2117
|
+
|
|
2118
|
+
## Maintained Access Paths
|
|
2119
|
+
|
|
2120
|
+
Write-side projections kept in sync automatically, within the source write's transaction
|
|
2121
|
+
(or via the CDC stream for \`updateMode: 'stream'\`).
|
|
2122
|
+
|
|
2123
|
+
| Source | Target | Pattern | Update |
|
|
2124
|
+
|---------|--------|--------|--------|
|
|
2125
|
+
{{#each maintainedPaths}}| {{source}} | {{target}} | {{pattern}} | {{update}} |
|
|
2126
|
+
{{/each}}
|
|
2127
|
+
|
|
2128
|
+
\`\`\`mermaid
|
|
2129
|
+
flowchart LR
|
|
2130
|
+
{{#each maintainedPaths}} {{mermaidId source}} -- "{{pattern}}" --> {{mermaidId target}}
|
|
2131
|
+
{{/each}}\`\`\`
|
|
2132
|
+
|
|
2133
|
+
{{/if}}`;
|
|
2134
|
+
var CDC = `{{#if cdcEntries.length}}---
|
|
2135
|
+
|
|
2136
|
+
## CDC
|
|
2137
|
+
|
|
2138
|
+
\`@cdcProjected\` models expose \`fromChange\` / \`subscribe\` to parse the change stream into
|
|
2139
|
+
typed records. Delivery and idempotency are the consumer's responsibility.
|
|
2140
|
+
|
|
2141
|
+
{{#each cdcEntries}}
|
|
2142
|
+
\`\`\`yaml
|
|
2143
|
+
Contract: {{model}}.subscribe
|
|
2144
|
+
Input: ChangeEvent<{{model}}>
|
|
2145
|
+
Output: [{{model}} | null, {{model}} | null] # [oldRecord, newRecord]
|
|
2146
|
+
\`\`\`
|
|
2147
|
+
|
|
2148
|
+
\`\`\`mermaid
|
|
2149
|
+
flowchart LR
|
|
2150
|
+
Mutate --> Stream["DynamoDB Stream"] --> CE["ChangeEvent"]
|
|
2151
|
+
CE --> FC["{{model}}.fromChange()"] --> Sub["subscribe()"] --> Consumer
|
|
2152
|
+
\`\`\`
|
|
2153
|
+
|
|
2154
|
+
{{/each}}
|
|
2155
|
+
{{/if}}`;
|
|
2156
|
+
var CLOUDFORMATION = `## CloudFormation
|
|
2157
|
+
|
|
2158
|
+
Generated by \`graphddb generate cloudformation\` (see \`{{cfnFile}}\`).
|
|
2159
|
+
|
|
2160
|
+
| Resource | Description |
|
|
2161
|
+
|------|------|
|
|
2162
|
+
{{#each cfnResources}}| \`{{resource}}\` | {{description}} |
|
|
2163
|
+
{{/each}}`;
|
|
2164
|
+
var BUNDLED_PARTIALS = {
|
|
2165
|
+
index: INDEX,
|
|
2166
|
+
"table-overview": TABLE_OVERVIEW,
|
|
2167
|
+
"er-diagram": ER_DIAGRAM,
|
|
2168
|
+
"key-schema": KEY_SCHEMA,
|
|
2169
|
+
"property-matrix": PROPERTY_MATRIX,
|
|
2170
|
+
entity: ENTITY,
|
|
2171
|
+
"access-pattern": ACCESS_PATTERN,
|
|
2172
|
+
maintained: MAINTAINED,
|
|
2173
|
+
cdc: CDC,
|
|
2174
|
+
cloudformation: CLOUDFORMATION
|
|
2175
|
+
};
|
|
2176
|
+
|
|
2177
|
+
// src/codegen/docs.ts
|
|
2178
|
+
var DOCS_INDEX_FILE = "index.md";
|
|
2179
|
+
function registerBuiltinHelpers(hb) {
|
|
2180
|
+
hb.registerHelper("eq", (a, b) => a === b);
|
|
2181
|
+
hb.registerHelper(
|
|
2182
|
+
"join",
|
|
2183
|
+
(list, sep) => Array.isArray(list) ? list.join(typeof sep === "string" ? sep : ", ") : ""
|
|
2184
|
+
);
|
|
2185
|
+
hb.registerHelper("pascal", (value) => {
|
|
2186
|
+
const s = String(value ?? "");
|
|
2187
|
+
return s.split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
2188
|
+
});
|
|
2189
|
+
hb.registerHelper("mermaidId", (value) => {
|
|
2190
|
+
const s = String(value ?? "");
|
|
2191
|
+
const id = s.replace(/[^A-Za-z0-9_]/g, "_").replace(/_+/g, "_");
|
|
2192
|
+
return /^[0-9]/.test(id) ? `n_${id}` : id || "n";
|
|
2193
|
+
});
|
|
2194
|
+
hb.registerHelper("codeOrEmpty", (value) => {
|
|
2195
|
+
const s = String(value ?? "");
|
|
2196
|
+
return s ? new hb.SafeString(`\`${s}\``) : "";
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
function partialSource(name, templateDir) {
|
|
2200
|
+
if (templateDir) {
|
|
2201
|
+
const candidate = resolve(templateDir, `${name}.hbs`);
|
|
2202
|
+
if (existsSync(candidate)) {
|
|
2203
|
+
return readFileSync(candidate, "utf8");
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
return BUNDLED_PARTIALS[name];
|
|
2207
|
+
}
|
|
2208
|
+
function buildEnvironment(options) {
|
|
2209
|
+
const hb = Handlebars.create();
|
|
2210
|
+
registerBuiltinHelpers(hb);
|
|
2211
|
+
if (options.helpers) {
|
|
2212
|
+
for (const [name, fn] of Object.entries(options.helpers)) {
|
|
2213
|
+
hb.registerHelper(name, fn);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
for (const name of PARTIAL_NAMES) {
|
|
2217
|
+
hb.registerPartial(
|
|
2218
|
+
name,
|
|
2219
|
+
hb.compile(partialSource(name, options.templateDir), { noEscape: true })
|
|
2220
|
+
);
|
|
2221
|
+
}
|
|
2222
|
+
return hb;
|
|
2223
|
+
}
|
|
2224
|
+
function generateDocs(docModel, options = {}) {
|
|
2225
|
+
const hb = buildEnvironment(options);
|
|
2226
|
+
const index = hb.partials["index"];
|
|
2227
|
+
const template = typeof index === "function" ? index : hb.compile(index, { noEscape: true });
|
|
2228
|
+
let out = template(docModel);
|
|
2229
|
+
out = out.replace(/([^\n])\n(---\n)/g, "$1\n\n$2").replace(/(\n---)\n([^\n])/g, "$1\n\n$2").replace(/([^\n])\n(#{1,6} )/g, "$1\n\n$2").replace(/\n{3,}/g, "\n\n").replace(/\s*$/, "") + "\n";
|
|
2230
|
+
return { [DOCS_INDEX_FILE]: out };
|
|
2231
|
+
}
|
|
2232
|
+
|
|
1466
2233
|
// src/codegen/index.ts
|
|
1467
2234
|
var OUTPUT_FILES = [
|
|
1468
2235
|
"manifest.json",
|
|
@@ -1498,7 +2265,7 @@ function renderBundle(queries = {}, commands = {}, registry = MetadataRegistry,
|
|
|
1498
2265
|
// src/codegen/jsdoc.ts
|
|
1499
2266
|
async function loadTypescript() {
|
|
1500
2267
|
try {
|
|
1501
|
-
const mod = await import("
|
|
2268
|
+
const mod = await import("typescript");
|
|
1502
2269
|
return mod.default ?? mod;
|
|
1503
2270
|
} catch {
|
|
1504
2271
|
return void 0;
|
|
@@ -1727,7 +2494,7 @@ function fail(code, message, exitCode) {
|
|
|
1727
2494
|
process.exit(exitCode);
|
|
1728
2495
|
}
|
|
1729
2496
|
function toUrl(file) {
|
|
1730
|
-
const abs = isAbsolute(file) ? file :
|
|
2497
|
+
const abs = isAbsolute(file) ? file : resolve2(process.cwd(), file);
|
|
1731
2498
|
return pathToFileURL(abs).href;
|
|
1732
2499
|
}
|
|
1733
2500
|
var tsxRegistered = false;
|
|
@@ -1749,6 +2516,12 @@ async function importModule(file) {
|
|
|
1749
2516
|
if (/\.[mc]?ts$/.test(file)) await ensureTsx();
|
|
1750
2517
|
return await import(toUrl(file));
|
|
1751
2518
|
}
|
|
2519
|
+
async function importModelEntry(entry) {
|
|
2520
|
+
await importModule(entry);
|
|
2521
|
+
if (MetadataRegistry.linter === null) {
|
|
2522
|
+
MetadataRegistry.linter = createDefaultLinter();
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
1752
2525
|
async function loadDefinitionMap(file, exportName) {
|
|
1753
2526
|
const mod = await importModule(file);
|
|
1754
2527
|
const value = mod[exportName];
|
|
@@ -1782,7 +2555,7 @@ var handlers = {
|
|
|
1782
2555
|
if (!entry) fail("INVALID_ARGS", "Missing required option --entry.", 2);
|
|
1783
2556
|
if (!out) fail("INVALID_ARGS", "Missing required option --out.", 2);
|
|
1784
2557
|
try {
|
|
1785
|
-
await
|
|
2558
|
+
await importModelEntry(entry);
|
|
1786
2559
|
const queries = await loadDefinitionMap(queriesPath ?? entry, "queries");
|
|
1787
2560
|
const commands = await loadDefinitionMap(commandsPath ?? entry, "commands");
|
|
1788
2561
|
const transactions = await loadOptionalMap(
|
|
@@ -1797,7 +2570,7 @@ var handlers = {
|
|
|
1797
2570
|
contextsPath ?? contractsPath ?? entry,
|
|
1798
2571
|
"contexts"
|
|
1799
2572
|
);
|
|
1800
|
-
const abs = (p) => isAbsolute(p) ? p :
|
|
2573
|
+
const abs = (p) => isAbsolute(p) ? p : resolve2(process.cwd(), p);
|
|
1801
2574
|
await applyJsDocDescriptions(
|
|
1802
2575
|
{
|
|
1803
2576
|
entry: abs(entry),
|
|
@@ -1812,19 +2585,19 @@ var handlers = {
|
|
|
1812
2585
|
contracts: { contracts, contexts }
|
|
1813
2586
|
});
|
|
1814
2587
|
void dataclass;
|
|
1815
|
-
const outDir = isAbsolute(out) ? out :
|
|
2588
|
+
const outDir = isAbsolute(out) ? out : resolve2(process.cwd(), out);
|
|
1816
2589
|
mkdirSync(outDir, { recursive: true });
|
|
1817
2590
|
for (const name of OUTPUT_FILES) {
|
|
1818
|
-
writeFileSync(
|
|
2591
|
+
writeFileSync(resolve2(outDir, name), files[name], "utf8");
|
|
1819
2592
|
}
|
|
1820
2593
|
const emittedFiles = [...OUTPUT_FILES];
|
|
1821
2594
|
if (cdcProjectedModelNames().length > 0) {
|
|
1822
|
-
const entryAbs = isAbsolute(entry) ? entry :
|
|
2595
|
+
const entryAbs = isAbsolute(entry) ? entry : resolve2(process.cwd(), entry);
|
|
1823
2596
|
let modelsModule = relative(outDir, entryAbs).replace(/\\/g, "/").replace(/\.[mc]?ts$/, "");
|
|
1824
2597
|
if (!modelsModule.startsWith(".")) modelsModule = `./${modelsModule}`;
|
|
1825
2598
|
const cdcRegistryFile = "cdc-registry.ts";
|
|
1826
2599
|
writeFileSync(
|
|
1827
|
-
|
|
2600
|
+
resolve2(outDir, cdcRegistryFile),
|
|
1828
2601
|
generateCdcRegistry({ modelsModule }),
|
|
1829
2602
|
"utf8"
|
|
1830
2603
|
);
|
|
@@ -1978,7 +2751,7 @@ var handlers = {
|
|
|
1978
2751
|
tblClass = tableClass;
|
|
1979
2752
|
}
|
|
1980
2753
|
try {
|
|
1981
|
-
await
|
|
2754
|
+
await importModelEntry(entry);
|
|
1982
2755
|
const manifest = buildManifest();
|
|
1983
2756
|
const streamMaintenanceDetected = streamMode === "auto" ? detectStreamMaintenance() : false;
|
|
1984
2757
|
const template = emitCloudformation(manifest, {
|
|
@@ -2001,7 +2774,7 @@ var handlers = {
|
|
|
2001
2774
|
autoscaleTarget: autoscaleTargetNum
|
|
2002
2775
|
});
|
|
2003
2776
|
if (out) {
|
|
2004
|
-
const outFile = isAbsolute(out) ? out :
|
|
2777
|
+
const outFile = isAbsolute(out) ? out : resolve2(process.cwd(), out);
|
|
2005
2778
|
mkdirSync(dirname(outFile), { recursive: true });
|
|
2006
2779
|
writeFileSync(outFile, template, "utf8");
|
|
2007
2780
|
process.stdout.write(
|
|
@@ -2018,6 +2791,131 @@ var handlers = {
|
|
|
2018
2791
|
const message = err instanceof Error ? err.message : String(err);
|
|
2019
2792
|
fail("GENERATION_FAILED", message, 1);
|
|
2020
2793
|
}
|
|
2794
|
+
},
|
|
2795
|
+
/**
|
|
2796
|
+
* `generate docs` (issue #183).
|
|
2797
|
+
*
|
|
2798
|
+
* Loads the entry model module (side-effecting import registers the entities)
|
|
2799
|
+
* plus the optional query / command definitions, builds the serializable
|
|
2800
|
+
* manifest + operations, then the normalized {@link buildDocModel} view model,
|
|
2801
|
+
* and renders the model-spec Markdown (Mermaid fences) via the Handlebars
|
|
2802
|
+
* generator to a single `index.md` in `--out`. `--template-dir` overrides
|
|
2803
|
+
* bundled partials per-name and `--helpers` registers extra Handlebars helpers.
|
|
2804
|
+
* Mirrors `generatePython`'s tsx-based entry loading and JSON error / exit shape.
|
|
2805
|
+
*
|
|
2806
|
+
* The Table Overview billing / streams are resolved from the CFn-shared flags
|
|
2807
|
+
* (`--billing-mode` / `--stream`, `stream auto` detecting `updateMode:'stream'`
|
|
2808
|
+
* maintenance like the CFn generator); deploy-time-only items with no flag
|
|
2809
|
+
* render "as configured at deployment" in the DocModel.
|
|
2810
|
+
*/
|
|
2811
|
+
async generateDocs(options) {
|
|
2812
|
+
const {
|
|
2813
|
+
entry,
|
|
2814
|
+
queries: queriesPath,
|
|
2815
|
+
out,
|
|
2816
|
+
format,
|
|
2817
|
+
split,
|
|
2818
|
+
templateDir,
|
|
2819
|
+
helpers: helpersPath,
|
|
2820
|
+
billingMode,
|
|
2821
|
+
stream
|
|
2822
|
+
} = options;
|
|
2823
|
+
if (!entry) fail("INVALID_ARGS", "Missing required option --entry.", 2);
|
|
2824
|
+
if (!out) fail("INVALID_ARGS", "Missing required option --out.", 2);
|
|
2825
|
+
if (format !== void 0 && format !== "markdown") {
|
|
2826
|
+
fail("INVALID_ARGS", `Invalid --format '${format}'. Use markdown.`, 2);
|
|
2827
|
+
}
|
|
2828
|
+
if (split !== void 0 && split !== "none" && split !== "entity") {
|
|
2829
|
+
fail("INVALID_ARGS", `Invalid --split '${split}'. Use none or entity.`, 2);
|
|
2830
|
+
}
|
|
2831
|
+
const outIsFile = out.toLowerCase().endsWith(".md");
|
|
2832
|
+
if (split === "entity" && outIsFile) {
|
|
2833
|
+
fail(
|
|
2834
|
+
"INVALID_ARGS",
|
|
2835
|
+
`--split entity writes multiple files and needs a directory --out, not a .md file ('${out}').`,
|
|
2836
|
+
2
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
if (split === "entity") {
|
|
2840
|
+
fail(
|
|
2841
|
+
"INVALID_ARGS",
|
|
2842
|
+
`--split entity is not yet supported. Use --split none (a single index.md).`,
|
|
2843
|
+
2
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2846
|
+
let billing = "PAY_PER_REQUEST";
|
|
2847
|
+
if (billingMode !== void 0) {
|
|
2848
|
+
if (billingMode !== "PAY_PER_REQUEST" && billingMode !== "PROVISIONED") {
|
|
2849
|
+
fail(
|
|
2850
|
+
"INVALID_ARGS",
|
|
2851
|
+
`Invalid --billing-mode '${billingMode}'. Use PAY_PER_REQUEST or PROVISIONED.`,
|
|
2852
|
+
2
|
|
2853
|
+
);
|
|
2854
|
+
}
|
|
2855
|
+
billing = billingMode;
|
|
2856
|
+
}
|
|
2857
|
+
let streamMode = "auto";
|
|
2858
|
+
if (stream !== void 0) {
|
|
2859
|
+
if (stream !== "auto" && stream !== "on" && stream !== "off") {
|
|
2860
|
+
fail("INVALID_ARGS", `Invalid --stream '${stream}'. Use auto, on, or off.`, 2);
|
|
2861
|
+
}
|
|
2862
|
+
streamMode = stream;
|
|
2863
|
+
}
|
|
2864
|
+
try {
|
|
2865
|
+
await importModelEntry(entry);
|
|
2866
|
+
const queries = await loadOptionalMap(queriesPath ?? entry, "queries");
|
|
2867
|
+
const commands = await loadOptionalMap(queriesPath ?? entry, "commands");
|
|
2868
|
+
const manifest = buildManifest();
|
|
2869
|
+
const operations = buildOperations(
|
|
2870
|
+
queries,
|
|
2871
|
+
commands
|
|
2872
|
+
);
|
|
2873
|
+
const streamsEnabled = streamMode === "on" ? true : streamMode === "off" ? false : detectStreamMaintenance();
|
|
2874
|
+
const docModel = buildDocModel(manifest, operations, MetadataRegistry, {
|
|
2875
|
+
billingMode: billing,
|
|
2876
|
+
streamsEnabled
|
|
2877
|
+
});
|
|
2878
|
+
let helpers;
|
|
2879
|
+
if (helpersPath) {
|
|
2880
|
+
const mod = await importModule(helpersPath);
|
|
2881
|
+
const exported = mod.helpers ?? mod.default;
|
|
2882
|
+
if (!exported || typeof exported !== "object") {
|
|
2883
|
+
fail(
|
|
2884
|
+
"INVALID_ARGS",
|
|
2885
|
+
`Module '${helpersPath}' must export a 'helpers' (or default) object mapping helper names to functions.`,
|
|
2886
|
+
2
|
|
2887
|
+
);
|
|
2888
|
+
}
|
|
2889
|
+
helpers = exported;
|
|
2890
|
+
}
|
|
2891
|
+
const files = generateDocs(docModel, {
|
|
2892
|
+
templateDir: templateDir ? isAbsolute(templateDir) ? templateDir : resolve2(process.cwd(), templateDir) : void 0,
|
|
2893
|
+
helpers
|
|
2894
|
+
});
|
|
2895
|
+
const outAbs = isAbsolute(out) ? out : resolve2(process.cwd(), out);
|
|
2896
|
+
const emitted = [];
|
|
2897
|
+
if (outIsFile) {
|
|
2898
|
+
const [contents] = Object.values(files);
|
|
2899
|
+
mkdirSync(dirname(outAbs), { recursive: true });
|
|
2900
|
+
writeFileSync(outAbs, contents, "utf8");
|
|
2901
|
+
emitted.push(out);
|
|
2902
|
+
process.stdout.write(
|
|
2903
|
+
JSON.stringify({ status: "ok", outDir: dirname(out), files: emitted }) + "\n"
|
|
2904
|
+
);
|
|
2905
|
+
} else {
|
|
2906
|
+
mkdirSync(outAbs, { recursive: true });
|
|
2907
|
+
for (const [name, contents] of Object.entries(files)) {
|
|
2908
|
+
writeFileSync(resolve2(outAbs, name), contents, "utf8");
|
|
2909
|
+
emitted.push(name);
|
|
2910
|
+
}
|
|
2911
|
+
process.stdout.write(
|
|
2912
|
+
JSON.stringify({ status: "ok", outDir: out, files: emitted }) + "\n"
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
} catch (err) {
|
|
2916
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2917
|
+
fail("GENERATION_FAILED", message, 1);
|
|
2918
|
+
}
|
|
2021
2919
|
}
|
|
2022
2920
|
};
|
|
2023
2921
|
|