graphddb 0.5.1 → 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.
@@ -0,0 +1,36 @@
1
+ import { b as Linter, L as LintRule } from './registry-BD_5Rm5C.js';
2
+
3
+ /**
4
+ * Creates a Linter pre-loaded with rules safe for Entity registration.
5
+ *
6
+ * Rules included: no-scan, require-limit, gsi-ambiguity, missing-gsi,
7
+ * relation-depth, same-partition-preset, plus the Epic #118 Phase 1 maintenance
8
+ * validators (issue #126): missing-context, 400kb, hot-partition, fan-out,
9
+ * multi-maintainer-same-row. The maintenance validators are no-ops for any
10
+ * relation that does not declare `write.maintainedOn`, so unannotated models are
11
+ * unaffected. query-boundary is excluded because it flags non-unique GSIs which
12
+ * are valid for list() operations; boundary enforcement is handled at runtime by
13
+ * the planner's boundary-check.
14
+ *
15
+ * Also includes cfn-schema-consistency (issue #169): a whole-table validator
16
+ * that rejects entities sharing a physical table with an ambiguous base
17
+ * KeySchema, inconsistently-shared GSI index names, or more than 20 unioned
18
+ * GSIs — the CFn deploy-consistency user-error class, caught statically at
19
+ * finalize. It is additive: a single entity on its own table with ≤ 20 GSIs is
20
+ * never affected.
21
+ */
22
+ declare function createDefaultLinter(): Linter;
23
+
24
+ declare const noScanRule: LintRule;
25
+
26
+ declare const requireLimitRule: LintRule;
27
+
28
+ declare const queryBoundaryRule: LintRule;
29
+
30
+ declare const gsiAmbiguityRule: LintRule;
31
+
32
+ declare const missingGsiRule: LintRule;
33
+
34
+ declare const relationDepthRule: LintRule;
35
+
36
+ export { requireLimitRule as a, createDefaultLinter as c, gsiAmbiguityRule as g, missingGsiRule as m, noScanRule as n, queryBoundaryRule as q, relationDepthRule as r };
@@ -0,0 +1,4 @@
1
+ export { $ as BridgeBundle, a1 as CommandContractMethodSpec, a2 as CommandResolutionTarget, L as CommandSpec, a3 as CompiledFragment, a4 as CompiledMutationPlan, a5 as ComposeSpec, a6 as CompositionPlanSpec, a0 as ConditionSpec, O as ContextSpec, a7 as ContractCardinality, a8 as ContractCommandResult, a9 as ContractInputArity, aa as ContractKeySpec, ab as ContractKind, ac as ContractResolution, J as ContractSpec, ad as DerivedConditionCheck, ae as DerivedEdgeWrite, af as DerivedIdempotencyGuard, ag as DerivedMaintainOutbox, ah as DerivedMaintainWrite, ai as DerivedOutboxEvent, aj as DerivedUniqueGuard, ak as DerivedUpdate, al as EntityRefResolver, am as ExecutionPlanSpec, an as FilterSpec, ao as MAX_TRANSACT_COMPOSE_ITEMS, _ as Manifest, ap as ManifestEntity, aq as ManifestField, ar as ManifestFieldType, as as ManifestGsi, at as ManifestKey, au as ManifestRelation, av as ManifestTable, aw as OperationSpec, Y as OperationsDocument, ax as ParamSpec, ay as QueryContractMethodSpec, K as QuerySpec, az as RangeConditionSpec, aA as ReadOperationType, aB as SPEC_VERSION, aC as TransactionItemSpec, aD as TransactionItemType, N as TransactionSpec, aE as WhenSpec, aF as WriteOperationType, aG as assertNoCrossFragmentMaintainCollision, aH as compileFragment, aI as compileMutationPlan, aJ as compileSingleFragmentPlan, aK as resetMaintenanceGraphCache, aL as resolveLifecycle, aM as resolveMaintainers } from '../maintenance-view-adapter-D5t9taTE.js';
2
+ export { A as AnyModelContract, B as BuiltContracts, C as ContextOwnership, a as ContextOwnershipMap, b as ContractBoundaryViolation, c as ContractInputs, d as ContractMap, e as ContractN1Violation, o as assertBundleSerializable, p as assertContractBoundaries, q as assertContractN1Safe, r as assertJsonSerializable, s as assertSupportedCondition, t as buildBridgeBundle, u as buildContexts, v as buildContracts, w as buildManifest, x as buildOperations, y as buildQuerySpec, z as buildTransactionSpec, D as buildTransactions, E as collectContractBoundaryViolations, F as collectContractN1Violations } from '../index-CtDBo8Se.js';
3
+ import '../registry-BD_5Rm5C.js';
4
+ import '@aws-sdk/client-dynamodb';
@@ -0,0 +1,54 @@
1
+ import {
2
+ MAX_TRANSACT_COMPOSE_ITEMS,
3
+ SPEC_VERSION,
4
+ assertBundleSerializable,
5
+ assertContractBoundaries,
6
+ assertContractN1Safe,
7
+ assertJsonSerializable,
8
+ assertNoCrossFragmentMaintainCollision,
9
+ assertSupportedCondition,
10
+ buildBridgeBundle,
11
+ buildContexts,
12
+ buildContracts,
13
+ buildManifest,
14
+ buildOperations,
15
+ buildQuerySpec,
16
+ buildTransactionSpec,
17
+ buildTransactions,
18
+ collectContractBoundaryViolations,
19
+ collectContractN1Violations,
20
+ compileFragment,
21
+ compileMutationPlan,
22
+ compileSingleFragmentPlan,
23
+ resetMaintenanceGraphCache,
24
+ resolveLifecycle,
25
+ resolveMaintainers
26
+ } from "../chunk-Y7XV5QL2.js";
27
+ import "../chunk-CCIVET5K.js";
28
+ import "../chunk-PDUVTYC5.js";
29
+ export {
30
+ MAX_TRANSACT_COMPOSE_ITEMS,
31
+ SPEC_VERSION,
32
+ assertBundleSerializable,
33
+ assertContractBoundaries,
34
+ assertContractN1Safe,
35
+ assertJsonSerializable,
36
+ assertNoCrossFragmentMaintainCollision,
37
+ assertSupportedCondition,
38
+ buildBridgeBundle,
39
+ buildContexts,
40
+ buildContracts,
41
+ buildManifest,
42
+ buildOperations,
43
+ buildQuerySpec,
44
+ buildTransactionSpec,
45
+ buildTransactions,
46
+ collectContractBoundaryViolations,
47
+ collectContractN1Violations,
48
+ compileFragment,
49
+ compileMutationPlan,
50
+ compileSingleFragmentPlan,
51
+ resetMaintenanceGraphCache,
52
+ resolveLifecycle,
53
+ resolveMaintainers
54
+ };
@@ -1,4 +1,4 @@
1
- import { E as Executor, D as DynamoDBOperation, R as ReadExecOptions, a as ExecutorResult, B as BatchGetExecInput, P as PutInput, W as WriteExecOptions, b as WriteResult, U as UpdateInput, c as DeleteInput, d as BatchWriteExecItem, e as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, f as DDBModel, C as ChangeEvent } from '../types-B9rJ1z3H.js';
1
+ import { n as Executor, D as DynamoDBOperation, o as ReadExecOptions, p as ExecutorResult, q as BatchGetExecInput, P as PutInput, W as WriteExecOptions, r as WriteResult, s as UpdateInput, t as DeleteInput, u as BatchWriteExecItem, v as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, w as DDBModel, c as ChangeEvent } from '../maintenance-view-adapter-D5t9taTE.js';
2
2
  import '@aws-sdk/client-dynamodb';
