@webpieces/nx-webpieces-rules 0.3.314 → 0.3.320

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.
@@ -1 +1 @@
1
- {"version":3,"file":"mermaid.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/mermaid.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Mermaid Emitter\n *\n * Renders the DI graph as design.md — a Mermaid `graph TD` diagram that GitHub\n * and IDEs render inline, so the DI design is reviewable in every PR. Emitted\n * in the same sorted order as design.json so diffs are equally stable.\n */\n\nimport { DiDesign, DiGraph, DiNode } from './model';\nimport { sortGraph } from './serializer';\n\n/** Mermaid node ids must be simple identifiers — map graph ids to safe ids. */\nfunction mermaidId(id: string): string {\n return id.replace(/[^A-Za-z0-9_]/g, '_');\n}\n\n/** Escape text destined for a quoted mermaid label. */\nfunction label(text: string): string {\n return text.replace(/\"/g, '#quot;');\n}\n\nfunction nodeStatement(node: DiNode): string {\n const id = mermaidId(node.id);\n // Injected as an API → contract on top, impl class in parens (leaves never set `api`).\n const text = node.api ? `${label(node.api)}<br/>(${label(node.className)})` : label(node.className);\n if (node.kind === 'controller') return ` ${id}[\"${text}\"]:::controller`;\n if (node.kind === 'apiImplementation') return ` ${id}[\"${text}\"]:::apiImpl`;\n if (node.kind === 'component') return ` ${id}[\"${text}\"]:::component`;\n if (node.kind === 'constant' || node.kind === 'dynamic') return ` ${id}([\"${text}\"])`;\n if (node.kind === 'unresolved') return ` ${id}{{\"${text} ?\"}}:::unresolved`;\n // External boundary — subroutine box (double vertical bars) + distinct class.\n if (node.kind === 'external') return ` ${id}[[\"${text}\"]]:::external`;\n return ` ${id}[\"${text}\"]`;\n}\n\nfunction graphBody(design: DiDesign): string[] {\n const lines: string[] = ['graph TD'];\n for (const node of design.nodes) {\n lines.push(nodeStatement(node));\n }\n for (const edge of design.edges) {\n const from = mermaidId(edge.from);\n const to = mermaidId(edge.to);\n // Unlabeled — the arrow alone shows the dependency; the param/field\n // name and token stay in design.json for tooling that wants them.\n lines.push(` ${from} --> ${to}`);\n }\n lines.push(' classDef controller fill:#1f6feb,color:#ffffff,stroke:#0d419d');\n lines.push(' classDef apiImpl fill:#0d9488,color:#ffffff,stroke:#0f766e');\n lines.push(' classDef component fill:#2da44e,color:#ffffff,stroke:#1a7f37');\n lines.push(' classDef unresolved fill:#f0ad4e,color:#000000,stroke:#b8860b,stroke-dasharray: 5 5');\n lines.push(' classDef external fill:#b39ddb,color:#000000,stroke:#5e35b1,stroke-width:3px');\n return lines;\n}\n\n/** One `## Root` section with the design's Mermaid diagram. */\nfunction designSection(design: DiDesign): string[] {\n const kind =\n design.rootKind === 'controller'\n ? 'controller'\n : design.rootKind === 'apiImplementation'\n ? 'api-impl'\n : design.rootKind === 'component'\n ? 'component'\n : 'root';\n const heading = `## ${design.root} — ${kind}, Level 0…${design.maxLevel}`;\n return [heading, '', '```mermaid', ...graphBody(design), '```', ''];\n}\n\n/**\n * Render the design.md content for a project's DI graph — one Mermaid diagram\n * per design (per controller, or per library top-of-DAG root), each rooted at\n * Level 0 with its injections fanning downward.\n */\nexport function toDesignMarkdown(graph: DiGraph): string {\n sortGraph(graph);\n\n const header = [\n `# DI Design Graph — ${graph.project}`,\n '',\n `> GENERATED by \\`nx run ${graph.project}:di-graph-generate\\` — do not edit by hand.`,\n `> Machine-readable version: [design.json](./design.json)`,\n '',\n `Each section below is one root's dependency tree: Level 0 is the root`,\n `(a \\`@DocumentDesign\\` or top-of-DAG class), and constructor injections fan`,\n `downward through Levels 1, 2, … A dependency shared by multiple roots`,\n `appears in each root's tree.`,\n '',\n ];\n\n if (graph.designs.length === 0) {\n return header.concat(['No DI-registered classes found in this project.', '']).join('\\n');\n }\n\n const sections: string[] = [];\n for (const design of graph.designs) {\n sections.push(...designSection(design));\n }\n\n const legend = [\n '',\n 'Edges are constructor/`inject()` dependencies (the injected param/field',\n 'name and token are in `design.json`). Rounded nodes are',\n '`toConstantValue`/`useValue` and `toDynamicValue`/`useFactory` leaves; dashed',\n 'nodes are tokens the analyzer could not resolve; double-bordered nodes are',\n 'classes from a published package outside this workspace shown as a boundary',\n 'but not expanded into their internals.',\n '',\n ];\n\n return header.concat(sections).concat(legend).join('\\n');\n}\n"]}
1
+ {"version":3,"file":"mermaid.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/mermaid.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AA8EH,4CAsCC;AAjHD,+BAAsC;AACtC,6CAAyC;AAEzC,+EAA+E;AAC/E,SAAS,SAAS,CAAC,EAAU;IACzB,OAAO,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,uDAAuD;AACvD,SAAS,KAAK,CAAC,IAAY;IACvB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IAC/B,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9B,uFAAuF;IACvF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpG,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,OAAO,EAAE,KAAK,IAAI,iBAAiB,CAAC;IAC3E,IAAI,IAAI,CAAC,IAAI,KAAK,mBAAmB;QAAE,OAAO,OAAO,EAAE,KAAK,IAAI,cAAc,CAAC;IAC/E,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,OAAO,EAAE,KAAK,IAAI,gBAAgB,CAAC;IACzE,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC;IACzF,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,OAAO,EAAE,MAAM,IAAI,oBAAoB,CAAC;IAC/E,8EAA8E;IAC9E,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,OAAO,EAAE,MAAM,IAAI,gBAAgB,CAAC;IACzE,6EAA6E;IAC7E,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,OAAO,EAAE,MAAM,IAAI,WAAW,CAAC;IAC/D,6FAA6F;IAC7F,6FAA6F;IAC7F,0FAA0F;IAC1F,IAAI,IAAA,mBAAa,EAAC,IAAI,CAAC;QAAE,OAAO,OAAO,EAAE,KAAK,IAAI,WAAW,CAAC;IAC9D,OAAO,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC;AAClC,CAAC;AAED,SAAS,SAAS,CAAC,MAAgB;IAC/B,MAAM,KAAK,GAAa,CAAC,UAAU,CAAC,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,oEAAoE;QACpE,kEAAkE;QAClE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,QAAQ,EAAE,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IAChF,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAC7E,KAAK,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;IAC/E,KAAK,CAAC,IAAI,CAAC,yFAAyF,CAAC,CAAC;IACtG,KAAK,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC;IAC/F,KAAK,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;IAC1F,wFAAwF;IACxF,KAAK,CAAC,IAAI,CAAC,oGAAoG,CAAC,CAAC;IACjH,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,+DAA+D;AAC/D,SAAS,aAAa,CAAC,MAAgB;IACnC,MAAM,IAAI,GACN,MAAM,CAAC,QAAQ,KAAK,YAAY;QAC5B,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,mBAAmB;YACvC,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,WAAW;gBAC/B,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,MAAM,CAAC;IACrB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,MAAM,IAAI,aAAa,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC1E,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,KAAc;IAC3C,IAAA,sBAAS,EAAC,KAAK,CAAC,CAAC;IAEjB,MAAM,MAAM,GAAG;QACX,uBAAuB,KAAK,CAAC,OAAO,EAAE;QACtC,EAAE;QACF,2BAA2B,KAAK,CAAC,OAAO,6CAA6C;QACrF,0DAA0D;QAC1D,EAAE;QACF,uEAAuE;QACvE,6EAA6E;QAC7E,uEAAuE;QACvE,8BAA8B;QAC9B,EAAE;KACL,CAAC;IAEF,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,iDAAiD,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACjC,QAAQ,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,MAAM,GAAG;QACX,EAAE;QACF,yEAAyE;QACzE,yDAAyD;QACzD,+EAA+E;QAC/E,4EAA4E;QAC5E,8EAA8E;QAC9E,kFAAkF;QAClF,6EAA6E;QAC7E,EAAE;KACL,CAAC;IAEF,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC","sourcesContent":["/**\n * Mermaid Emitter\n *\n * Renders the DI graph as design.md — a Mermaid `graph TD` diagram that GitHub\n * and IDEs render inline, so the DI design is reviewable in every PR. Emitted\n * in the same sorted order as design.json so diffs are equally stable.\n */\n\nimport { DiDesign, DiGraph, DiNode } from './model';\nimport { isStackedNode } from './dot';\nimport { sortGraph } from './serializer';\n\n/** Mermaid node ids must be simple identifiers — map graph ids to safe ids. */\nfunction mermaidId(id: string): string {\n return id.replace(/[^A-Za-z0-9_]/g, '_');\n}\n\n/** Escape text destined for a quoted mermaid label. */\nfunction label(text: string): string {\n return text.replace(/\"/g, '#quot;');\n}\n\nfunction nodeStatement(node: DiNode): string {\n const id = mermaidId(node.id);\n // Injected as an API → contract on top, impl class in parens (leaves never set `api`).\n const text = node.api ? `${label(node.api)}<br/>(${label(node.className)})` : label(node.className);\n if (node.kind === 'controller') return ` ${id}[\"${text}\"]:::controller`;\n if (node.kind === 'apiImplementation') return ` ${id}[\"${text}\"]:::apiImpl`;\n if (node.kind === 'component') return ` ${id}[\"${text}\"]:::component`;\n if (node.kind === 'constant' || node.kind === 'dynamic') return ` ${id}([\"${text}\"])`;\n if (node.kind === 'unresolved') return ` ${id}{{\"${text} ?\"}}:::unresolved`;\n // External boundary — subroutine box (double vertical bars) + distinct class.\n if (node.kind === 'external') return ` ${id}[[\"${text}\"]]:::external`;\n // API-client proxy boundary — stadium subroutine (rounded) + distinct class.\n if (node.kind === 'api') return ` ${id}[[\"${text}\"]]:::api`;\n // Transient (1-to-many). A plain box with a dashed accent, NOT mermaid's `@{ shape: procs }`\n // stack: that needs mermaid >= 11.3 and design.md is rendered by whatever version the viewer\n // ships (GitHub, an IDE preview) — this repo pins none. design.html draws the real stack.\n if (isStackedNode(node)) return ` ${id}[\"${text}\"]:::many`;\n return ` ${id}[\"${text}\"]`;\n}\n\nfunction graphBody(design: DiDesign): string[] {\n const lines: string[] = ['graph TD'];\n for (const node of design.nodes) {\n lines.push(nodeStatement(node));\n }\n for (const edge of design.edges) {\n const from = mermaidId(edge.from);\n const to = mermaidId(edge.to);\n // Unlabeled — the arrow alone shows the dependency; the param/field\n // name and token stay in design.json for tooling that wants them.\n lines.push(` ${from} --> ${to}`);\n }\n lines.push(' classDef controller fill:#1f6feb,color:#ffffff,stroke:#0d419d');\n lines.push(' classDef apiImpl fill:#0d9488,color:#ffffff,stroke:#0f766e');\n lines.push(' classDef component fill:#2da44e,color:#ffffff,stroke:#1a7f37');\n lines.push(' classDef unresolved fill:#f0ad4e,color:#000000,stroke:#b8860b,stroke-dasharray: 5 5');\n lines.push(' classDef external fill:#b39ddb,color:#000000,stroke:#5e35b1,stroke-width:3px');\n lines.push(' classDef api fill:#4fc3f7,color:#000000,stroke:#0277bd,stroke-width:3px');\n // TRANSIENT: every arrow in resolves its own instance (a singleton's arrows share one).\n lines.push(' classDef many fill:#eceff1,color:#000000,stroke:#546e7a,stroke-width:2px,stroke-dasharray: 4 2');\n return lines;\n}\n\n/** One `## Root` section with the design's Mermaid diagram. */\nfunction designSection(design: DiDesign): string[] {\n const kind =\n design.rootKind === 'controller'\n ? 'controller'\n : design.rootKind === 'apiImplementation'\n ? 'api-impl'\n : design.rootKind === 'component'\n ? 'component'\n : 'root';\n const heading = `## ${design.root} — ${kind}, Level 0…${design.maxLevel}`;\n return [heading, '', '```mermaid', ...graphBody(design), '```', ''];\n}\n\n/**\n * Render the design.md content for a project's DI graph — one Mermaid diagram\n * per design (per controller, or per library top-of-DAG root), each rooted at\n * Level 0 with its injections fanning downward.\n */\nexport function toDesignMarkdown(graph: DiGraph): string {\n sortGraph(graph);\n\n const header = [\n `# DI Design Graph — ${graph.project}`,\n '',\n `> GENERATED by \\`nx run ${graph.project}:di-graph-generate\\` — do not edit by hand.`,\n `> Machine-readable version: [design.json](./design.json)`,\n '',\n `Each section below is one root's dependency tree: Level 0 is the root`,\n `(a \\`@DocumentDesign\\` or top-of-DAG class), and constructor injections fan`,\n `downward through Levels 1, 2, … A dependency shared by multiple roots`,\n `appears in each root's tree.`,\n '',\n ];\n\n if (graph.designs.length === 0) {\n return header.concat(['No DI-registered classes found in this project.', '']).join('\\n');\n }\n\n const sections: string[] = [];\n for (const design of graph.designs) {\n sections.push(...designSection(design));\n }\n\n const legend = [\n '',\n 'Edges are constructor/`inject()` dependencies (the injected param/field',\n 'name and token are in `design.json`). Rounded nodes are',\n '`toConstantValue`/`useValue` and `toDynamicValue`/`useFactory` leaves; dashed',\n 'nodes are tokens the analyzer could not resolve; double-bordered nodes are',\n 'boundaries shown but not expanded: violet = a class from a published package',\n 'outside this workspace; cyan = a generated API-client proxy (`createApiClient`),',\n 'i.e. a service/network boundary whose remote impl lives in another process.',\n '',\n ];\n\n return header.concat(sections).concat(legend).join('\\n');\n}\n"]}
@@ -14,9 +14,16 @@
14
14
  * data-only structures.