3
3
 
4
4
  /**
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  createCdcEmulator
3
- } from "../chunk-EMJHTUA4.js";
3
+ } from "../chunk-M6URQOAW.js";
4
4
  import {
5
5
  ClientManager,
6
6
  MetadataRegistry,
7
- TableMapping,
8
7
  resolveModelClass
9
- } from "../chunk-B3GWIWT6.js";
8
+ } from "../chunk-CCIVET5K.js";
9
+ import {
10
+ TableMapping
11
+ } from "../chunk-PDUVTYC5.js";
10
12
 
11
13
  // src/memory/memory-store.ts
12
14
  function deepClone(value) {
@@ -0,0 +1,263 @@
1
+ # UserPermissions Table Specification
2
+
3
+ > Generated by `graphddb generate docs`
4
+
5
+ ---
6
+
7
+ ## Table Overview
8
+
9
+ | Item | Value |
10
+ |------|-------|
11
+ | Table | `UserPermissions` |
12
+ | Description | User / Group / Permission Management |
13
+ | Billing Mode | PAY_PER_REQUEST |
14
+ | Streams | NEW_AND_OLD_IMAGES |
15
+ | TTL | Disabled |
16
+ | PITR | Enabled |
17
+ | Table Class | STANDARD |
18
+ | Encryption | AWS Managed SSE |
19
+ | CloudFormation | `cloudformation.yaml` |
20
+
21
+ ---
22
+
23
+ ## Entity Relationship Diagram
24
+
25
+ ```mermaid
26
+ graph TD
27
+ User -->|hasMany| Membership
28
+ Membership -->|belongsTo| Group
29
+ User -->|hasMany| Permission
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Physical Key Schema
35
+
36
+ | Model | PK | SK | GSI1 PK | GSI1 SK |
37
+ |------|------|------|------|------|
38
+ | User | `USER#{userId}` | `PROFILE` | `EMAIL#{email}` | `USER#{userId}` |
39
+ | Group | `GROUP#{groupId}` | `PROFILE` | | |
40
+ | Membership | `GROUP#{groupId}` | `USER#{userId}` | `USER#{userId}` | `GROUP#{groupId}` |
41
+ | Permission | `USER#{userId}` | `PERMISSION#{permission}` | | |
42
+
43
+ ---
44
+
45
+ ## Property Matrix
46
+
47
+ | Property | Type | Description | User | Group | Membership | Permission |
48
+ |----------|------|-------------|------|------|------|------|
49
+ | userId | string | User ID | PK | | GSI1 PK | PK |
50
+ | groupId | string | Group ID | | PK | PK | |
51
+ | permission | string | Permission Name | | | | SK |
52
+ | email | string | Login Email | GSI1 PK | | | |
53
+ | name | string | Display Name | ✓ | ✓ | | |
54
+ | role | string | Group Role | | | ✓ | |
55
+ | createdAt | datetime | Created Time | ✓ | ✓ | ✓ | ✓ |
56
+
57
+ Legend: **PK** primary partition key · **SK** primary sort key · **GSI1 PK/SK** GSI1 keys · **✓** normal attribute
58
+
59
+ ---
60
+
61
+ ## Entity Details
62
+
63
+ ### User
64
+
65
+ - **Key**: `PK = USER#{userId}` / `SK = PROFILE`
66
+ - **GSI1**: `PK = EMAIL#{email}` / `SK = USER#{userId}`
67
+ - **Relations**: `groups` → Membership → Group (hasMany), `permissions` → Permission (hasMany)
68
+
69
+ | Field | Type | Description |
70
+ |------|------|------|
71
+ | userId | string | User ID |
72
+ | email | string | Login Email |
73
+ | name | string | Display Name |
74
+ | createdAt | datetime | Created Time |
75
+
76
+ ### Group
77
+
78
+ - **Key**: `PK = GROUP#{groupId}` / `SK = PROFILE`
79
+ - **Maintained**: `memberCount` (counter), `groupSnapshots` on User (embedded snapshot)
80
+
81
+ | Field | Type | Description |
82
+ |------|------|------|
83
+ | groupId | string | Group ID |
84
+ | name | string | Display Name |
85
+ | createdAt | datetime | Created Time |
86
+
87
+ ### Membership
88
+
89
+ - **Key**: `PK = GROUP#{groupId}` / `SK = USER#{userId}`
90
+ - **GSI1**: `PK = USER#{userId}` / `SK = GROUP#{groupId}`
91
+
92
+ | Field | Type | Description |
93
+ |------|------|------|
94
+ | groupId | string | Group ID |
95
+ | userId | string | User ID |
96
+ | role | string | Group Role |
97
+ | createdAt | datetime | Created Time |
98
+
99
+ ### Permission
100
+
101
+ - **Key**: `PK = USER#{userId}` / `SK = PERMISSION#{permission}`
102
+
103
+ | Field | Type | Description |
104
+ |------|------|------|
105
+ | userId | string | User ID |
106
+ | permission | string | Permission Name |
107
+ | createdAt | datetime | Created Time |
108
+
109
+ ---
110
+
111
+ ## Access Patterns
112
+
113
+ Each query resolves to a physical operation, an index (PK or GSI), and a projection.
114
+ The matrix summarizes; the contracts below give the input/output and execution for each.
115
+
116
+ | Query | Index | Relation | BatchGet | Cursor |
117
+ |------|------|------|------|------|
118
+ | `User.query(userId)` | PK | | | |
119
+ | `User.queryByEmail(email)` | GSI1 | | | |
120
+ | `User.groups(userId)` | PK | ✓ | ✓ | ✓ |
121
+ | `Group.members(groupId)` | PK | ✓ | ✓ | ✓ |
122
+ | `User.permissions(userId)` | PK | ✓ | | ✓ |
123
+
124
+ ### `User.query(userId)`
125
+
126
+ Returns a single user by primary key.
127
+
128
+ ```yaml
129
+ Input: { userId: string }
130
+ Output: User
131
+ Resolve: PK = USER#{userId} → Query → Projection(name, email)
132
+ ```
133
+
134
+ ```mermaid
135
+ flowchart LR
136
+ In["userId"] --> Key["PK = USER#{userId}"] --> Q["Query"] --> Out["User"]
137
+ ```
138
+
139
+ ### `User.queryByEmail(email)`
140
+
141
+ Returns a single user by email (unique GSI).
142
+
143
+ ```yaml
144
+ Input: { email: string }
145
+ Output: User
146
+ Resolve: GSI1 PK = EMAIL#{email} → Query → Projection(userId, name)
147
+ ```
148
+
149
+ ```mermaid
150
+ flowchart LR
151
+ In["email"] --> Key["GSI1 PK = EMAIL#{email}"] --> Q["Query"] --> Out["User"]
152
+ ```
153
+
154
+ ### `User.groups(userId)`
155
+
156
+ Returns the groups a user belongs to (paged).
157
+
158
+ ```yaml
159
+ Input: { userId: string, limit?: number, cursor?: string }
160
+ Output: Page<Group>
161
+ Resolve: Query(User) → Query(Membership by PK) → BatchGet(Group)
162
+ ```
163
+
164
+ ```mermaid
165
+ flowchart LR
166
+ User -- "PK" --> Membership -- "BatchGet" --> Group
167
+ ```
168
+
169
+ ### `Group.members(groupId)`
170
+
171
+ Returns the members of a group (paged).
172
+
173
+ ```yaml
174
+ Input: { groupId: string, limit?: number, cursor?: string }
175
+ Output: Page<User>
176
+ Resolve: Query(Group) → Query(Membership by PK) → BatchGet(User)
177
+ ```
178
+
179
+ ```mermaid
180
+ flowchart LR
181
+ Group -- "PK" --> Membership -- "BatchGet" --> User
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Mutation Contracts
187
+
188
+ ### `User.update()`
189
+
190
+ ```yaml
191
+ Input: { userId: string, name?: string, email?: string }
192
+ Output: User
193
+ Resolve: TransactWrite → User item
194
+ ```
195
+
196
+ ### `Membership.create()`
197
+
198
+ Creating a membership also maintains `Group.memberCount` in the same transaction.
199
+
200
+ ```yaml
201
+ Input: { groupId: string, userId: string, role: string }
202
+ Output: Membership
203
+ Resolve: TransactWrite → Membership item + Group.memberCount (ADD +1)
204
+ ```
205
+
206
+ ```mermaid
207
+ flowchart LR
208
+ Create["Membership.create"] --> TW["TransactWrite"]
209
+ TW --> M["Membership"]
210
+ TW --> C["Group.memberCount (+1)"]
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Maintained Access Paths
216
+
217
+ Write-side projections kept in sync automatically, within the source write's transaction
218
+ (or via the CDC stream for `updateMode: 'stream'`).
219
+
220
+ | Source | Target | Pattern | Update |
221
+ |---------|--------|--------|--------|
222
+ | Membership | Group.memberCount | counter | ADD +1 / −1 on create / remove |
223
+ | Group | User.groupSnapshots | embeddedSnapshot | project Group fields into User |
224
+
225
+ ```mermaid
226
+ flowchart LR
227
+ Membership -- "create / remove" --> Counter["ADD ±1"] --> MC["Group.memberCount"]
228
+ Group -- "projection" --> Snap["Embedded Snapshot"] --> GS["User.groupSnapshots"]
229
+ ```
230
+
231
+ ---
232
+
233
+ ## CDC
234
+
235
+ `@cdcProjected` models expose `fromChange` / `subscribe` to parse the change stream into
236
+ typed records. Delivery and idempotency are the consumer's responsibility.
237
+
238
+ ```yaml
239
+ Contract: User.subscribe
240
+ Input: ChangeEvent<User>
241
+ Output: [User | null, User | null] # [oldRecord, newRecord]
242
+ ```
243
+
244
+ ```mermaid
245
+ flowchart LR
246
+ Mutate --> Stream["DynamoDB Stream"] --> CE["ChangeEvent"]
247
+ CE --> FC["User.fromChange()"] --> Sub["subscribe()"] --> Consumer
248
+ ```
249
+
250
+ ---
251
+
252
+ ## CloudFormation
253
+
254
+ Generated by `graphddb generate cloudformation` (see `cloudformation.yaml`).
255
+
256
+ | Resource | Description |
257
+ |------|------|
258
+ | `AWS::DynamoDB::Table` | Physical table |
259
+ | GlobalSecondaryIndexes | GSI definitions (GSI1 …) |
260
+ | StreamSpecification | NEW_AND_OLD_IMAGES |
261
+ | TimeToLiveSpecification | TTL (when a TTL attribute is declared) |
262
+ | PointInTimeRecovery | PITR |
263
+ | Auto Scaling | Optional (PROVISIONED mode) |
@@ -0,0 +1,183 @@
1
+ # GraphDDB — Docs generation
2
+
3
+ `graphddb generate docs` renders a **human-facing model specification** —
4
+ Markdown with embedded Mermaid diagrams — directly from your TypeScript models.
5
+ It reads the same serializable manifest / operations the Python bridge and the
6
+ CloudFormation generator use (it loads your entry model, builds the manifest and
7
+ operation specs from the shared metadata registry, and renders the document), so
8
+ the spec never drifts from the code: your models are the single source of truth.
9
+
10
+ Output is **deterministic** — the same model set always produces the same
11
+ document — and the layout is defined by **Handlebars** templates you can override
12
+ per-section and extend with your own helpers.
13
+
14
+ > This document is the reference for the shipped `generate docs` command: its
15
+ > options, the sections it produces, and how to customize the templates.
16
+
17
+ ### Related specifications
18
+
19
+ - [`spec.md`](./spec.md) — the core API: entities, structured keys / GSIs,
20
+ relations, queries.
21
+ - [`cloudformation.md`](./cloudformation.md) — the sibling generator; the
22
+ `--billing-mode` / `--stream` flags are shared with it.
23
+ - [`design-patterns.md`](./design-patterns.md) — maintained access paths and the
24
+ `updateMode: 'stream'` signal that drives `--stream auto`.
25
+
26
+ ## Overview
27
+
28
+ ```bash
29
+ graphddb generate docs --entry models.ts --out doc.md
30
+ ```
31
+
32
+ - **`--entry`** loads the TypeScript (or pre-compiled `.js`/`.mjs`) module that
33
+ registers your entity models as a side effect of import. TypeScript modules are
34
+ transpiled on demand via `tsx` (an optional peer dependency — install it with
35
+ `npm i -D tsx`).
36
+ - **`--out`** accepts either a **file** or a **directory**. A path ending in
37
+ `.md` (e.g. `--out doc.md`) writes the single Markdown file at exactly that
38
+ path, creating parent directories as needed — this is the preferred usage.
39
+ Any other value is treated as a **directory** and the document is written as
40
+ `<dir>/index.md` (backward compatible). Either way the command prints a JSON
41
+ result (`{"status":"ok","outDir":…,"files":[…]}`) to stdout, where `files`
42
+ reports the written path(s).
43
+ - **`--queries`** (optional) points at the module exporting your `queries` /
44
+ `commands` (the `defineQueries` / `defineCommands` maps). It powers the Access
45
+ Patterns and Mutation Contracts sections; omit it and those sections are empty.
46
+ Defaults to the entry module.
47
+
48
+ To also document your access patterns and mutations:
49
+
50
+ ```bash
51
+ graphddb generate docs --entry models.ts --queries definitions.ts --out docs/
52
+ ```
53
+
54
+ ## Output sections
55
+
56
+ The generated `index.md` is composed, in this fixed order, from:
57
+
58
+ | Section | Source | Contents |
59
+ | --- | --- | --- |
60
+ | **Table Overview** | manifest + flags | Table name, billing mode, streams, TTL, and the deploy-time settings. |
61
+ | **Entity Relationship Diagram** | manifest relations | A Mermaid `graph TD` of the `hasMany` / `hasOne` / `belongsTo` edges. |
62
+ | **Physical Key Schema** | manifest keys / GSIs | A table of each model's `PK` / `SK` and per-GSI key templates. |
63
+ | **Property Matrix** | manifest fields | Each property's role (PK / SK / GSI key / plain attribute) across entities. |
64
+ | **Entity Details** | manifest | Per-entity key, GSIs, relations, maintained fields, and field table. |
65
+ | **Access Patterns** | operations | A summary matrix plus, per query, the input/output contract, a `Resolve:` line, and a Mermaid execution flowchart. |
66
+ | **Mutation Contracts** | operations | Per command, the input/output and resolution. |
67
+ | **Maintained Access Paths** | metadata registry | Write-side projections kept in sync (counter / embedded snapshot), with a Mermaid diagram. |
68
+ | **CDC** | metadata registry | For each `@cdcProjected()` model, the `subscribe` / `fromChange` contract and a stream diagram. |
69
+ | **CloudFormation** | manifest | A summary of the resources `generate cloudformation` emits. |
70
+
71
+ The Table Overview's **billing** and **streams** are resolved from the CLI flags
72
+ (shared with `generate cloudformation`); deploy-time-only settings that have no
73
+ flag (PITR, table class, encryption) render as `as configured at deployment`.
74
+
75
+ ## Options
76
+
77
+ | Option | Default | Description |
78
+ | --- | --- | --- |
79
+ | `--entry <file>` | (required) | Entry module registering the entity models. |
80
+ | `--out <path>` | (required) | Output path. A `*.md` file is written verbatim at that path; any other value is a directory and `index.md` is written inside it. |
81
+ | `--queries <file>` | entry module | Module exporting `queries` / `commands` for Access Patterns / Mutation Contracts. |
82
+ | `--format <format>` | `markdown` | Output format (only `markdown` today). |
83
+ | `--split <mode>` | `none` | `none` emits a single `index.md`. `entity` is reserved for a future per-entity split. |
84
+ | `--template-dir <dir>` | — | Directory of override `<partial>.hbs` templates. |
85
+ | `--helpers <file>` | — | JS module exporting extra Handlebars helpers. |
86
+ | `--billing-mode <mode>` | `PAY_PER_REQUEST` | Billing mode shown in the Table Overview (shared with `generate cloudformation`). |
87
+ | `--stream <mode>` | `auto` | Streams state in the Table Overview: `auto` detects stream-based maintenance (`updateMode: 'stream'`); `on` / `off` force it. |
88
+
89
+ ## Customizing the templates
90
+
91
+ The document is rendered from a set of bundled **Handlebars partials**, one per
92
+ section:
93
+
94
+ ```
95
+ index · table-overview · er-diagram · key-schema · property-matrix
96
+ · entity · access-pattern · maintained · cdc · cloudformation
97
+ ```
98
+
99
+ `index` is the document skeleton; it renders each section partial in order. All
100
+ partials reference only a normalized **view model** (the section headings, table
101
+ rows, diagram nodes are precomputed), so a custom template is just a loop over
102
+ ready-to-print data — you never have to reach into the raw manifest.
103
+
104
+ ### `--template-dir` (per-section override)
105
+
106
+ Point `--template-dir` at a directory containing any `<partial>.hbs` files. Each
107
+ file whose name matches a bundled partial **replaces** it; every partial you do
108
+ **not** provide falls back to the bundled default. You only write the sections
109
+ you want to change — there is no need to copy the whole set.
110
+
111
+ For example, to replace just the CloudFormation section:
112
+
113
+ ```
114
+ doc-templates/
115
+ cloudformation.hbs
116
+ ```
117
+
118
+ ```handlebars
119
+ {{! doc-templates/cloudformation.hbs }}
120
+ ## Infrastructure
121
+
122
+ The table `{{table}}` is provisioned by our platform team — see the internal
123
+ runbook. Generated CloudFormation lives in `{{cfnFile}}`.
124
+ ```
125
+
126
+ ```bash
127
+ graphddb generate docs --entry models.ts --out docs/ --template-dir doc-templates/
128
+ ```
129
+
130
+ Every other section still renders from the bundled partials.
131
+
132
+ ### `--helpers` (extra Handlebars helpers)
133
+
134
+ The generator registers a small built-in helper set — `eq`, `join`, `pascal`,
135
+ `mermaidId`, and `codeOrEmpty`. Pass `--helpers` a JS module to register more (a
136
+ custom helper of the same name overrides a built-in). The module exports its
137
+ helpers as a `helpers` named export (or the default export):
138
+
139
+ ```js
140
+ // doc-helpers.mjs
141
+ export const helpers = {
142
+ shout: (value) => String(value).toUpperCase(),
143
+ badge: (text) => `![${text}](https://img.shields.io/badge/-${text}-blue)`,
144
+ };
145
+ ```
146
+
147
+ ```bash
148
+ graphddb generate docs --entry models.ts --out docs/ \
149
+ --template-dir doc-templates/ --helpers doc-helpers.mjs
150
+ ```
151
+
152
+ Your helpers are then callable from any partial (bundled or overridden):
153
+
154
+ ```handlebars
155
+ {{shout table}}
156
+ ```
157
+
158
+ ## The view model
159
+
160
+ Templates receive a single normalized view model with these top-level fields —
161
+ the vocabulary a custom template can rely on:
162
+
163
+ | Field | Shape | Used by |
164
+ | --- | --- | --- |
165
+ | `title` / `table` | strings | headings, Table Overview |
166
+ | `overview` | `{ item, value }[]` | Table Overview |
167
+ | `entityNames` | `string[]` | Property Matrix columns |
168
+ | `relationEdges` | `{ source, target, label }[]` | ER diagram |
169
+ | `gsiColumns` / `keySchema` | GSI names / `{ model, pk, sk, gsis }[]` | Key Schema |
170
+ | `propertyMatrix` | `{ property, type, description, cells }[]` | Property Matrix |
171
+ | `entities` | per-entity `{ name, pk, sk, gsis, relations, maintained, fields }` | Entity Details |
172
+ | `accessMatrix` / `accessPatterns` | matrix rows / `{ signature, input, output, resolve, diagram, … }` | Access Patterns |
173
+ | `mutationContracts` | `{ signature, input, output, resolve }[]` | Mutation Contracts |
174
+ | `maintainedPaths` | `{ source, target, pattern, update }[]` | Maintained Access Paths |
175
+ | `cdcEntries` | `{ model }[]` | CDC |
176
+ | `cfnResources` / `cfnFile` | `{ resource, description }[]` / string | CloudFormation |
177
+
178
+ ## Scope
179
+
180
+ The Access Patterns are described to the extent resolvable at generation time —
181
+ the operation type, the index, the key template, the projection, and the
182
+ cardinality (single vs. paged). Actual read-capacity cost and row counts require
183
+ runtime information and are out of scope.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphddb",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Graph data modeling on DynamoDB with adjacency list pattern",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,6 +17,18 @@
17
17
  "./testing": {
18
18
  "types": "./dist/testing/index.d.ts",
19
19
  "import": "./dist/testing/index.js"
20
+ },
21
+ "./spec": {
22
+ "types": "./dist/spec/index.d.ts",
23
+ "import": "./dist/spec/index.js"
24
+ },
25
+ "./linter": {
26
+ "types": "./dist/linter/index.d.ts",
27
+ "import": "./dist/linter/index.js"
28
+ },
29
+ "./cdc": {
30
+ "types": "./dist/cdc/index.d.ts",
31
+ "import": "./dist/cdc/index.js"
20
32
  }
21
33
  },
22
34
  "bin": {
@@ -41,9 +53,36 @@
41
53
  "gen:cli": "cli-contracts generate",
42
54
  "gen:cli:check": "cli-contracts generate --dry-run",
43
55
  "clean": "rm -rf dist",
56
+ "size": "size-limit",
57
+ "size:why": "size-limit --why",
44
58
  "docker:up": "docker compose -f docker-compose.test.yml up -d",
45
59
  "docker:down": "docker compose -f docker-compose.test.yml down"
46
60
  },
61
+ "size-limit": [
62
+ {
63
+ "name": "runtime core (DDBModel + authoring)",
64
+ "path": "dist/index.js",
65
+ "import": "{ DDBModel, model, string, number, key, k, executeQuery, executeList, executeExplain }",
66
+ "limit": "42 KB"
67
+ },
68
+ {
69
+ "name": "runtime core (import *)",
70
+ "path": "dist/index.js",
71
+ "limit": "80 KB"
72
+ },
73
+ {
74
+ "name": "graphddb/testing",
75
+ "path": "dist/testing/index.js",
76
+ "import": "{ createTestDB }",
77
+ "limit": "12 KB"
78
+ },
79
+ {
80
+ "name": "graphddb/spec (build-time)",
81
+ "path": "dist/spec/index.js",
82
+ "import": "{ buildBridgeBundle }",
83
+ "limit": "20 KB"
84
+ }
85
+ ],
47
86
  "peerDependencies": {
48
87
  "@aws-sdk/client-dynamodb": "^3.0.0",
49
88
  "@aws-sdk/lib-dynamodb": "^3.0.0",
@@ -63,11 +102,13 @@
63
102
  "devDependencies": {
64
103
  "@aws-sdk/client-dynamodb": "^3.700.0",
65
104
  "@aws-sdk/lib-dynamodb": "^3.700.0",
105
+ "@size-limit/preset-small-lib": "^11.0.0",
66
106
  "@types/node": "^22.20.0",
67
107
  "cli-contracts": "^0.33.14",
68
108
  "dynamodb-onetable": "^2.7.7",
69
109
  "dynamodb-toolbox": "^2.9.0",
70
110
  "electrodb": "^3.9.1",
111
+ "size-limit": "^11.0.0",
71
112
  "tsup": "^8.0.0",
72
113
  "tsx": "^4.22.4",
73
114
  "typescript": "^5.5.0",
@@ -79,6 +120,11 @@
79
120
  },
80
121
  "license": "MIT",
81
122
  "dependencies": {
82
- "commander": "^15.0.0"
83
- }
123
+ "commander": "^15.0.0",
124
+ "handlebars": "^4.7.9"
125
+ },
126
+ "optionalDependencies": {
127
+ "typescript": "^5.5.0"
128
+ },
129
+ "sideEffects": false
84
130
  }