15
15
  */
16
16
  import type * as ts from 'typescript';
17
- export type DiNodeKind = 'controller' | 'apiImplementation' | 'component' | 'class' | 'constant' | 'dynamic' | 'unresolved' | 'external';
17
+ export type DiNodeKind = 'controller' | 'apiImplementation' | 'component' | 'class' | 'constant' | 'dynamic' | 'unresolved' | 'external' | 'api';
18
18
  export type DiInjectionKind = 'token' | 'type' | 'multiInject';
19
- export type DiScope = 'singleton' | 'transient' | 'unknown';
19
+ /**
20
+ * There is no 'unknown'. A class with no explicit `inSingletonScope()` IS transient at runtime:
21
+ * inversify's `DEFAULT_DEFAULT_SCOPE` is Transient and no `new Container(...)` in this workspace
22
+ * overrides `defaultScope`. Reporting 'unknown' hid that fact — and the difference matters,
23
+ * because two arrows into a singleton share one instance while two arrows into a transient do not.
24
+ * Renderers draw transient nodes as a STACK of boxes for exactly that reason.
25
+ */
26
+ export type DiScope = 'singleton' | 'transient';
20
27
  export type BindingKind = 'to' | 'toSelf' | 'toConstantValue' | 'toDynamicValue' | 'decorator';
21
28
  /**
22
29
  * A node in the DI graph — a class, a constant/dynamic binding leaf, or an
@@ -121,5 +128,13 @@ export declare class Binding {
121
128
  * MutableContextStore). Empty for every Inversify binding.
122
129
  */
123
130
  factoryDeps: TokenRef[];
124
- constructor(tokenKey: string, tokenDisplay: string, kind: BindingKind, scope: DiScope, implClass: ts.ClassDeclaration | null, valueText: string, file: string, factoryDeps?: TokenRef[]);
131
+ /**
132
+ * True when the bound expression is a `createApiClient(SomeApi, ...)` proxy
133
+ * over an @ApiPath contract. The walker renders this as an `api` boundary
134
+ * leaf and STOPS — it does not expand the factory's `deps` (which are only
135
+ * the client's own transport config, e.g. ClientConfig), matching the way
136
+ * {@link isExternalClass} stops at a published-package boundary.
137
+ */
138
+ isApiBoundary: boolean;
139
+ constructor(tokenKey: string, tokenDisplay: string, kind: BindingKind, scope: DiScope, implClass: ts.ClassDeclaration | null, valueText: string, file: string, factoryDeps?: TokenRef[], isApiBoundary?: boolean);
125
140
  }
@@ -156,7 +156,15 @@ class Binding {
156
156
  * MutableContextStore). Empty for every Inversify binding.
157
157
  */
158
158
  factoryDeps;
159
- constructor(tokenKey, tokenDisplay, kind, scope, implClass, valueText, file, factoryDeps = []) {
159
+ /**
160
+ * True when the bound expression is a `createApiClient(SomeApi, ...)` proxy
161
+ * over an @ApiPath contract. The walker renders this as an `api` boundary
162
+ * leaf and STOPS — it does not expand the factory's `deps` (which are only
163
+ * the client's own transport config, e.g. ClientConfig), matching the way
164
+ * {@link isExternalClass} stops at a published-package boundary.
165
+ */
166
+ isApiBoundary;
167
+ constructor(tokenKey, tokenDisplay, kind, scope, implClass, valueText, file, factoryDeps = [], isApiBoundary = false) {
160
168
  this.tokenKey = tokenKey;
161
169
  this.tokenDisplay = tokenDisplay;
162
170
  this.kind = kind;
@@ -165,6 +173,7 @@ class Binding {
165
173
  this.valueText = valueText;
166
174
  this.file = file;
167
175
  this.factoryDeps = factoryDeps;
176
+ this.isApiBoundary = isApiBoundary;
168
177
  }
169
178
  }
170
179
  exports.Binding = Binding;
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/model.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DI Graph Model\n *\n * Data classes for the per-project Inversify dependency DAG that is generated\n * into <projectRoot>/design.json and <projectRoot>/design.md on every build.\n *\n * A project's design.json is a container (`DiGraph`) holding an ARRAY of\n * per-root designs (`DiDesign`) — one self-contained downward tree per\n * @DocumentDesign class (or, for design-root-less library projects, per top-of-DAG\n * class). Each node in a design carries its `level` = BFS depth from that\n * design's root (root = level 0, its direct injections = level 1, and so on).\n *\n * All structures are classes (not interfaces) per the repo convention for\n * data-only structures.\n */\n\nimport type * as ts from 'typescript';\n\nexport type DiNodeKind =\n | 'controller'\n | 'apiImplementation'\n | 'component'\n | 'class'\n | 'constant'\n | 'dynamic'\n | 'unresolved'\n | 'external';\n\nexport type DiInjectionKind = 'token' | 'type' | 'multiInject';\n\nexport type DiScope = 'singleton' | 'transient' | 'unknown';\n\nexport type BindingKind = 'to' | 'toSelf' | 'toConstantValue' | 'toDynamicValue' | 'decorator';\n\n/**\n * A node in the DI graph — a class, a constant/dynamic binding leaf, or an\n * unresolved token placeholder.\n */\nexport class DiNode {\n id: string;\n className: string;\n kind: DiNodeKind;\n scope: DiScope;\n file: string;\n /** BFS depth from the design's root (root = 0). Filled in after the walk. */\n level: number;\n /**\n * Secondary detail for constant/dynamic/unresolved leaves whose box is now\n * labeled by the declared param type (see B0): the bound expression text\n * (`toConstantValue`/`toDynamicValue`/`.to(X)` source) or resolving token.\n * Omitted (undefined → not serialized) for class/controller/component nodes.\n */\n detail?: string;\n /**\n * The declared API/interface type this class was injected AS, when it differs\n * from the impl class name — e.g. injected `FirestoreAdminApi`, resolved\n * `.to(FirestoreAdminClient)`. Renderers show it as the primary box label with\n * `className` in parens beneath. Set on first reach; omitted (not serialized)\n * for inject-by-type nodes (declared type == class) and leaves.\n */\n api?: string;\n\n constructor(\n id: string,\n className: string,\n kind: DiNodeKind,\n scope: DiScope,\n file: string,\n level = 0,\n detail?: string,\n ) {\n this.id = id;\n this.className = className;\n this.kind = kind;\n this.scope = scope;\n this.file = file;\n this.level = level;\n if (detail !== undefined && detail !== '') this.detail = detail;\n }\n}\n\n/**\n * A constructor-injection edge: `from` class injects `to` node.\n */\nexport class DiEdge {\n from: string;\n to: string;\n injection: DiInjectionKind;\n token: string;\n tokenKey: string;\n paramName: string;\n paramType: string;\n\n constructor(\n from: string,\n to: string,\n injection: DiInjectionKind,\n token: string,\n tokenKey: string,\n paramName: string,\n paramType: string,\n ) {\n this.from = from;\n this.to = to;\n this.injection = injection;\n this.token = token;\n this.tokenKey = tokenKey;\n this.paramName = paramName;\n this.paramType = paramType;\n }\n}\n\n/**\n * One self-contained DI design tree: a single root (a @DocumentDesign, or a\n * library top-of-DAG class) and everything it injects \"on down\". Shared\n * dependencies are duplicated across designs — each design is its own tree so\n * it can be reviewed in isolation.\n */\nexport class DiDesign {\n /** Node id of this design's root (level 0). */\n root: string;\n rootKind: DiNodeKind;\n /** Workspace-relative posix path of the root class's file. */\n file: string;\n /** Deepest level reached from the root (0 when the root has no injections). */\n maxLevel: number;\n nodes: DiNode[];\n edges: DiEdge[];\n unresolved: string[];\n\n constructor(root: string, rootKind: DiNodeKind, file: string) {\n this.root = root;\n this.rootKind = rootKind;\n this.file = file;\n this.maxLevel = 0;\n this.nodes = [];\n this.edges = [];\n this.unresolved = [];\n }\n}\n\n/**\n * The full per-project DI graph, serialized to design.json — a container over\n * one `DiDesign` per root (controller or library top-of-DAG class).\n */\nexport class DiGraph {\n schemaVersion: number;\n project: string;\n designs: DiDesign[];\n\n constructor(project: string) {\n this.schemaVersion = 2;\n this.project = project;\n this.designs = [];\n }\n}\n\n/**\n * Canonical identity of a DI token — the key links `Symbol.for('X')` token\n * definitions to their bind() sites even across packages; display is what a\n * human reads in design.md edge labels (e.g. \"TYPES.Counter\").\n */\nexport class TokenRef {\n key: string;\n display: string;\n\n constructor(key: string, display: string) {\n this.key = key;\n this.display = display;\n }\n}\n\n/**\n * One binding discovered in pass 1 — either a ContainerModule bind() call or a\n * @provideSingleton/@provideSingletonAs/@provideTransient decorator.\n */\nexport class Binding {\n tokenKey: string;\n tokenDisplay: string;\n kind: BindingKind;\n scope: DiScope;\n /** Implementation class for to/toSelf/decorator bindings; null for constant/dynamic. */\n implClass: ts.ClassDeclaration | null;\n /** Source text of the bound expression for constant/dynamic leaves; '' otherwise. */\n valueText: string;\n /** Workspace-relative posix path of the file the binding appears in. */\n file: string;\n /**\n * Angular `useFactory` deps: the declared `deps: [A, B]` tokens. The walker\n * emits an edge from the dynamic leaf to each dep so a factory's true DI\n * boundary is visible (e.g. ClientConfig's factory → EnvironmentConfig +\n * MutableContextStore). Empty for every Inversify binding.\n */\n factoryDeps: TokenRef[];\n\n constructor(\n tokenKey: string,\n tokenDisplay: string,\n kind: BindingKind,\n scope: DiScope,\n implClass: ts.ClassDeclaration | null,\n valueText: string,\n file: string,\n factoryDeps: TokenRef[] = [],\n ) {\n this.tokenKey = tokenKey;\n this.tokenDisplay = tokenDisplay;\n this.kind = kind;\n this.scope = scope;\n this.implClass = implClass;\n this.valueText = valueText;\n this.file = file;\n this.factoryDeps = factoryDeps;\n }\n}\n"]}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAgCH;;;GAGG;AACH,MAAa,MAAM;IACf,EAAE,CAAS;IACX,SAAS,CAAS;IAClB,IAAI,CAAa;IACjB,KAAK,CAAU;IACf,IAAI,CAAS;IACb,6EAA6E;IAC7E,KAAK,CAAS;IACd;;;;;OAKG;IACH,MAAM,CAAU;IAChB;;;;;;OAMG;IACH,GAAG,CAAU;IAEb,YACI,EAAU,EACV,SAAiB,EACjB,IAAgB,EAChB,KAAc,EACd,IAAY,EACZ,KAAK,GAAG,CAAC,EACT,MAAe;QAEf,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE;YAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACpE,CAAC;CACJ;AAzCD,wBAyCC;AAED;;GAEG;AACH,MAAa,MAAM;IACf,IAAI,CAAS;IACb,EAAE,CAAS;IACX,SAAS,CAAkB;IAC3B,KAAK,CAAS;IACd,QAAQ,CAAS;IACjB,SAAS,CAAS;IAClB,SAAS,CAAS;IAElB,YACI,IAAY,EACZ,EAAU,EACV,SAA0B,EAC1B,KAAa,EACb,QAAgB,EAChB,SAAiB,EACjB,SAAiB;QAEjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;CACJ;AA1BD,wBA0BC;AAED;;;;;GAKG;AACH,MAAa,QAAQ;IACjB,+CAA+C;IAC/C,IAAI,CAAS;IACb,QAAQ,CAAa;IACrB,8DAA8D;IAC9D,IAAI,CAAS;IACb,+EAA+E;IAC/E,QAAQ,CAAS;IACjB,KAAK,CAAW;IAChB,KAAK,CAAW;IAChB,UAAU,CAAW;IAErB,YAAY,IAAY,EAAE,QAAoB,EAAE,IAAY;QACxD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACzB,CAAC;CACJ;AArBD,4BAqBC;AAED;;;GAGG;AACH,MAAa,OAAO;IAChB,aAAa,CAAS;IACtB,OAAO,CAAS;IAChB,OAAO,CAAa;IAEpB,YAAY,OAAe;QACvB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACtB,CAAC;CACJ;AAVD,0BAUC;AAED;;;;GAIG;AACH,MAAa,QAAQ;IACjB,GAAG,CAAS;IACZ,OAAO,CAAS;IAEhB,YAAY,GAAW,EAAE,OAAe;QACpC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;CACJ;AARD,4BAQC;AAED;;;GAGG;AACH,MAAa,OAAO;IAChB,QAAQ,CAAS;IACjB,YAAY,CAAS;IACrB,IAAI,CAAc;IAClB,KAAK,CAAU;IACf,wFAAwF;IACxF,SAAS,CAA6B;IACtC,qFAAqF;IACrF,SAAS,CAAS;IAClB,wEAAwE;IACxE,IAAI,CAAS;IACb;;;;;OAKG;IACH,WAAW,CAAa;IACxB;;;;;;OAMG;IACH,aAAa,CAAU;IAEvB,YACI,QAAgB,EAChB,YAAoB,EACpB,IAAiB,EACjB,KAAc,EACd,SAAqC,EACrC,SAAiB,EACjB,IAAY,EACZ,cAA0B,EAAE,EAC5B,aAAa,GAAG,KAAK;QAErB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACvC,CAAC;CACJ;AAhDD,0BAgDC","sourcesContent":["/**\n * DI Graph Model\n *\n * Data classes for the per-project Inversify dependency DAG that is generated\n * into <projectRoot>/design.json and <projectRoot>/design.md on every build.\n *\n * A project's design.json is a container (`DiGraph`) holding an ARRAY of\n * per-root designs (`DiDesign`) — one self-contained downward tree per\n * @DocumentDesign class (or, for design-root-less library projects, per top-of-DAG\n * class). Each node in a design carries its `level` = BFS depth from that\n * design's root (root = level 0, its direct injections = level 1, and so on).\n *\n * All structures are classes (not interfaces) per the repo convention for\n * data-only structures.\n */\n\nimport type * as ts from 'typescript';\n\nexport type DiNodeKind =\n | 'controller'\n | 'apiImplementation'\n | 'component'\n | 'class'\n | 'constant'\n | 'dynamic'\n | 'unresolved'\n | 'external'\n // A generated API-client proxy (`createApiClient(SomeApi, ...)` over an\n // @ApiPath contract) — a service/network boundary. Rendered as a leaf; the\n // walk stops here (the remote impl lives in another process, and the client's\n // own transport config, e.g. ClientConfig, is not app architecture).\n | 'api';\n\nexport type DiInjectionKind = 'token' | 'type' | 'multiInject';\n\n/**\n * There is no 'unknown'. A class with no explicit `inSingletonScope()` IS transient at runtime:\n * inversify's `DEFAULT_DEFAULT_SCOPE` is Transient and no `new Container(...)` in this workspace\n * overrides `defaultScope`. Reporting 'unknown' hid that fact — and the difference matters,\n * because two arrows into a singleton share one instance while two arrows into a transient do not.\n * Renderers draw transient nodes as a STACK of boxes for exactly that reason.\n */\nexport type DiScope = 'singleton' | 'transient';\n\nexport type BindingKind = 'to' | 'toSelf' | 'toConstantValue' | 'toDynamicValue' | 'decorator';\n\n/**\n * A node in the DI graph — a class, a constant/dynamic binding leaf, or an\n * unresolved token placeholder.\n */\nexport class DiNode {\n id: string;\n className: string;\n kind: DiNodeKind;\n scope: DiScope;\n file: string;\n /** BFS depth from the design's root (root = 0). Filled in after the walk. */\n level: number;\n /**\n * Secondary detail for constant/dynamic/unresolved leaves whose box is now\n * labeled by the declared param type (see B0): the bound expression text\n * (`toConstantValue`/`toDynamicValue`/`.to(X)` source) or resolving token.\n * Omitted (undefined → not serialized) for class/controller/component nodes.\n */\n detail?: string;\n /**\n * The declared API/interface type this class was injected AS, when it differs\n * from the impl class name — e.g. injected `FirestoreAdminApi`, resolved\n * `.to(FirestoreAdminClient)`. Renderers show it as the primary box label with\n * `className` in parens beneath. Set on first reach; omitted (not serialized)\n * for inject-by-type nodes (declared type == class) and leaves.\n */\n api?: string;\n\n constructor(\n id: string,\n className: string,\n kind: DiNodeKind,\n scope: DiScope,\n file: string,\n level = 0,\n detail?: string,\n ) {\n this.id = id;\n this.className = className;\n this.kind = kind;\n this.scope = scope;\n this.file = file;\n this.level = level;\n if (detail !== undefined && detail !== '') this.detail = detail;\n }\n}\n\n/**\n * A constructor-injection edge: `from` class injects `to` node.\n */\nexport class DiEdge {\n from: string;\n to: string;\n injection: DiInjectionKind;\n token: string;\n tokenKey: string;\n paramName: string;\n paramType: string;\n\n constructor(\n from: string,\n to: string,\n injection: DiInjectionKind,\n token: string,\n tokenKey: string,\n paramName: string,\n paramType: string,\n ) {\n this.from = from;\n this.to = to;\n this.injection = injection;\n this.token = token;\n this.tokenKey = tokenKey;\n this.paramName = paramName;\n this.paramType = paramType;\n }\n}\n\n/**\n * One self-contained DI design tree: a single root (a @DocumentDesign, or a\n * library top-of-DAG class) and everything it injects \"on down\". Shared\n * dependencies are duplicated across designs — each design is its own tree so\n * it can be reviewed in isolation.\n */\nexport class DiDesign {\n /** Node id of this design's root (level 0). */\n root: string;\n rootKind: DiNodeKind;\n /** Workspace-relative posix path of the root class's file. */\n file: string;\n /** Deepest level reached from the root (0 when the root has no injections). */\n maxLevel: number;\n nodes: DiNode[];\n edges: DiEdge[];\n unresolved: string[];\n\n constructor(root: string, rootKind: DiNodeKind, file: string) {\n this.root = root;\n this.rootKind = rootKind;\n this.file = file;\n this.maxLevel = 0;\n this.nodes = [];\n this.edges = [];\n this.unresolved = [];\n }\n}\n\n/**\n * The full per-project DI graph, serialized to design.json — a container over\n * one `DiDesign` per root (controller or library top-of-DAG class).\n */\nexport class DiGraph {\n schemaVersion: number;\n project: string;\n designs: DiDesign[];\n\n constructor(project: string) {\n this.schemaVersion = 2;\n this.project = project;\n this.designs = [];\n }\n}\n\n/**\n * Canonical identity of a DI token — the key links `Symbol.for('X')` token\n * definitions to their bind() sites even across packages; display is what a\n * human reads in design.md edge labels (e.g. \"TYPES.Counter\").\n */\nexport class TokenRef {\n key: string;\n display: string;\n\n constructor(key: string, display: string) {\n this.key = key;\n this.display = display;\n }\n}\n\n/**\n * One binding discovered in pass 1 — either a ContainerModule bind() call or a\n * @provideSingleton/@provideSingletonAs/@provideTransient decorator.\n */\nexport class Binding {\n tokenKey: string;\n tokenDisplay: string;\n kind: BindingKind;\n scope: DiScope;\n /** Implementation class for to/toSelf/decorator bindings; null for constant/dynamic. */\n implClass: ts.ClassDeclaration | null;\n /** Source text of the bound expression for constant/dynamic leaves; '' otherwise. */\n valueText: string;\n /** Workspace-relative posix path of the file the binding appears in. */\n file: string;\n /**\n * Angular `useFactory` deps: the declared `deps: [A, B]` tokens. The walker\n * emits an edge from the dynamic leaf to each dep so a factory's true DI\n * boundary is visible (e.g. ClientConfig's factory → EnvironmentConfig +\n * MutableContextStore). Empty for every Inversify binding.\n */\n factoryDeps: TokenRef[];\n /**\n * True when the bound expression is a `createApiClient(SomeApi, ...)` proxy\n * over an @ApiPath contract. The walker renders this as an `api` boundary\n * leaf and STOPS — it does not expand the factory's `deps` (which are only\n * the client's own transport config, e.g. ClientConfig), matching the way\n * {@link isExternalClass} stops at a published-package boundary.\n */\n isApiBoundary: boolean;\n\n constructor(\n tokenKey: string,\n tokenDisplay: string,\n kind: BindingKind,\n scope: DiScope,\n implClass: ts.ClassDeclaration | null,\n valueText: string,\n file: string,\n factoryDeps: TokenRef[] = [],\n isApiBoundary = false,\n ) {\n this.tokenKey = tokenKey;\n this.tokenDisplay = tokenDisplay;\n this.kind = kind;\n this.scope = scope;\n this.implClass = implClass;\n this.valueText = valueText;\n this.file = file;\n this.factoryDeps = factoryDeps;\n this.isApiBoundary = isApiBoundary;\n }\n}\n"]}