@buun_group/plotwork-mcp 0.1.0-beta.5 → 0.1.0-beta.6

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/plotwork-mcp.mjs CHANGED
@@ -51,7 +51,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
51
51
  var package_default;
52
52
  var init_package = __esm({
53
53
  "package.json"() {
54
- package_default = { version: "0.1.0-beta.5" };
54
+ package_default = { version: "0.1.0-beta.6" };
55
55
  }
56
56
  });
57
57
 
@@ -643,7 +643,7 @@ var init_view_links = __esm({
643
643
  };
644
644
  CLI_RELEASE = {
645
645
  packageName: "@buun_group/plotwork",
646
- version: "0.1.0-beta.6",
646
+ version: "0.1.0-beta.7",
647
647
  onNpm: true
648
648
  };
649
649
  }
@@ -662,7 +662,7 @@ var init_mcp_release = __esm({
662
662
  "use strict";
663
663
  MCP_RELEASE = {
664
664
  packageName: "@buun_group/plotwork-mcp",
665
- version: "0.1.0-beta.4"
665
+ version: "0.1.0-beta.5"
666
666
  };
667
667
  }
668
668
  });
@@ -6919,6 +6919,53 @@ var init_icons = __esm({
6919
6919
  }
6920
6920
  });
6921
6921
 
6922
+ // ../../packages/diagrams/src/importers/guide.ts
6923
+ function importFormatGuide(formats) {
6924
+ return formats.map((format) => ({ format, ...GUIDE[format] }));
6925
+ }
6926
+ var GUIDE;
6927
+ var init_guide = __esm({
6928
+ "../../packages/diagrams/src/importers/guide.ts"() {
6929
+ "use strict";
6930
+ GUIDE = {
6931
+ sql: { reads: "CREATE TABLE and ALTER TABLE of Postgres, MySQL, and SQLite; a migration history is applied in order.", draws: "schema", options: "tables: names or prefix* to draw." },
6932
+ prisma: { reads: "schema.prisma models and their relations.", draws: "schema", options: "tables." },
6933
+ drizzle: { reads: "pgTable, sqliteTable, and mysqlTable calls, as text.", draws: "schema", options: "tables." },
6934
+ openapi: { reads: "An OpenAPI 3 document in JSON or YAML.", draws: "matrix of endpoints against schemes, scopes, and roles (view endpoints), tree by tag (view map), or sequence of one operation (view sequence)", options: "view; operationId for view sequence; paths and tags to read part of it." },
6935
+ terraform: { reads: "The .tf and .tfvars files of a stack with its local modules and environments; send every file in files.", draws: "flow framed by module, nested in groups, networks, and subnets", options: "groupBy module, network, or file; root: the root module of several (envs/prod); varFiles." },
6936
+ "terraform-plan": { reads: "The JSON of terraform show -json for a plan or a state, or a raw terraform.tfstate.", draws: "flow as terraform, with planned changes as tones and words", options: "groupBy." },
6937
+ bicep: { reads: ".bicep files with their modules, and .bicepparam values.", draws: "flow as terraform", options: "groupBy; root; varFiles: the .bicepparam file." },
6938
+ arm: { reads: "An ARM template and its parameters file.", draws: "flow as terraform", options: "groupBy; varFiles: the parameters file." },
6939
+ pulumi: { reads: "A Pulumi YAML program with Pulumi.<stack>.yaml, or pulumi stack export or pulumi preview --json of any language.", draws: "flow as terraform, components as frames", options: "groupBy; root; varFiles: the stack file." },
6940
+ cloudformation: { reads: "A CloudFormation or SAM template in YAML or JSON.", draws: "flow as terraform", options: "groupBy; root: one template of several." },
6941
+ cdk: { reads: "The cdk.out folder cdk synth writes (manifest.json, tree.json, templates, nested stacks), or one template it prints.", draws: "flow as cloudformation, stacks, nested stacks, and constructs as frames", options: "root: one stack of several." },
6942
+ compose: { reads: "A Docker Compose file.", draws: "flow of services, networks, and named volumes", options: "none." },
6943
+ kubernetes: { reads: "Multi-document manifests; send a folder of them in files.", draws: "flow of workloads, Services, Ingresses, ConfigMaps, Secrets, and claims, namespaces as frames", options: "none." },
6944
+ helm: { reads: "The output of helm template, or a chart folder (Chart.yaml, charts/, Chart.lock, values.yaml).", draws: "flow as kubernetes framed by release and chart, or a flow of charts and their dependencies", options: "none." },
6945
+ kustomize: { reads: "The output of kustomize build, or a folder of kustomization.yaml files with what they list.", draws: "flow as kubernetes, or a flow of overlays, bases, and components", options: "none." },
6946
+ backstage: { reads: "Backstage catalog descriptors (catalog-info.yaml), one file or a folder.", draws: "flow of components, APIs, and resources in systems and domains", options: "groupBy owner: frame by the owning group instead." },
6947
+ "github-actions": { reads: "A GitHub Actions workflow file.", draws: "flow of the trigger, the jobs in needs order, and their steps", options: "none." },
6948
+ csv: { reads: "A CSV, TSV, or semicolon-separated table.", draws: "the chart its shape suits: xychart, pie, sankey, treemap, or matrix", options: "chart: auto or one of those." },
6949
+ json: { reads: "Records, rows, a name to number object, or nested objects of numbers.", draws: "the same charts as csv", options: "chart." },
6950
+ tree: { reads: "tree output, a list of paths, or an indented outline.", draws: "tree", options: "none." },
6951
+ "git-log": { reads: "git log --oneline --graph --decorate, plain git log, or --oneline.", draws: "git graph (view git) or timeline (view timeline)", options: "view." }
6952
+ };
6953
+ }
6954
+ });
6955
+
6956
+ // ../../packages/diagrams/src/importers/types.ts
6957
+ var IMPORT_FORMATS, IMPORT_VIEWS, IMPORT_CHARTS, IMPORT_GROUP_BY, NO_STATS;
6958
+ var init_types2 = __esm({
6959
+ "../../packages/diagrams/src/importers/types.ts"() {
6960
+ "use strict";
6961
+ IMPORT_FORMATS = ["sql", "prisma", "drizzle", "openapi", "terraform", "terraform-plan", "bicep", "arm", "pulumi", "cloudformation", "cdk", "compose", "kubernetes", "helm", "kustomize", "backstage", "github-actions", "csv", "json", "tree", "git-log"];
6962
+ IMPORT_VIEWS = ["endpoints", "map", "sequence", "git", "timeline"];
6963
+ IMPORT_CHARTS = ["auto", "xychart", "pie", "sankey", "treemap", "matrix"];
6964
+ IMPORT_GROUP_BY = ["module", "network", "file", "owner"];
6965
+ NO_STATS = { items: 0, links: 0, groups: 0, dropped: 0 };
6966
+ }
6967
+ });
6968
+
6922
6969
  // ../../packages/diagrams/src/parse/layout-budget.ts
6923
6970
  function checkLayoutBudget(diagram, at3) {
6924
6971
  const { maxFlowNodes, maxLayerCrossings } = LAYOUT_LIMITS;
@@ -115955,9 +116002,9 @@ function readAttributes(text3, startAt) {
115955
116002
  let colon = end;
115956
116003
  while (colon < text3.length && isSpace3(text3.charAt(colon))) colon += 1;
115957
116004
  if (end === at3 || text3.charAt(colon) !== ":") return { attributes: attributes2, leftover: text3.slice(at3).trim(), leftoverAt: at3 };
115958
- let valueAt2 = colon + 1;
115959
- while (valueAt2 < text3.length && isSpace3(text3.charAt(valueAt2))) valueAt2 += 1;
115960
- const read = readValue(text3, valueAt2);
116005
+ let valueAt3 = colon + 1;
116006
+ while (valueAt3 < text3.length && isSpace3(text3.charAt(valueAt3))) valueAt3 += 1;
116007
+ const read = readValue(text3, valueAt3);
115961
116008
  attributes2.push({ key: text3.slice(at3, end).toLowerCase(), value: read.value, column: at3 + 1 });
115962
116009
  at3 = read.end;
115963
116010
  }
@@ -139121,8 +139168,19 @@ function kindOf(full, detail) {
139121
139168
  examples: full.examples.filter((example) => example.kind === detail.kind)
139122
139169
  };
139123
139170
  }
139124
- function buildSyntax(kind) {
139171
+ function importFormatsOf(full) {
139172
+ return {
139173
+ answer: "import-formats",
139174
+ howToGetAKind: "This is the topic import-formats: every format import_source reads, what it reads, the diagram it draws, and the options it takes. Send a file's text, or every file of a folder in files, to import_source with its format. Call get_diagram_syntax with no kind and no topic for the language.",
139175
+ kinds: full.kinds,
139176
+ kindDetails: [],
139177
+ examples: [],
139178
+ importFormats: importFormatGuide(IMPORT_FORMATS)
139179
+ };
139180
+ }
139181
+ function buildSyntax(kind, topic) {
139125
139182
  const full = buildVocabulary();
139183
+ if (topic === "import-formats") return importFormatsOf(full);
139126
139184
  const detail = full.kindDetails.find((entry2) => entry2.kind === kind);
139127
139185
  return detail === void 0 ? overviewOf(full) : kindOf(full, detail);
139128
139186
  }
@@ -139133,6 +139191,8 @@ var init_build_vocabulary = __esm({
139133
139191
  init_constants();
139134
139192
  init_core();
139135
139193
  init_icons();
139194
+ init_guide();
139195
+ init_types2();
139136
139196
  init_registry2();
139137
139197
  init_theme();
139138
139198
  init_examples_generated();
@@ -178041,19 +178101,6 @@ var init_schema3 = __esm({
178041
178101
  }
178042
178102
  });
178043
178103
 
178044
- // ../../packages/diagrams/src/importers/types.ts
178045
- var IMPORT_FORMATS, IMPORT_VIEWS, IMPORT_CHARTS, IMPORT_GROUP_BY, NO_STATS;
178046
- var init_types2 = __esm({
178047
- "../../packages/diagrams/src/importers/types.ts"() {
178048
- "use strict";
178049
- IMPORT_FORMATS = ["sql", "prisma", "drizzle", "openapi", "terraform", "terraform-plan", "bicep", "arm", "pulumi", "cloudformation", "compose", "kubernetes", "github-actions", "csv", "json", "tree", "git-log"];
178050
- IMPORT_VIEWS = ["endpoints", "map", "sequence", "git", "timeline"];
178051
- IMPORT_CHARTS = ["auto", "xychart", "pie", "sankey", "treemap", "matrix"];
178052
- IMPORT_GROUP_BY = ["module", "network", "file"];
178053
- NO_STATS = { items: 0, links: 0, groups: 0, dropped: 0 };
178054
- }
178055
- });
178056
-
178057
178104
  // ../../packages/diagrams/src/settings/sources.ts
178058
178105
  var repositoryPath, pattern2, outPath, SourceOptionsSchema, SourcePublishSchema, SourceSchema;
178059
178106
  var init_sources = __esm({
@@ -178074,9 +178121,9 @@ var init_sources = __esm({
178074
178121
  tables: external_exports.array(external_exports.string().min(1).max(200)).max(200).optional().describe("sql, prisma and drizzle: the tables to draw, each a name or a prefix ending in * (org_*)."),
178075
178122
  paths: external_exports.array(external_exports.string().min(1).max(200)).max(200).optional().describe("openapi: the path prefixes to read, such as /orders."),
178076
178123
  tags: external_exports.array(external_exports.string().min(1).max(200)).max(200).optional().describe("openapi: the tags to read."),
178077
- groupBy: external_exports.enum(IMPORT_GROUP_BY).optional().describe("terraform, bicep, arm, pulumi, cloudformation: module (the default) frames each module's resources and nests them by what they name; network nests by what they name alone; file (terraform) frames them by their .tf file."),
178124
+ groupBy: external_exports.enum(IMPORT_GROUP_BY).optional().describe("terraform, bicep, arm, pulumi, cloudformation, cdk: module (the default) frames each module's resources and nests them by what they name; network nests by what they name alone; file (terraform) frames them by their .tf file; owner (backstage) frames a catalog by the group that owns each entity."),
178078
178125
  varFiles: external_exports.array(repositoryPath).max(20).optional().describe("terraform: the variables files to apply, as -var-file would; bicep: the .bicepparam file; arm: the parameters file; pulumi: the Pulumi.<stack>.yaml file."),
178079
- root: repositoryPath.optional().describe("terraform, bicep, pulumi, cloudformation: the root module, deployment, program, or template to draw when the files hold several, such as envs/prod; each is a part of its own when left out.")
178126
+ root: repositoryPath.optional().describe("terraform, bicep, pulumi, cloudformation, cdk: the root module, deployment, program, template, or stack to draw when the files hold several, such as envs/prod; each is a part of its own when left out.")
178080
178127
  }).meta({ id: "SourceOptions", description: "The reader options of one source, as import_source takes them." });
178081
178128
  SourcePublishSchema = external_exports.strictObject({
178082
178129
  alias: external_exports.string().min(1).max(60).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "An alias is lower-case words joined by single hyphens, such as checkout-flow").optional().describe("A stable link of a personal key: one address that always shows the latest version."),
@@ -178418,7 +178465,7 @@ var init_icons2 = __esm({
178418
178465
  });
178419
178466
 
178420
178467
  // ../../packages/diagrams/src/schemas/vocabulary.ts
178421
- var NodeTypeSchema, StatusIconSchema, ExampleSchema, GroupTypeSchema, GroupSchema, OverlaySchema, IconProviderSchema, LabelIconSchema, AutoIconsSchema, ArrowSchema, LineOptionSchema, KindStatementSchema, KindLimitSchema, KindDetailsSchema, DiagnosticCodeEntrySchema, VocabularyResponseSchema, SyntaxKindSchema, SyntaxQuerySchema, SyntaxInputSchema, SyntaxKindDetailsSchema, SyntaxTablesSchema, SyntaxResponseSchema;
178468
+ var NodeTypeSchema, StatusIconSchema, ExampleSchema, GroupTypeSchema, GroupSchema, OverlaySchema, IconProviderSchema, LabelIconSchema, AutoIconsSchema, ArrowSchema, LineOptionSchema, KindStatementSchema, KindLimitSchema, KindDetailsSchema, DiagnosticCodeEntrySchema, VocabularyResponseSchema, SyntaxKindSchema, SYNTAX_TOPICS, SyntaxTopicSchema, SyntaxQuerySchema, SyntaxInputSchema, SyntaxKindDetailsSchema, SyntaxTablesSchema, SyntaxResponseSchema;
178422
178469
  var init_vocabulary18 = __esm({
178423
178470
  "../../packages/diagrams/src/schemas/vocabulary.ts"() {
178424
178471
  "use strict";
@@ -178557,21 +178604,24 @@ var init_vocabulary18 = __esm({
178557
178604
  examples: external_exports.array(ExampleSchema)
178558
178605
  }).meta({ id: "VocabularyResponse", description: "The diagram language: kinds, node types, arrows, flags, error codes, limits, and examples." });
178559
178606
  SyntaxKindSchema = external_exports.enum(BUILT_KINDS).optional().describe("The one kind to read in full. Omit it for the overview: the language every kind shares and one line about each kind.");
178560
- SyntaxQuerySchema = external_exports.object({ kind: SyntaxKindSchema }).meta({ id: "SyntaxQuery", description: "The kind to read in full, or none for the overview." });
178561
- SyntaxInputSchema = external_exports.object({ kind: SyntaxKindSchema }).strict();
178607
+ SYNTAX_TOPICS = ["import-formats"];
178608
+ SyntaxTopicSchema = external_exports.enum(SYNTAX_TOPICS).optional().describe("import-formats: what import_source reads, draws, and takes, a line each.");
178609
+ SyntaxQuerySchema = external_exports.object({ kind: SyntaxKindSchema, topic: SyntaxTopicSchema }).meta({ id: "SyntaxQuery", description: "The kind to read in full, or none for the overview, or a topic." });
178610
+ SyntaxInputSchema = external_exports.object({ kind: SyntaxKindSchema, topic: SyntaxTopicSchema }).strict();
178562
178611
  SyntaxKindDetailsSchema = KindDetailsSchema.partial({ statements: true, limits: true }).extend({
178563
178612
  more: external_exports.string().optional().describe("Only in the overview, where statements and limits are left out: what a call with this kind returns that the overview does not, and how to ask for it.")
178564
178613
  });
178565
178614
  SyntaxTablesSchema = VocabularyResponseSchema.omit({ kinds: true, kindDetails: true, examples: true }).partial();
178566
178615
  SyntaxResponseSchema = external_exports.object({
178567
178616
  answer: openKindSchema(
178568
- `What this answer is: overview, or the kind it describes in full (${joinWords(BUILT_KINDS, "or")} today; later versions add kinds, so expect others).`
178617
+ `What this answer is: overview, a topic (import-formats), or the kind it describes in full (${joinWords(BUILT_KINDS, "or")} today; later versions add kinds, so expect others).`
178569
178618
  ),
178570
178619
  howToGetAKind: external_exports.string().describe("In plain words: what this answer holds and how to get the rest."),
178571
178620
  kinds: VocabularyResponseSchema.shape.kinds,
178572
178621
  kindDetails: external_exports.array(SyntaxKindDetailsSchema).describe("The overview lists every kind in the order of kinds, with its header, noun, and summary, and a pointer in more. A kind answer holds that kind alone, in full."),
178573
178622
  ...SyntaxTablesSchema.shape,
178574
- examples: VocabularyResponseSchema.shape.examples.describe("Tested examples: the overview holds the ones for the kinds the shared tables describe in full, a kind answer holds its own.")
178623
+ examples: VocabularyResponseSchema.shape.examples.describe("Tested examples: the overview holds the ones for the kinds the shared tables describe in full, a kind answer holds its own."),
178624
+ importFormats: external_exports.array(external_exports.object({ format: external_exports.string().describe("The format, as import_source takes it."), reads: external_exports.string().describe("What it reads."), draws: external_exports.string().describe("The diagram it draws."), options: external_exports.string().describe("The options it takes.") })).optional().describe("Only for topic import-formats: every format import_source reads, a line each.")
178575
178625
  }).meta({ id: "SyntaxResponse", description: "The diagram language for one scope: the overview, or one kind in full." });
178576
178626
  }
178577
178627
  });
@@ -178660,7 +178710,7 @@ var init_import_source = __esm({
178660
178710
  init_kinds2();
178661
178711
  init_workflows();
178662
178712
  ImportSourceInputSchema = external_exports.object({
178663
- format: external_exports.enum(IMPORT_FORMATS).describe("What the text is. sql, prisma, drizzle: a schema. openapi: endpoints or a sequence. terraform, terraform-plan (terraform show -json), bicep, arm, pulumi, cloudformation, compose, kubernetes, github-actions: a flow. csv, json: a chart. tree: a tree. git-log: a git graph or timeline."),
178713
+ format: external_exports.enum(IMPORT_FORMATS).describe("What the text is; get_diagram_syntax with topic import-formats describes each."),
178664
178714
  // Neither text nor files has a length here: a call over the limits is answered with I102 and a hint, as data.
178665
178715
  text: external_exports.string().optional().describe("The file content, as text. Send text or files, not both."),
178666
178716
  files: external_exports.array(
@@ -178668,19 +178718,19 @@ var init_import_source = __esm({
178668
178718
  path: external_exports.string().min(1).max(WORKFLOW_LIMITS.maxPathLength).describe("The file path, such as infra/network.tf; diagnostics name it."),
178669
178719
  text: external_exports.string().describe("The file content, as text.")
178670
178720
  })
178671
- ).optional().describe("The files of a folder, for a format that spans files: a Terraform or Bicep stack, SQL migrations, Kubernetes manifests. Other formats read the first file."),
178721
+ ).optional().describe("The files of a folder, for a format that spans files; the others read the first."),
178672
178722
  title: external_exports.string().max(200).optional().describe("The diagram title; taken from the file when left out."),
178673
178723
  partsPage: external_exports.number().int().min(1).max(1e5).optional().describe(`Which page of parts to return when a split import has more than ${WORKFLOW_LIMITS.maxImportParts} diagrams: 1 (default) is the first ${WORKFLOW_LIMITS.maxImportParts}.`),
178674
178724
  options: external_exports.object({
178675
- view: external_exports.enum(IMPORT_VIEWS).optional().describe("openapi: endpoints (default), map (endpoints by tag), or sequence. git-log: git or timeline."),
178676
- operationId: external_exports.string().max(200).optional().describe("openapi sequence: the operation to draw."),
178677
- chart: external_exports.enum(IMPORT_CHARTS).optional().describe("csv, json: auto (default) picks from the shape of the table."),
178678
- tables: external_exports.array(external_exports.string().min(1).max(80)).max(40).optional().describe("sql, prisma, drizzle: tables to draw, by name or prefix* (org_*); for a big schema."),
178679
- paths: external_exports.array(external_exports.string().min(1).max(200)).max(40).optional().describe("openapi: path prefixes to read, such as /orders; for a big API."),
178680
- tags: external_exports.array(external_exports.string().min(1).max(80)).max(40).optional().describe("openapi: tags to read; an operation with any of them is read."),
178681
- varFiles: external_exports.array(external_exports.string().min(1).max(WORKFLOW_LIMITS.maxPathLength)).max(20).optional().describe("Variables files: .tfvars, .bicepparam, ARM parameters, Pulumi.<stack>.yaml."),
178682
- groupBy: external_exports.enum(IMPORT_GROUP_BY).optional().describe("module (default) frames modules; network only nests; file frames by .tf file."),
178683
- root: external_exports.string().max(WORKFLOW_LIMITS.maxPathLength).optional().describe("The root to draw of several: envs/prod.")
178725
+ view: external_exports.enum(IMPORT_VIEWS).optional().describe("The picture of a format drawn several ways."),
178726
+ operationId: external_exports.string().max(200).optional().describe("openapi sequence: the operation."),
178727
+ chart: external_exports.enum(IMPORT_CHARTS).optional().describe("csv, json: the chart; auto picks."),
178728
+ tables: external_exports.array(external_exports.string().min(1).max(80)).max(40).optional().describe("Tables to draw, by name or prefix*."),
178729
+ paths: external_exports.array(external_exports.string().min(1).max(200)).max(40).optional().describe("openapi: path prefixes to read."),
178730
+ tags: external_exports.array(external_exports.string().min(1).max(80)).max(40).optional().describe("openapi: tags to read."),
178731
+ varFiles: external_exports.array(external_exports.string().min(1).max(WORKFLOW_LIMITS.maxPathLength)).max(20).optional().describe("Variables files to apply."),
178732
+ groupBy: external_exports.enum(IMPORT_GROUP_BY).optional().describe("How things are framed; module is the default."),
178733
+ root: external_exports.string().max(WORKFLOW_LIMITS.maxPathLength).optional().describe("The root, stack, or template of several to draw.")
178684
178734
  }).strict().default({})
178685
178735
  }).meta({ id: "ImportSourceInput", description: "A file of another format to turn into Plotwork source." });
178686
178736
  StatsSchema2 = external_exports.object({
@@ -181110,204 +181160,6 @@ var init_patterns3 = __esm({
181110
181160
  }
181111
181161
  });
181112
181162
 
181113
- // ../../packages/diagrams/src/importers/detect.ts
181114
- function graphCommits(text3) {
181115
- let count3 = 0;
181116
- for (const line3 of text3.split("\n")) {
181117
- let index2 = 0;
181118
- while (index2 < line3.length && "*|\\/ _".includes(line3.charAt(index2))) index2 += 1;
181119
- if (line3.slice(0, index2).includes("*") && /^[0-9a-f]{7,40}\s/.test(line3.slice(index2))) count3 += 1;
181120
- }
181121
- return count3;
181122
- }
181123
- function everyLine(text3, test) {
181124
- const lines = filled(text3);
181125
- return lines.length >= 3 && lines.every((line3) => test(line3.trim()));
181126
- }
181127
- function isOutline(text3) {
181128
- const lines = filled(text3);
181129
- return lines.length >= 3 && lines.every((line3) => /^\s*[-*+] \S/.test(line3)) && lines.some((line3) => /^\s+[-*+] /.test(line3));
181130
- }
181131
- function separators(line3, separator) {
181132
- let count3 = 0;
181133
- let quoted5 = false;
181134
- for (const char of line3) {
181135
- if (char === '"') quoted5 = !quoted5;
181136
- else if (char === separator && !quoted5) count3 += 1;
181137
- }
181138
- return count3;
181139
- }
181140
- function isTable(text3) {
181141
- const lines = filled(text3).slice(0, 6);
181142
- if (lines.length < 2) return false;
181143
- for (const separator of [",", " ", ";"]) {
181144
- const counts = lines.map((line3) => separators(line3, separator));
181145
- if ((counts[0] ?? 0) > 0 && counts.every((count3) => count3 === counts[0])) return true;
181146
- }
181147
- return false;
181148
- }
181149
- function importFormatFor(request, data) {
181150
- const fromData = detectImportFormat(data);
181151
- if (fromData !== null && SURE_FROM_DATA.has(fromData)) return fromData;
181152
- return NAMES.find((name2) => name2.words.test(request.slice(0, SNIFF_LENGTH)))?.format ?? null;
181153
- }
181154
- function detectImportFormat(text3) {
181155
- const head = text3.slice(0, SNIFF_LENGTH);
181156
- if (head.trim() === "") return null;
181157
- return SIGNS.find((sign) => sign.test(head))?.format ?? null;
181158
- }
181159
- var SNIFF_LENGTH, SIGNS, filled, NAMES, SURE_FROM_DATA;
181160
- var init_detect = __esm({
181161
- "../../packages/diagrams/src/importers/detect.ts"() {
181162
- "use strict";
181163
- SNIFF_LENGTH = 2e4;
181164
- SIGNS = [
181165
- { format: "sql", test: (text3) => /^\s*create\s+(?:or\s+replace\s+)?(?:temporary\s+|temp\s+|unlogged\s+)?table\b/im.test(text3) },
181166
- { format: "prisma", test: (text3) => /^\s*model\s+\w+\s*\{/m.test(text3) && /@id\b|@relation\b|^\s*datasource\s+\w+\s*\{/m.test(text3) },
181167
- { format: "drizzle", test: (text3) => /\b(?:pgTable|sqliteTable|mysqlTable)\s*\(/.test(text3) },
181168
- { format: "openapi", test: (text3) => /^\s*["']?openapi["']?\s*:\s*["']?3\./m.test(text3) || /"openapi"\s*:\s*"3\./.test(text3) },
181169
- { format: "terraform-plan", test: (text3) => /^\s*\{\s*"format_version"\s*:/.test(text3) && /"terraform_version"\s*:/.test(text3) && /"(?:planned_values|resource_changes|root_module)"\s*:/.test(text3) },
181170
- { format: "terraform", test: (text3) => /^\s*resource\s+"[A-Za-z0-9_]+"\s+"[^"]+"\s*\{/m.test(text3) },
181171
- { format: "bicep", test: (text3) => /^\s*(?:resource|module)\s+[A-Za-z_]\w*\s+'[^'\n]+'\s*(?:existing\s*)?=/m.test(text3) || /^\s*targetScope\s*=\s*'/m.test(text3) },
181172
- { format: "arm", test: (text3) => /"\$schema"\s*:\s*"https?:\/\/schema\.management\.azure\.com\/schemas\/[^"]*(?:deploymentTemplate|DeploymentTemplate)/.test(text3) },
181173
- { format: "pulumi", test: (text3) => /^runtime\s*:\s*(?:yaml|\{\s*name\s*:\s*yaml)/m.test(text3) && /^resources\s*:/m.test(text3) || /"urn"\s*:\s*"urn:pulumi:/.test(text3) },
181174
- { format: "cloudformation", test: (text3) => /^\s*"?AWSTemplateFormatVersion"?\s*:/m.test(text3) || /^Transform\s*:\s*AWS::Serverless/m.test(text3) || /^\s*"?Resources"?\s*:/m.test(text3) && /"?Type"?\s*:\s*["']?AWS::[A-Za-z0-9]+::/.test(text3) },
181175
- { format: "kubernetes", test: (text3) => /^\s*apiVersion\s*:/m.test(text3) && /^\s*kind\s*:\s*\w+/m.test(text3) },
181176
- { format: "github-actions", test: (text3) => /^jobs\s*:/m.test(text3) && (/^\s*runs-on\s*:/m.test(text3) || /^["']?on["']?\s*:/m.test(text3)) },
181177
- { format: "compose", test: (text3) => /^services\s*:/m.test(text3) && /^\s+(?:image|build)\s*:/m.test(text3) },
181178
- { format: "tree", test: (text3) => /(?:├──|└──|\|-- |`-- )/.test(text3) || everyLine(text3, (line3) => /^[^\s/]+(?:\/[^\s/]+)+\/?$/.test(line3)) || isOutline(text3) },
181179
- { format: "git-log", test: (text3) => /^commit [0-9a-f]{7,40}\b/m.test(text3) || graphCommits(text3) >= 2 || everyLine(text3, (line3) => /^[0-9a-f]{7,40} \S/.test(line3)) },
181180
- { format: "json", test: (text3) => /^\s*[[{]/.test(text3) && /[\]}]\s*$/.test(text3) },
181181
- { format: "csv", test: (text3) => isTable(text3) }
181182
- ];
181183
- filled = (text3) => text3.split("\n", 200).map((line3) => line3.trimEnd()).filter((line3) => line3.trim() !== "");
181184
- NAMES = [
181185
- { format: "compose", words: /\b(?:docker[- ]compose|compose (?:file|yaml|yml|stack))\b/i },
181186
- { format: "kubernetes", words: /\b(?:kubernetes|k8s)\b/i },
181187
- { format: "github-actions", words: /\b(?:github actions?|workflow (?:file|yaml|yml)|ci workflow)\b/i },
181188
- { format: "terraform-plan", words: /\b(?:terraform|tofu) (?:plan|state|show -json)\b|\bplan\.json\b/i },
181189
- { format: "terraform", words: /\b(?:terraform|opentofu|hcl|\.tf files?)\b/i },
181190
- { format: "bicep", words: /\b(?:bicep|\.bicep files?)\b/i },
181191
- { format: "arm", words: /\b(?:arm templates?|azure resource manager|azuredeploy\.json)\b/i },
181192
- { format: "pulumi", words: /\bpulumi\b/i },
181193
- { format: "cloudformation", words: /\b(?:cloudformation|cfn templates?|sam templates?|serverless application model)\b/i },
181194
- { format: "openapi", words: /\b(?:openapi|swagger|api spec(?:ification)?)\b/i },
181195
- { format: "prisma", words: /\bprisma\b/i },
181196
- { format: "drizzle", words: /\bdrizzle\b/i },
181197
- { format: "sql", words: /\b(?:sql|ddl|create table|database schema dump)\b/i },
181198
- { format: "git-log", words: /\bgit (?:log|history)\b/i },
181199
- { format: "csv", words: /\b(?:csv|tsv|spreadsheet export)\b/i },
181200
- { format: "json", words: /\bjson (?:data|file|records|export)\b/i },
181201
- { format: "tree", words: /\b(?:tree output|folder (?:structure|tree|listing)|directory (?:tree|listing))\b/i }
181202
- ];
181203
- SURE_FROM_DATA = /* @__PURE__ */ new Set(["sql", "prisma", "drizzle", "openapi", "terraform", "terraform-plan", "bicep", "arm", "pulumi", "cloudformation", "compose", "kubernetes", "github-actions", "git-log"]);
181204
- }
181205
- });
181206
-
181207
- // ../../packages/diagrams/src/importers/terraform/azure-families.ts
181208
- function azureFamilyIcon(type) {
181209
- if (!type.startsWith("azurerm_")) return null;
181210
- const name2 = type.slice("azurerm_".length);
181211
- let best;
181212
- for (const family of AZURE_FAMILIES) if (name2.startsWith(family[0]) && family[0].length > (best?.[0].length ?? -1)) best = family;
181213
- return best?.[1] ?? null;
181214
- }
181215
- var AZURE_FAMILIES;
181216
- var init_azure_families = __esm({
181217
- "../../packages/diagrams/src/importers/terraform/azure-families.ts"() {
181218
- "use strict";
181219
- AZURE_FAMILIES = [
181220
- ["aadb2c_", "azure:ad-b2c"],
181221
- ["active_directory_domain_service", "azure:entra-domain-services"],
181222
- ["advanced_threat_protection", "azure:defender-for-cloud"],
181223
- ["attestation_", "azure:azureattestation"],
181224
- ["automanage_", "azure:automanaged-vm"],
181225
- ["automation_", "azure:automation-accounts"],
181226
- ["backup_", "azure:recovery-services-vaults"],
181227
- ["batch_", "azure:batch-accounts"],
181228
- ["bot_", "azure:bot-services"],
181229
- ["cognitive_", "azure:cognitive-services"],
181230
- ["container_connected_registry", "azure:container-registries"],
181231
- ["container_group", "azure:container-instances"],
181232
- ["cosmosdb_postgresql_", "azure:database-postgresql-server-group"],
181233
- ["cosmosdb_", "azure:cosmos-db"],
181234
- ["custom_provider", "azure:resource-groups"],
181235
- ["data_protection_", "azure:backup-vault"],
181236
- ["database_migration_", "azure:database-migration-services"],
181237
- ["dev_center", "azure:dev-box"],
181238
- ["dev_test_", "azure:devtest-labs"],
181239
- ["elastic_cloud_", "simple:elasticsearch"],
181240
- ["eventgrid_domain", "azure:event-grid-domains"],
181241
- ["eventgrid_", "azure:event-grid"],
181242
- ["eventhub_cluster", "azure:event-hub-clusters"],
181243
- ["eventhub", "azure:event-hubs"],
181244
- ["express_route_", "azure:expressroute-circuits"],
181245
- ["fabric_", "azure:power-bi-embedded"],
181246
- ["federated_identity_credential", "azure:managed-identities"],
181247
- ["frontdoor", "azure:front-door"],
181248
- ["gallery_application", "azure:image-definitions"],
181249
- ["graph_services_", "azure:resource-graph-explorer"],
181250
- ["hdinsight_", "azure:hd-insight-clusters"],
181251
- ["healthbot", "azure:bot-services"],
181252
- ["healthcare_", "azure:fhir-service"],
181253
- ["iotcentral_", "azure:iot-central-applications"],
181254
- ["iothub", "azure:iot-hub"],
181255
- ["kubernetes_automatic", "azure:aks-automatic"],
181256
- ["kubernetes_fleet", "azure:kubernetes-fleet-manager"],
181257
- ["kubernetes_", "azure:kubernetes-services"],
181258
- ["kusto_", "azure:data-explorer-clusters"],
181259
- ["maintenance_", "azure:maintenance-configuration"],
181260
- ["managed_application", "azure:managed-applications-center"],
181261
- ["management_lock", "azure:resource-groups"],
181262
- ["mongo_cluster", "azure:cosmos-db"],
181263
- ["mssql_elasticpool", "azure:sql-elastic-pools"],
181264
- ["mssql_job", "azure:elastic-job-agents"],
181265
- ["mssql_managed", "azure:sql-managed-instance"],
181266
- ["mssql_", "azure:sql-database"],
181267
- ["mysql_", "azure:database-for-mysql"],
181268
- ["netapp_", "azure:netapp-files"],
181269
- ["network_function_", "azure:network-function-manager"],
181270
- ["network_profile", "azure:virtual-network"],
181271
- ["nginx_", "simple:nginx"],
181272
- ["oracle_", "azure:oracle-database"],
181273
- ["pim_", "azure:entra-identity-roles-and-administrators"],
181274
- ["playwright_", "azure:load-testing"],
181275
- ["point_to_site_vpn_gateway", "azure:virtual-network-gateways"],
181276
- ["policy_", "azure:policy"],
181277
- ["portal_", "azure:management-portal"],
181278
- ["powerbi_", "azure:power-bi-embedded"],
181279
- ["purview_", "azure:identity-governance"],
181280
- ["redhat_openshift", "azure:red-hat-openshift"],
181281
- ["redis_", "azure:cache-redis"],
181282
- ["resource_deployment_script", "azure:templates"],
181283
- ["resource_policy", "azure:policy"],
181284
- ["resource_provider_", "azure:subscriptions"],
181285
- ["role_definition", "azure:entra-identity-custom-roles"],
181286
- ["role_management", "azure:entra-identity-roles-and-administrators"],
181287
- ["route_map", "azure:virtual-router"],
181288
- ["route_server", "azure:virtual-router"],
181289
- ["route", "azure:route-tables"],
181290
- ["service_fabric", "azure:service-fabric-clusters"],
181291
- ["site_recovery_", "azure:recovery-services-vaults"],
181292
- ["snapshot", "azure:disks-snapshots"],
181293
- ["spring_cloud_", "azure:spring-apps"],
181294
- ["ssh_public_key", "azure:ssh-keys"],
181295
- ["stack_hci_", "azure:stack-hci-premium"],
181296
- ["storage_blob", "azure:blob-storage"],
181297
- ["storage_sync", "azure:storage-sync-services"],
181298
- ["storage_", "azure:storage-accounts"],
181299
- ["stream_analytics_", "azure:stream-analytics-jobs"],
181300
- ["traffic_manager_", "azure:traffic-manager-profiles"],
181301
- ["trusted_signing_", "azure:key-vaults"],
181302
- ["virtual_hub", "azure:virtual-wan-hub"],
181303
- ["vmware_", "azure:vmware-solution"],
181304
- ["vpn_", "azure:virtual-network-gateways"],
181305
- ["web_app", "azure:app-service"],
181306
- ["workloads_sap_", "azure:virtual-instance-for-sap"]
181307
- ];
181308
- }
181309
- });
181310
-
181311
181163
  // ../../packages/diagrams/src/importers/shared/direction.ts
181312
181164
  function longestChain(keys, edges) {
181313
181165
  const out = /* @__PURE__ */ new Map();
@@ -182243,6 +182095,1345 @@ var init_shared2 = __esm({
182243
182095
  }
182244
182096
  });
182245
182097
 
182098
+ // ../../packages/diagrams/src/importers/shared/yaml/access.ts
182099
+ function get(node3, key) {
182100
+ if (node3?.kind !== "map") return void 0;
182101
+ return node3.entries.find((entry2) => entry2.key === key)?.value;
182102
+ }
182103
+ function text(node3) {
182104
+ return node3?.kind === "scalar" && !node3.isNull ? node3.value : null;
182105
+ }
182106
+ function items(node3) {
182107
+ if (node3 === null || node3 === void 0) return [];
182108
+ if (node3.kind === "seq") return node3.items;
182109
+ if (node3.kind === "scalar" && node3.isNull) return [];
182110
+ return [node3];
182111
+ }
182112
+ function names5(node3) {
182113
+ if (node3?.kind === "map") return node3.entries.map((entry2) => entry2.key);
182114
+ return items(node3).map((item) => text(item)).filter((value) => value !== null && value !== "");
182115
+ }
182116
+ function entries(node3) {
182117
+ return node3?.kind === "map" ? node3.entries : [];
182118
+ }
182119
+ var init_access = __esm({
182120
+ "../../packages/diagrams/src/importers/shared/yaml/access.ts"() {
182121
+ "use strict";
182122
+ }
182123
+ });
182124
+
182125
+ // ../../packages/diagrams/src/importers/shared/yaml/lines.ts
182126
+ function opensQuote(text3, index2) {
182127
+ if (index2 === 0) return true;
182128
+ const before = text3.charAt(index2 - 1);
182129
+ return before === " " || before === " " || before === "[" || before === "{" || before === ",";
182130
+ }
182131
+ function cutComment(text3) {
182132
+ let quote3 = null;
182133
+ for (let index2 = 0; index2 < text3.length; index2 += 1) {
182134
+ const char = text3.charAt(index2);
182135
+ if (quote3 === '"') {
182136
+ if (char === "\\") index2 += 1;
182137
+ else if (char === '"') quote3 = null;
182138
+ } else if (quote3 === "'") {
182139
+ if (char === "'" && text3.charAt(index2 + 1) === "'") index2 += 1;
182140
+ else if (char === "'") quote3 = null;
182141
+ } else if ((char === '"' || char === "'") && opensQuote(text3, index2)) {
182142
+ quote3 = char;
182143
+ } else if (char === "#" && (index2 === 0 || text3.charAt(index2 - 1) === " " || text3.charAt(index2 - 1) === " ")) {
182144
+ return text3.slice(0, index2);
182145
+ }
182146
+ }
182147
+ return text3;
182148
+ }
182149
+ function blockIndicator(text3) {
182150
+ const lastSpace = text3.lastIndexOf(" ");
182151
+ const tail = text3.slice(lastSpace + 1);
182152
+ if (!/^[|>][-+0-9]{0,2}$/.test(tail)) return null;
182153
+ const words2 = text3.slice(0, lastSpace + 1).trim().split(" ").filter((part) => part !== "");
182154
+ while (words2.length > 0 && /^[&!]/.test(words2[words2.length - 1] ?? "")) words2.pop();
182155
+ const head = words2.join(" ");
182156
+ if (head !== "" && !head.endsWith(":") && head !== "-") return null;
182157
+ return { folded: tail.startsWith(">"), keep: tail.includes("+"), strip: tail.includes("-") };
182158
+ }
182159
+ function fold4(lines) {
182160
+ let out = "";
182161
+ for (const line3 of lines) {
182162
+ if (line3 === "") out += "\n";
182163
+ else out += out === "" || out.endsWith("\n") ? line3 : ` ${line3}`;
182164
+ }
182165
+ return out;
182166
+ }
182167
+ function splitDocuments(text3) {
182168
+ const raw = text3.split("\n");
182169
+ const documents = [];
182170
+ let current = { lines: [], line: 1 };
182171
+ let started = false;
182172
+ for (let index2 = 0; index2 < raw.length; index2 += 1) {
182173
+ const full = (raw[index2] ?? "").replace(/\r$/, "");
182174
+ const lineNumber = index2 + 1;
182175
+ if (/^---(\s|$)/.test(full) || /^\.\.\.\s*$/.test(full)) {
182176
+ if (started || current.lines.length > 0) documents.push(current);
182177
+ current = { lines: [], line: lineNumber };
182178
+ started = full.startsWith("---");
182179
+ const rest = cutComment(full.slice(3)).trim();
182180
+ if (full.startsWith("---") && rest !== "" && !/^![^\s]*$/.test(rest)) current.lines.push({ indent: 0, text: rest.replace(/^![^\s]*\s+/, ""), line: lineNumber, block: null });
182181
+ continue;
182182
+ }
182183
+ if (full.startsWith("%") && current.lines.length === 0) continue;
182184
+ const indent = full.length - full.replace(/^ +/, "").length;
182185
+ if (full.charAt(indent) === " " && full.trim() !== "") {
182186
+ return { documents, problem: { line: lineNumber, column: indent + 1, message: "A tab is used to indent this line.", hint: "YAML indents with spaces only. Replace the tab with spaces." } };
182187
+ }
182188
+ const textPart = cutComment(full.slice(indent)).trimEnd();
182189
+ if (textPart === "") continue;
182190
+ const indicator = blockIndicator(textPart);
182191
+ let block = null;
182192
+ if (indicator !== null) {
182193
+ const body6 = [];
182194
+ let bodyIndent = -1;
182195
+ while (index2 + 1 < raw.length) {
182196
+ const next = (raw[index2 + 1] ?? "").replace(/\r$/, "");
182197
+ const nextIndent = next.length - next.replace(/^ +/, "").length;
182198
+ if (next.trim() !== "" && nextIndent <= indent) break;
182199
+ if (next.trim() !== "" && bodyIndent === -1) bodyIndent = nextIndent;
182200
+ body6.push(next.trim() === "" ? "" : next.slice(Math.max(0, bodyIndent)));
182201
+ index2 += 1;
182202
+ }
182203
+ while (!indicator.keep && body6.length > 0 && body6[body6.length - 1] === "") body6.pop();
182204
+ const joined2 = indicator.folded ? fold4(body6) : body6.join("\n");
182205
+ block = indicator.strip || joined2 === "" ? joined2 : `${joined2}
182206
+ `;
182207
+ }
182208
+ current.lines.push({ indent, text: indicator === null ? textPart : textPart.replace(/[|>][-+0-9]*$/, "").trimEnd(), line: lineNumber, block });
182209
+ }
182210
+ if (started || current.lines.length > 0) documents.push(current);
182211
+ return { documents, problem: null };
182212
+ }
182213
+ var init_lines = __esm({
182214
+ "../../packages/diagrams/src/importers/shared/yaml/lines.ts"() {
182215
+ "use strict";
182216
+ }
182217
+ });
182218
+
182219
+ // ../../packages/diagrams/src/importers/shared/yaml/scalars.ts
182220
+ function scalar(value, quoted5, line3, column) {
182221
+ const isNull = !quoted5 && (value === "" || value === "~" || value === "null" || value === "Null" || value === "NULL");
182222
+ return { kind: "scalar", value: isNull ? "" : value, quoted: quoted5, isNull, line: line3, column };
182223
+ }
182224
+ function readQuoted(text3, start2) {
182225
+ const quote3 = text3.charAt(start2);
182226
+ let value = "";
182227
+ for (let index2 = start2 + 1; index2 < text3.length; index2 += 1) {
182228
+ const char = text3.charAt(index2);
182229
+ if (quote3 === "'") {
182230
+ if (char !== "'") value += char;
182231
+ else if (text3.charAt(index2 + 1) === "'") {
182232
+ value += "'";
182233
+ index2 += 1;
182234
+ } else return { value, end: index2 + 1 };
182235
+ } else if (char === '"') {
182236
+ return { value, end: index2 + 1 };
182237
+ } else if (char === "\\") {
182238
+ const next = text3.charAt(index2 + 1);
182239
+ const width = next === "u" ? 4 : next === "x" ? 2 : next === "U" ? 8 : 0;
182240
+ if (width > 0) {
182241
+ const code = Number.parseInt(text3.slice(index2 + 2, index2 + 2 + width), 16);
182242
+ value += Number.isFinite(code) && code <= 1114111 ? String.fromCodePoint(code) : "";
182243
+ index2 += 1 + width;
182244
+ } else {
182245
+ value += ESCAPES[next] ?? next;
182246
+ index2 += 1;
182247
+ }
182248
+ } else {
182249
+ value += char;
182250
+ }
182251
+ }
182252
+ return null;
182253
+ }
182254
+ function stripProperties(text3) {
182255
+ let rest = text3;
182256
+ let anchor2 = null;
182257
+ let tag = null;
182258
+ let offset = 0;
182259
+ for (let round2 = 0; round2 < 2; round2 += 1) {
182260
+ const match = /^([!&])([^\s,[\]{}]*)\s*/.exec(rest);
182261
+ if (match === null) break;
182262
+ if (match[1] === "&") anchor2 = match[2] ?? null;
182263
+ else tag = `!${match[2] ?? ""}`;
182264
+ offset += match[0].length;
182265
+ rest = rest.slice(match[0].length);
182266
+ }
182267
+ return { text: rest, anchor: anchor2, tag, offset };
182268
+ }
182269
+ function tagged(node3, tag) {
182270
+ return tag === null || tag.startsWith("!!") ? node3 : { ...node3, tag };
182271
+ }
182272
+ function readFlow2(text3, line3, column, resolveAlias) {
182273
+ const reader = new FlowReader(text3, line3, column, resolveAlias);
182274
+ const node3 = reader.value(0);
182275
+ if (!reader.done()) throw new FlowError("Text follows the end of the flow collection.");
182276
+ return node3;
182277
+ }
182278
+ function flowClosed(text3) {
182279
+ let depth = 0;
182280
+ let quote3 = null;
182281
+ for (let index2 = 0; index2 < text3.length; index2 += 1) {
182282
+ const char = text3.charAt(index2);
182283
+ if (quote3 !== null) {
182284
+ if (quote3 === '"' && char === "\\") index2 += 1;
182285
+ else if (char === quote3) quote3 = null;
182286
+ } else if (char === '"' || char === "'") quote3 = char;
182287
+ else if (char === "[" || char === "{") depth += 1;
182288
+ else if (char === "]" || char === "}") depth -= 1;
182289
+ }
182290
+ return depth <= 0 && quote3 === null;
182291
+ }
182292
+ var MAX_FLOW_DEPTH, ESCAPES, FlowReader, FlowError;
182293
+ var init_scalars = __esm({
182294
+ "../../packages/diagrams/src/importers/shared/yaml/scalars.ts"() {
182295
+ "use strict";
182296
+ MAX_FLOW_DEPTH = 32;
182297
+ ESCAPES = { n: "\n", t: " ", r: "\r", '"': '"', "\\": "\\", "/": "/", "0": "\0", " ": " ", e: "\x1B", N: "\x85", _: "\xA0" };
182298
+ FlowReader = class {
182299
+ constructor(text3, line3, column, resolveAlias) {
182300
+ this.text = text3;
182301
+ this.line = line3;
182302
+ this.column = column;
182303
+ this.resolveAlias = resolveAlias;
182304
+ }
182305
+ text;
182306
+ line;
182307
+ column;
182308
+ resolveAlias;
182309
+ index = 0;
182310
+ skipSpace() {
182311
+ while (this.index < this.text.length && /\s/.test(this.text.charAt(this.index))) this.index += 1;
182312
+ }
182313
+ /** The column in the file of the reader's position, for a node it reads. */
182314
+ at() {
182315
+ return this.column + this.index;
182316
+ }
182317
+ /** One value: a collection, a quoted scalar, an alias, or a plain scalar up to `,`, `]`, `}`, or `: `. */
182318
+ value(depth) {
182319
+ if (depth > MAX_FLOW_DEPTH) throw new FlowError("The flow collection is nested too deep.");
182320
+ this.skipSpace();
182321
+ const char = this.text.charAt(this.index);
182322
+ const column = this.at();
182323
+ if (char === "[") return this.sequence(depth);
182324
+ if (char === "{") return this.mapping(depth);
182325
+ if (char === '"' || char === "'") {
182326
+ const read = readQuoted(this.text, this.index);
182327
+ if (read === null) throw new FlowError("A quoted string is never closed.");
182328
+ this.index = read.end;
182329
+ return scalar(read.value, true, this.line, column);
182330
+ }
182331
+ const start2 = this.index;
182332
+ while (this.index < this.text.length) {
182333
+ const next = this.text.charAt(this.index);
182334
+ if (next === "," || next === "]" || next === "}") break;
182335
+ if (next === ":" && /[\s,\]}]/.test(this.text.charAt(this.index + 1) || " ")) break;
182336
+ this.index += 1;
182337
+ }
182338
+ const word2 = this.text.slice(start2, this.index).trim();
182339
+ if (word2.startsWith("*")) return this.resolveAlias(word2.slice(1)) ?? scalar("", false, this.line, column);
182340
+ const props = stripProperties(word2);
182341
+ return tagged(scalar(props.text, false, this.line, column), props.tag);
182342
+ }
182343
+ sequence(depth) {
182344
+ const column = this.at();
182345
+ this.index += 1;
182346
+ const items2 = [];
182347
+ for (; ; ) {
182348
+ this.skipSpace();
182349
+ if (this.index >= this.text.length) throw new FlowError("A [ list is never closed with ].");
182350
+ if (this.text.charAt(this.index) === "]") {
182351
+ this.index += 1;
182352
+ return { kind: "seq", items: items2, line: this.line, column };
182353
+ }
182354
+ items2.push(this.value(depth + 1));
182355
+ this.skipSpace();
182356
+ if (this.text.charAt(this.index) === ",") this.index += 1;
182357
+ else if (this.text.charAt(this.index) !== "]") throw new FlowError("Items of a [ list are separated by commas.");
182358
+ }
182359
+ }
182360
+ mapping(depth) {
182361
+ const column = this.at();
182362
+ this.index += 1;
182363
+ const entries2 = [];
182364
+ for (; ; ) {
182365
+ this.skipSpace();
182366
+ if (this.index >= this.text.length) throw new FlowError("A { mapping is never closed with }.");
182367
+ if (this.text.charAt(this.index) === "}") {
182368
+ this.index += 1;
182369
+ return { kind: "map", entries: entries2, line: this.line, column };
182370
+ }
182371
+ const keyColumn = this.at();
182372
+ const key = this.value(depth + 1);
182373
+ this.skipSpace();
182374
+ let value = scalar("", false, this.line, this.at());
182375
+ if (this.text.charAt(this.index) === ":") {
182376
+ this.index += 1;
182377
+ value = this.value(depth + 1);
182378
+ }
182379
+ entries2.push({ key: key.kind === "scalar" ? key.value : "", value, line: this.line, column: keyColumn });
182380
+ this.skipSpace();
182381
+ if (this.text.charAt(this.index) === ",") this.index += 1;
182382
+ else if (this.text.charAt(this.index) !== "}") throw new FlowError("Entries of a { mapping are separated by commas.");
182383
+ }
182384
+ }
182385
+ /** True when only spaces are left. */
182386
+ done() {
182387
+ this.skipSpace();
182388
+ return this.index >= this.text.length;
182389
+ }
182390
+ };
182391
+ FlowError = class extends Error {
182392
+ };
182393
+ }
182394
+ });
182395
+
182396
+ // ../../packages/diagrams/src/importers/shared/yaml/parse.ts
182397
+ function splitKey(text3) {
182398
+ if (text3.startsWith('"') || text3.startsWith("'")) {
182399
+ const read = readQuoted(text3, 0);
182400
+ if (read === null) return null;
182401
+ const after = text3.slice(read.end).trimStart();
182402
+ if (!after.startsWith(":")) return null;
182403
+ const colon = text3.length - after.length;
182404
+ return /^:(\s|$)/.test(text3.slice(colon)) ? { key: read.value, colon } : null;
182405
+ }
182406
+ if (/^[[{]/.test(text3) || text3.startsWith("- ") || text3 === "-") return null;
182407
+ for (let index2 = text3.indexOf(":"); index2 !== -1; index2 = text3.indexOf(":", index2 + 1)) {
182408
+ const next = text3.charAt(index2 + 1);
182409
+ if (next === "" || next === " " || next === " ") return { key: text3.slice(0, index2).trim(), colon: index2 };
182410
+ }
182411
+ return null;
182412
+ }
182413
+ function parseYaml(text3) {
182414
+ const split = splitDocuments(text3);
182415
+ if (split.problem !== null) return { documents: [], error: split.problem };
182416
+ const documents = [];
182417
+ for (const document2 of split.documents) {
182418
+ try {
182419
+ documents.push({ root: new BlockReader(document2.lines).document(), line: document2.line });
182420
+ } catch (error62) {
182421
+ if (!(error62 instanceof YamlProblem)) throw error62;
182422
+ const problem = { line: error62.line, column: error62.column, message: error62.message, hint: error62.hint };
182423
+ return { documents, error: problem };
182424
+ }
182425
+ }
182426
+ return { documents, error: null };
182427
+ }
182428
+ var MAX_DEPTH2, MAX_JOINED_LINES, YamlProblem, isItem, BlockReader;
182429
+ var init_parse21 = __esm({
182430
+ "../../packages/diagrams/src/importers/shared/yaml/parse.ts"() {
182431
+ "use strict";
182432
+ init_lines();
182433
+ init_scalars();
182434
+ MAX_DEPTH2 = 64;
182435
+ MAX_JOINED_LINES = 200;
182436
+ YamlProblem = class extends Error {
182437
+ constructor(line3, column, message2, hint2) {
182438
+ super(message2);
182439
+ this.line = line3;
182440
+ this.column = column;
182441
+ this.hint = hint2;
182442
+ }
182443
+ line;
182444
+ column;
182445
+ hint;
182446
+ };
182447
+ isItem = (text3) => text3 === "-" || text3.startsWith("- ");
182448
+ BlockReader = class {
182449
+ constructor(lines) {
182450
+ this.lines = lines;
182451
+ }
182452
+ lines;
182453
+ index = 0;
182454
+ anchors = /* @__PURE__ */ new Map();
182455
+ problem(line3, message2, hint2) {
182456
+ return new YamlProblem(line3.line, line3.indent + 1, message2, hint2);
182457
+ }
182458
+ /** The whole document: one block, and nothing left over. */
182459
+ document() {
182460
+ const first = this.lines[0];
182461
+ if (first === void 0) return null;
182462
+ const root = this.block(first.indent, 0);
182463
+ const extra = this.lines[this.index];
182464
+ if (extra !== void 0) throw this.problem(extra, "This line is indented less than the block it follows, or not like the lines before it.", "Line it up with the other keys at its level.");
182465
+ return root;
182466
+ }
182467
+ /** A block that starts at the current line, indented `indent` spaces: a sequence, a mapping, or a scalar. */
182468
+ block(indent, depth) {
182469
+ const line3 = this.lines[this.index];
182470
+ if (depth > MAX_DEPTH2) throw this.problem(line3, "The YAML is nested too deep.", `Keep nesting under ${MAX_DEPTH2} levels.`);
182471
+ if (isItem(line3.text)) return this.sequence(indent, depth);
182472
+ if (splitKey(line3.text) !== null) return this.mapping(indent, depth);
182473
+ this.index += 1;
182474
+ return this.value(line3.text, line3, indent, depth, line3.indent + 1);
182475
+ }
182476
+ mapping(indent, depth) {
182477
+ const first = this.lines[this.index];
182478
+ const entries2 = [];
182479
+ const merges = [];
182480
+ while (this.index < this.lines.length) {
182481
+ const line3 = this.lines[this.index];
182482
+ if (line3.indent < indent) break;
182483
+ if (line3.indent > indent) throw this.problem(line3, "This line is indented more than the keys around it.", "Indent it like the other keys of the same mapping.");
182484
+ if (isItem(line3.text)) break;
182485
+ const split = splitKey(line3.text);
182486
+ if (split === null) throw this.problem(line3, "Expected a key followed by a colon.", "Write it as key: value, with a space after the colon.");
182487
+ this.index += 1;
182488
+ const rest = line3.text.slice(split.colon + 1).trim();
182489
+ const value = this.entryValue(rest, line3, indent, depth, line3.indent + split.colon + 2);
182490
+ if (split.key === "<<") merges.push(value);
182491
+ else entries2.push({ key: split.key, value, line: line3.line, column: line3.indent + 1 });
182492
+ }
182493
+ for (const merge3 of merges) {
182494
+ const sources = merge3.kind === "seq" ? merge3.items : [merge3];
182495
+ for (const source of sources) {
182496
+ if (source.kind !== "map") continue;
182497
+ for (const entry2 of source.entries) if (!entries2.some((own2) => own2.key === entry2.key)) entries2.push(entry2);
182498
+ }
182499
+ }
182500
+ return { kind: "map", entries: entries2, line: first.line, column: first.indent + 1 };
182501
+ }
182502
+ /** The value of a mapping entry: on its own line, in a block below it, or a sequence at the same indent. */
182503
+ entryValue(rest, line3, indent, depth, column) {
182504
+ const next = this.lines[this.index];
182505
+ const props = stripProperties(rest);
182506
+ if (props.text === "" && line3.block === null && next !== void 0 && (next.indent > indent || next.indent === indent && isItem(next.text))) {
182507
+ const node3 = tagged(this.block(next.indent, depth + 1), props.tag);
182508
+ if (props.anchor !== null) this.anchors.set(props.anchor, node3);
182509
+ return node3;
182510
+ }
182511
+ return this.value(rest, line3, indent, depth, column, true);
182512
+ }
182513
+ sequence(indent, depth) {
182514
+ const first = this.lines[this.index];
182515
+ const items2 = [];
182516
+ while (this.index < this.lines.length) {
182517
+ const line3 = this.lines[this.index];
182518
+ if (line3.indent !== indent || !isItem(line3.text)) {
182519
+ if (line3.indent > indent) throw this.problem(line3, "This line is indented more than the items around it.", "Indent it like the other items, or under the item it belongs to.");
182520
+ break;
182521
+ }
182522
+ const rest = line3.text.slice(1).trimStart();
182523
+ const restColumn = line3.indent + (line3.text.length - rest.length);
182524
+ if (rest === "" && line3.block === null) {
182525
+ this.index += 1;
182526
+ const next = this.lines[this.index];
182527
+ items2.push(next !== void 0 && next.indent > indent ? this.block(next.indent, depth + 1) : scalar("", false, line3.line, restColumn + 1));
182528
+ } else if (line3.block === null && /^![A-Za-z:]+$/.test(rest) && this.lines[this.index + 1] !== void 0 && this.lines[this.index + 1].indent > indent) {
182529
+ const tag = rest;
182530
+ this.index += 1;
182531
+ items2.push(tagged(this.block(this.lines[this.index].indent, depth + 1), tag));
182532
+ } else if (line3.block === null && (isItem(rest) || splitKey(stripProperties(rest).text) !== null)) {
182533
+ const props = stripProperties(rest);
182534
+ line3.indent = restColumn + props.offset;
182535
+ line3.text = props.text;
182536
+ const node3 = this.block(line3.indent, depth + 1);
182537
+ if (props.anchor !== null) this.anchors.set(props.anchor, node3);
182538
+ items2.push(node3);
182539
+ } else {
182540
+ this.index += 1;
182541
+ items2.push(this.value(rest, line3, indent, depth, restColumn + 1));
182542
+ }
182543
+ }
182544
+ return { kind: "seq", items: items2, line: first.line, column: first.indent + 1 };
182545
+ }
182546
+ /** A value written on a line: a block scalar, an alias, a flow collection, a quoted scalar, or a plain scalar that may run on. */
182547
+ value(text3, line3, indent, depth, column, ofKey = false) {
182548
+ const props = stripProperties(text3);
182549
+ const body6 = props.text;
182550
+ let node3;
182551
+ if (line3.block !== null) {
182552
+ node3 = scalar(line3.block, true, line3.line, column);
182553
+ } else if (body6.startsWith("*")) {
182554
+ const target = this.anchors.get(body6.slice(1).trim());
182555
+ if (target === void 0) throw this.problem(line3, `The alias ${body6} names no anchor written before it.`, "Write the anchor (&name) before the alias that uses it.");
182556
+ node3 = target;
182557
+ } else if (body6.startsWith("[") || body6.startsWith("{")) {
182558
+ node3 = this.flow(this.joined(body6, flowClosed), line3, column + props.offset);
182559
+ } else if (body6.startsWith('"') || body6.startsWith("'")) {
182560
+ const whole2 = this.joined(body6, (joined2) => readQuoted(joined2, 0) !== null);
182561
+ const read = readQuoted(whole2, 0);
182562
+ if (read === null) throw this.problem(line3, "A quoted string is never closed.", "Close it with the same quote it opened with.");
182563
+ node3 = scalar(read.value, true, line3.line, column + props.offset);
182564
+ } else {
182565
+ let plain3 = body6;
182566
+ while (this.index < this.lines.length) {
182567
+ const more = this.lines[this.index];
182568
+ if (more.indent < indent || more.indent === indent && !ofKey) break;
182569
+ if (splitKey(more.text) !== null || isItem(more.text)) break;
182570
+ plain3 = `${plain3} ${more.text}`;
182571
+ this.index += 1;
182572
+ }
182573
+ node3 = scalar(plain3.trim(), false, line3.line, column + props.offset);
182574
+ }
182575
+ node3 = tagged(node3, props.tag);
182576
+ if (props.anchor !== null) this.anchors.set(props.anchor, node3);
182577
+ return node3;
182578
+ }
182579
+ /** The text of a value that runs over several lines until `complete` says it is whole, consuming those lines. */
182580
+ joined(start2, complete) {
182581
+ let text3 = start2;
182582
+ for (let count3 = 0; !complete(text3) && count3 < MAX_JOINED_LINES && this.index < this.lines.length; count3 += 1) {
182583
+ text3 = `${text3} ${this.lines[this.index].text}`;
182584
+ this.index += 1;
182585
+ }
182586
+ return text3;
182587
+ }
182588
+ flow(text3, line3, column) {
182589
+ try {
182590
+ return readFlow2(text3, line3.line, column, (name2) => this.anchors.get(name2) ?? null);
182591
+ } catch (error62) {
182592
+ if (error62 instanceof FlowError) throw this.problem(line3, error62.message, "Close every [ and { on the line it opens, and separate items with commas.");
182593
+ throw error62;
182594
+ }
182595
+ }
182596
+ };
182597
+ }
182598
+ });
182599
+
182600
+ // ../../packages/diagrams/src/importers/shared/yaml/index.ts
182601
+ var init_yaml = __esm({
182602
+ "../../packages/diagrams/src/importers/shared/yaml/index.ts"() {
182603
+ "use strict";
182604
+ init_access();
182605
+ init_parse21();
182606
+ }
182607
+ });
182608
+
182609
+ // ../../packages/diagrams/src/importers/helm/chart.ts
182610
+ function isChartFile(textIn) {
182611
+ return /^apiVersion\s*:\s*["']?v[12]["']?\s*$/m.test(textIn) && /^name\s*:/m.test(textIn) && /^version\s*:/m.test(textIn) && !/^kind\s*:/m.test(textIn);
182612
+ }
182613
+ function dependenciesOf(node3) {
182614
+ return items(node3).flatMap((entry2) => {
182615
+ const name2 = text(get(entry2, "name"));
182616
+ return name2 === null ? [] : [{ name: name2, alias: text(get(entry2, "alias")), version: text(get(entry2, "version")), repository: text(get(entry2, "repository")), condition: text(get(entry2, "condition")) }];
182617
+ });
182618
+ }
182619
+ function valueAt(values, path) {
182620
+ let node3 = values ?? void 0;
182621
+ for (const key of path.split(".")) node3 = get(node3, key);
182622
+ return text(node3);
182623
+ }
182624
+ function drawCharts(files, context, index2) {
182625
+ const charts = [];
182626
+ const byFolder = (folder, name2) => files.find((file2) => folderOf(file2.path) === folder && baseName(file2.path) === name2);
182627
+ const parsed = (file2) => {
182628
+ if (file2 === void 0) return null;
182629
+ const read = parseYaml(file2.text);
182630
+ if (read.error !== null) {
182631
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, index2.span(file2.start), `${cleanText(file2.path, "A file")} is not read: ${read.error.message}`, read.error.hint);
182632
+ return null;
182633
+ }
182634
+ return read.documents[0]?.root ?? null;
182635
+ };
182636
+ const templates = files.filter((file2) => /(?:^|\/)templates\//.test(file2.path));
182637
+ for (const file2 of files.filter((entry2) => baseName(entry2.path) === "Chart.yaml" || entry2.path === "" && isChartFile(entry2.text))) {
182638
+ const root2 = parsed(file2);
182639
+ const name2 = text(get(root2, "name"));
182640
+ if (root2 === null || name2 === null) continue;
182641
+ const folder = folderOf(file2.path);
182642
+ const requirements = parsed(byFolder(folder, "requirements.yaml"));
182643
+ const lock = parsed(byFolder(folder, "Chart.lock")) ?? parsed(byFolder(folder, "requirements.lock"));
182644
+ const locked = /* @__PURE__ */ new Map();
182645
+ for (const entry2 of items(get(lock, "dependencies"))) {
182646
+ const lockedName = text(get(entry2, "name"));
182647
+ const version2 = text(get(entry2, "version"));
182648
+ if (lockedName !== null && version2 !== null) locked.set(lockedName, version2);
182649
+ }
182650
+ charts.push({
182651
+ name: name2,
182652
+ version: text(get(root2, "version")),
182653
+ appVersion: text(get(root2, "appVersion")),
182654
+ library: text(get(root2, "type")) === "library",
182655
+ folder,
182656
+ start: file2.start,
182657
+ dependencies: [...dependenciesOf(get(root2, "dependencies")), ...dependenciesOf(get(requirements, "dependencies"))],
182658
+ values: parsed(byFolder(folder, "values.yaml")),
182659
+ locked
182660
+ });
182661
+ }
182662
+ if (charts.length === 0) {
182663
+ context.report.add(DIAGNOSTIC_CODES.importNoShape, START, "No Chart.yaml with a name was found.", "Send the chart folder with its Chart.yaml, or the output of helm template.");
182664
+ return null;
182665
+ }
182666
+ if (templates.length > 0) {
182667
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, index2.span(templates[0]?.start ?? 0), `${countOf2(templates.length, "template")} of the chart ${templates.length === 1 ? "is" : "are"} not rendered, so the objects ${templates.length === 1 ? "it makes are" : "they make are"} not drawn.`, "Run helm template with the values to deploy, and import its output as helm to draw the objects.");
182668
+ }
182669
+ charts.sort((a, b) => a.folder.split("/").length - b.folder.split("/").length || (a.folder < b.folder ? -1 : 1));
182670
+ const chartKey = (chart) => `chart:${chart.folder === "" ? chart.name : chart.folder}`;
182671
+ const nodes = [];
182672
+ const edges = [];
182673
+ const repositories = new Set(charts.flatMap((chart) => chart.dependencies.map((dependency) => dependency.repository)).filter((repository) => repository !== null && repository !== "" && !repository.startsWith("file://")));
182674
+ const framed = repositories.size > 1;
182675
+ const groups = framed ? [...repositories].sort().map((repository) => ({ key: `repository:${repository}`, label: repositoryLabel(repository), sub: "Chart repository", icon: "simple:helm", idHint: repositoryLabel(repository).split("/").pop() ?? "repository" })) : [];
182676
+ for (const chart of charts) {
182677
+ const look = imageLook(chart.name);
182678
+ nodes.push({ key: chartKey(chart), type: look?.type ?? "service", label: chart.name, sub: cleanText([[chart.library ? "Library chart" : "Chart", chart.version].filter((part) => part !== null).join(" "), chart.appVersion === null ? null : `app ${chart.appVersion}`].filter((part) => part !== null).join(", ")), icon: look?.icon ?? "simple:helm", idHint: chart.name });
182679
+ }
182680
+ for (const chart of charts) {
182681
+ for (const dependency of chart.dependencies) {
182682
+ const shown = dependency.alias ?? dependency.name;
182683
+ const vendored = charts.find((other) => other.folder === `${chart.folder === "" ? "" : `${chart.folder}/`}charts/${dependency.name}`) ?? (dependency.repository?.startsWith("file://") === true ? charts.find((other) => other.name === dependency.name) : void 0);
182684
+ const key = vendored === void 0 ? `dependency:${chart.name}/${shown}` : chartKey(vendored);
182685
+ const setting = dependency.condition === null ? null : dependency.condition.split(",").map((path) => valueAt(chart.values, path.trim())).find((value) => value !== null) ?? null;
182686
+ const off = setting === "false";
182687
+ if (vendored === void 0) {
182688
+ const look = imageLook(dependency.name);
182689
+ const repository = dependency.repository !== null && repositories.has(dependency.repository) && framed ? `repository:${dependency.repository}` : null;
182690
+ nodes.push({ key, type: look?.type ?? "service", label: shown, sub: cleanText([shown === dependency.name ? "Chart" : `Chart ${dependency.name}`, off ? "off in values" : dependency.condition !== null && setting !== "true" ? "optional" : null].filter((part) => part !== null).join(", ")), icon: look?.icon ?? iconForName(dependency.name, look?.type ?? "service") ?? "simple:helm", group: repository, idHint: shown });
182691
+ }
182692
+ const version2 = chart.locked.get(dependency.name) ?? dependency.version;
182693
+ edges.push({ from: chartKey(chart), to: key, label: version2, ...off ? { arrow: "-->" } : {} });
182694
+ }
182695
+ }
182696
+ const root = charts[0];
182697
+ const dependencies = charts.reduce((total, chart) => total + chart.dependencies.length, 0);
182698
+ return writeFlow(
182699
+ {
182700
+ title: context.title ?? `Helm chart ${root.name}`,
182701
+ desc: `${countOf2(charts.length, "chart")} and the ${countOf2(dependencies, "dependency", "dependencies")} they declare, imported from a Helm chart folder. An arrow points from a chart to a chart it depends on, labelled with the version it gets; a dashed one is switched off in values.yaml.`,
182702
+ direction: "down",
182703
+ nodes,
182704
+ groups,
182705
+ edges,
182706
+ noun: "charts",
182707
+ capSpan: (key) => index2.span(charts.find((chart) => chartKey(chart) === key)?.start ?? 0),
182708
+ advice: "Import the output of helm template as helm to draw the objects each chart makes."
182709
+ },
182710
+ context.report
182711
+ );
182712
+ }
182713
+ var baseName, folderOf, repositoryLabel;
182714
+ var init_chart = __esm({
182715
+ "../../packages/diagrams/src/importers/helm/chart.ts"() {
182716
+ "use strict";
182717
+ init_core();
182718
+ init_shared2();
182719
+ init_yaml();
182720
+ baseName = (path) => path.slice(path.lastIndexOf("/") + 1);
182721
+ folderOf = (path) => path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
182722
+ repositoryLabel = (repository) => repository.replace(/^[a-z]+:\/\//i, "").replace(/\/+$/, "") || repository;
182723
+ }
182724
+ });
182725
+
182726
+ // ../../packages/diagrams/src/importers/kubernetes/manifests.ts
182727
+ function valuesOf(kind, root, podSpec) {
182728
+ const raw = kind === "ConfigMap" ? entries(get(root, "data")).map((entry2) => text(entry2.value)) : items(get(podSpec, "containers")).flatMap((container) => items(get(container, "env")).map((variable) => text(get(variable, "value"))));
182729
+ return raw.filter((value) => value !== null && value.length <= MAX_VALUE_LENGTH);
182730
+ }
182731
+ function stringMap(node3) {
182732
+ const out = /* @__PURE__ */ new Map();
182733
+ if (node3?.kind === "map") for (const entry2 of node3.entries) out.set(entry2.key, text(entry2.value) ?? "");
182734
+ return out;
182735
+ }
182736
+ function podUses(spec) {
182737
+ const uses = [];
182738
+ const add2 = (kind, name2) => {
182739
+ if (name2 !== null && name2 !== "" && !uses.some((use) => use.kind === kind && use.name === name2)) uses.push({ kind, name: name2 });
182740
+ };
182741
+ for (const container of [...items(get(spec, "initContainers")), ...items(get(spec, "containers"))]) {
182742
+ for (const source of items(get(container, "envFrom"))) {
182743
+ add2("ConfigMap", text(at2(source, ["configMapRef", "name"])));
182744
+ add2("Secret", text(at2(source, ["secretRef", "name"])));
182745
+ }
182746
+ for (const variable of items(get(container, "env"))) {
182747
+ add2("ConfigMap", text(at2(variable, ["valueFrom", "configMapKeyRef", "name"])));
182748
+ add2("Secret", text(at2(variable, ["valueFrom", "secretKeyRef", "name"])));
182749
+ }
182750
+ }
182751
+ for (const volume of items(get(spec, "volumes"))) {
182752
+ add2("ConfigMap", text(at2(volume, ["configMap", "name"])));
182753
+ add2("Secret", text(at2(volume, ["secret", "secretName"])));
182754
+ add2("PersistentVolumeClaim", text(at2(volume, ["persistentVolumeClaim", "claimName"])));
182755
+ }
182756
+ return uses;
182757
+ }
182758
+ function ingressRoutes(spec) {
182759
+ const routes = [];
182760
+ const backend = (node3) => text(at2(node3, ["service", "name"])) ?? text(get(node3, "serviceName"));
182761
+ const add2 = (service, label) => {
182762
+ if (service !== null && !routes.some((route) => route.service === service)) routes.push({ service, label });
182763
+ };
182764
+ add2(backend(get(spec, "defaultBackend") ?? get(spec, "backend")), null);
182765
+ for (const rule of items(get(spec, "rules"))) {
182766
+ for (const path of items(at2(rule, ["http", "paths"]))) {
182767
+ const route = text(get(path, "path"));
182768
+ add2(backend(get(path, "backend")), route === null || route === "/" ? text(get(rule, "host")) : route);
182769
+ }
182770
+ }
182771
+ return routes;
182772
+ }
182773
+ function detailOf(kind, spec) {
182774
+ if (kind === "Service") {
182775
+ const ports = items(get(spec, "ports")).map((port) => text(get(port, "port"))).filter((port) => port !== null);
182776
+ return [text(get(spec, "type")) ?? "ClusterIP", ports.length === 0 ? "" : `port ${ports.slice(0, 3).join(", ")}`].filter((part) => part !== "").join(", ");
182777
+ }
182778
+ if (kind === "Ingress") return text(get(items(get(spec, "rules"))[0], "host")) ?? names5(get(items(get(spec, "tls"))[0], "hosts"))[0] ?? null;
182779
+ if (kind === "PersistentVolumeClaim") return text(at2(spec, ["resources", "requests", "storage"]));
182780
+ return null;
182781
+ }
182782
+ function readManifest(root) {
182783
+ const kind = text(get(root, "kind"));
182784
+ const name2 = text(at2(root, ["metadata", "name"]));
182785
+ if (kind === null || name2 === null) return null;
182786
+ const spec = get(root, "spec");
182787
+ const templatePath = WORKLOAD_TEMPLATES[kind];
182788
+ const template = templatePath === void 0 ? kind === "Pod" ? root : void 0 : at2(root, templatePath);
182789
+ const podSpec = get(template, "spec");
182790
+ const firstContainer = items(get(podSpec, "containers"))[0];
182791
+ return {
182792
+ kind,
182793
+ name: name2,
182794
+ namespace: text(at2(root, ["metadata", "namespace"])) ?? "default",
182795
+ line: root.line,
182796
+ column: root.column,
182797
+ labels: template !== void 0 ? stringMap(at2(template, ["metadata", "labels"])) : kind === "Service" ? stringMap(get(spec, "selector")) : /* @__PURE__ */ new Map(),
182798
+ meta: stringMap(at2(root, ["metadata", "labels"])),
182799
+ image: text(get(firstContainer, "image")),
182800
+ replicas: text(get(spec, "replicas")),
182801
+ uses: podSpec === void 0 ? [] : podUses(podSpec),
182802
+ routes: kind === "Ingress" ? ingressRoutes(spec) : [],
182803
+ detail: detailOf(kind, spec),
182804
+ values: valuesOf(kind, root, podSpec)
182805
+ };
182806
+ }
182807
+ function manifestRoots(roots) {
182808
+ const out = [];
182809
+ for (const root of roots) {
182810
+ if (text(get(root, "kind")) === "List") out.push(...items(get(root, "items")));
182811
+ else out.push(root);
182812
+ }
182813
+ return out;
182814
+ }
182815
+ var WORKLOAD_TEMPLATES, DRAWN_KINDS, QUIET_KINDS, MAX_VALUE_LENGTH, at2;
182816
+ var init_manifests = __esm({
182817
+ "../../packages/diagrams/src/importers/kubernetes/manifests.ts"() {
182818
+ "use strict";
182819
+ init_yaml();
182820
+ WORKLOAD_TEMPLATES = {
182821
+ Deployment: ["spec", "template"],
182822
+ StatefulSet: ["spec", "template"],
182823
+ DaemonSet: ["spec", "template"],
182824
+ ReplicaSet: ["spec", "template"],
182825
+ Job: ["spec", "template"],
182826
+ CronJob: ["spec", "jobTemplate", "spec", "template"]
182827
+ };
182828
+ DRAWN_KINDS = /* @__PURE__ */ new Set([...Object.keys(WORKLOAD_TEMPLATES), "Pod", "Service", "Ingress", "ConfigMap", "Secret", "PersistentVolumeClaim"]);
182829
+ QUIET_KINDS = /* @__PURE__ */ new Set([
182830
+ "Namespace",
182831
+ "ServiceAccount",
182832
+ "Role",
182833
+ "ClusterRole",
182834
+ "RoleBinding",
182835
+ "ClusterRoleBinding",
182836
+ "HorizontalPodAutoscaler",
182837
+ "PodDisruptionBudget",
182838
+ "NetworkPolicy",
182839
+ "LimitRange",
182840
+ "ResourceQuota",
182841
+ "PriorityClass",
182842
+ "ServiceMonitor",
182843
+ "PodMonitor",
182844
+ "VerticalPodAutoscaler",
182845
+ "Certificate",
182846
+ "IngressClass",
182847
+ "StorageClass"
182848
+ ]);
182849
+ MAX_VALUE_LENGTH = 500;
182850
+ at2 = (node3, path) => path.reduce((current, key) => get(current, key), node3);
182851
+ }
182852
+ });
182853
+
182854
+ // ../../packages/diagrams/src/importers/kubernetes/index.ts
182855
+ function kindLook(kind) {
182856
+ const look = LOOKS[kind];
182857
+ return look === void 0 ? { type: "service", icon: null } : { type: look.type, icon: look.icon };
182858
+ }
182859
+ function namespaceType(name2) {
182860
+ const phrase2 = name2.toLowerCase().replace(/[-_]+/g, " ").trim();
182861
+ const matches3 = GROUP_TYPES.filter((type) => GROUP_TYPE_SPECS[type].phrases.includes(phrase2));
182862
+ return matches3.length === 1 ? matches3[0] : null;
182863
+ }
182864
+ function nodeOf(manifest, grouped, serviceDetail) {
182865
+ const look = LOOKS[manifest.kind] ?? { type: "service", icon: null, rank: 2 };
182866
+ const image = manifest.image === null ? null : imageLook(manifest.image);
182867
+ const replicas = manifest.replicas !== null && manifest.replicas !== "1" ? `, ${manifest.replicas} replicas` : "";
182868
+ const detail = (serviceDetail ?? manifest.detail) === null ? "" : `, ${serviceDetail ?? manifest.detail}`;
182869
+ return {
182870
+ key: keyOf(manifest.kind, manifest.namespace, manifest.name),
182871
+ type: image?.type ?? look.type,
182872
+ label: manifest.name,
182873
+ sub: `${manifest.kind}${replicas}${detail}`,
182874
+ // A workload whose image this reader does not know may still be named after a product, such as kafka; lint's own rule finds it.
182875
+ icon: image?.icon ?? look.icon ?? iconForName(manifest.name, image?.type ?? look.type),
182876
+ group: grouped ? `ns:${manifest.namespace}` : null,
182877
+ idHint: manifest.name
182878
+ };
182879
+ }
182880
+ function namesHost(value, host) {
182881
+ if (host === "") return false;
182882
+ for (let index2 = value.indexOf(host); index2 !== -1; index2 = value.indexOf(host, index2 + 1)) {
182883
+ const before = index2 === 0 ? "" : value.charAt(index2 - 1);
182884
+ const after = value.charAt(index2 + host.length);
182885
+ if (!/[A-Za-z0-9.-]/.test(before) && !/[A-Za-z0-9_-]/.test(after)) return true;
182886
+ }
182887
+ return false;
182888
+ }
182889
+ function foldedServices(drawn) {
182890
+ const folded = /* @__PURE__ */ new Map();
182891
+ for (const service of drawn) {
182892
+ if (service.kind !== "Service") continue;
182893
+ const workloads = selected(service, drawn);
182894
+ const only = workloads[0];
182895
+ if (workloads.length === 1 && only !== void 0) folded.set(keyOf("Service", service.namespace, service.name), keyOf(only.kind, only.namespace, only.name));
182896
+ }
182897
+ return folded;
182898
+ }
182899
+ function edgesOf(drawn, folded, context) {
182900
+ const edges = [];
182901
+ const has = (kind, namespace, name2) => drawn.some((other) => other.kind === kind && other.namespace === namespace && other.name === name2);
182902
+ const unresolved = (manifest, what) => {
182903
+ context.report.add(DIAGNOSTIC_CODES.importUnresolved, spanAt3(manifest.line, manifest.column), `${manifest.kind} ${manifest.name} names the ${what}, which is not in the text.`, "Include its manifest, or leave it out if it is created elsewhere; the arrow was not drawn.");
182904
+ };
182905
+ for (const manifest of drawn) {
182906
+ const from = keyOf(manifest.kind, manifest.namespace, manifest.name);
182907
+ for (const route of manifest.routes) {
182908
+ const service = keyOf("Service", manifest.namespace, route.service);
182909
+ if (has("Service", manifest.namespace, route.service)) edges.push({ from, to: folded.get(service) ?? service, label: route.label });
182910
+ else unresolved(manifest, `Service ${route.service}`);
182911
+ }
182912
+ if (manifest.kind === "Service" && !folded.has(from)) {
182913
+ for (const workload of selected(manifest, drawn)) edges.push({ from, to: keyOf(workload.kind, workload.namespace, workload.name) });
182914
+ }
182915
+ if (manifest.image !== null) {
182916
+ const read = drawn.filter((other) => other.kind === "ConfigMap" && other.namespace === manifest.namespace && manifest.uses.some((use) => use.kind === "ConfigMap" && use.name === other.name));
182917
+ const values = [...manifest.values, ...read.flatMap((config2) => config2.values)];
182918
+ for (const service of drawn) {
182919
+ const host = service.namespace === manifest.namespace ? service.name : `${service.name}.${service.namespace}`;
182920
+ if (service.kind !== "Service" || !values.some((value) => namesHost(value, host))) continue;
182921
+ const key = keyOf("Service", service.namespace, service.name);
182922
+ const to = folded.get(key) ?? key;
182923
+ if (to !== from) edges.push({ from, to });
182924
+ }
182925
+ }
182926
+ for (const use of manifest.uses) {
182927
+ if (has(use.kind, manifest.namespace, use.name)) edges.push({ from, to: keyOf(use.kind, manifest.namespace, use.name), arrow: use.kind === "PersistentVolumeClaim" ? "->" : "-->", label: use.kind === "PersistentVolumeClaim" ? "mounts" : "reads" });
182928
+ else unresolved(manifest, `${use.kind} ${use.name}`);
182929
+ }
182930
+ }
182931
+ return edges;
182932
+ }
182933
+ function readKubernetes(textIn, context) {
182934
+ const read = parseYaml(textIn);
182935
+ if (read.error !== null) {
182936
+ context.report.add(DIAGNOSTIC_CODES.importUnreadable, spanAt3(read.error.line, read.error.column), read.error.message, read.error.hint);
182937
+ return null;
182938
+ }
182939
+ return drawManifests(read.documents.map((document2) => document2.root).filter((root) => root !== null), context, null);
182940
+ }
182941
+ function drawManifests(documents, context, framing) {
182942
+ const roots = manifestRoots(documents);
182943
+ const drawn = [];
182944
+ const unknown2 = [];
182945
+ for (const root of roots) {
182946
+ const manifest = readManifest(root);
182947
+ if (manifest === null || QUIET_KINDS.has(manifest.kind)) continue;
182948
+ if (DRAWN_KINDS.has(manifest.kind)) {
182949
+ if (!drawn.some((other) => other.kind === manifest.kind && other.namespace === manifest.namespace && other.name === manifest.name)) drawn.push(manifest);
182950
+ } else unknown2.push(manifest);
182951
+ }
182952
+ const firstUnknown = unknown2[0];
182953
+ if (firstUnknown !== void 0) {
182954
+ const kinds2 = [...new Set(unknown2.map((manifest) => manifest.kind))];
182955
+ context.report.add(
182956
+ DIAGNOSTIC_CODES.importSkipped,
182957
+ spanAt3(firstUnknown.line, firstUnknown.column),
182958
+ `${countOf2(unknown2.length, "object")} of a kind this reader does not draw ${unknown2.length === 1 ? "was" : "were"} left out: ${kinds2.slice(0, 6).join(", ")}.`,
182959
+ "The reader draws workloads, Services, Ingresses, ConfigMaps, Secrets, and PersistentVolumeClaims. Add the others by hand if they matter."
182960
+ );
182961
+ }
182962
+ if (drawn.length === 0) {
182963
+ context.report.add(DIAGNOSTIC_CODES.importNoShape, spanAt3(1, 1), "No workload, Service, Ingress, ConfigMap, Secret, or claim was found.", "Send manifests with apiVersion, kind, and metadata.name, separated by ---.");
182964
+ return null;
182965
+ }
182966
+ const ordered = [...drawn].sort((a, b) => (LOOKS[a.kind]?.rank ?? 2) - (LOOKS[b.kind]?.rank ?? 2));
182967
+ const namespaces = [...new Set(ordered.map((manifest) => manifest.namespace))];
182968
+ const grouped = namespaces.length > 1;
182969
+ const groups = grouped ? namespaces.map((namespace) => ({ key: `ns:${namespace}`, label: namespace, type: namespaceType(namespace), sub: "Namespace", idHint: `ns_${namespace}` })) : [];
182970
+ const framed = /* @__PURE__ */ new Map();
182971
+ for (const manifest of framing === null ? [] : ordered) {
182972
+ let parent = grouped ? `ns:${manifest.namespace}` : null;
182973
+ for (const frame of framing?.framesOf(manifest) ?? []) {
182974
+ const key = grouped ? `${manifest.namespace}/${frame.key}` : frame.key;
182975
+ if (!groups.some((group2) => group2.key === key)) groups.push({ key, label: frame.label, sub: frame.sub, icon: frame.icon, parent, idHint: frame.idHint });
182976
+ parent = key;
182977
+ }
182978
+ if (parent !== null) framed.set(manifest, parent);
182979
+ }
182980
+ const only = namespaces[0] ?? "default";
182981
+ const folded = foldedServices(ordered);
182982
+ const servicePorts = /* @__PURE__ */ new Map();
182983
+ for (const manifest of ordered) {
182984
+ const workload = folded.get(keyOf(manifest.kind, manifest.namespace, manifest.name));
182985
+ const ports = manifest.detail?.replace(/^ClusterIP(, )?/, "") ?? "";
182986
+ if (workload !== void 0 && ports !== "" && !servicePorts.has(workload)) servicePorts.set(workload, ports);
182987
+ }
182988
+ const boxes2 = ordered.filter((manifest) => !folded.has(keyOf(manifest.kind, manifest.namespace, manifest.name)));
182989
+ const workloads = boxes2.filter((manifest) => manifest.image !== null).length;
182990
+ const others = boxes2.length - workloads;
182991
+ return writeFlow(
182992
+ {
182993
+ title: context.title ?? framing?.title ?? (grouped || only === "default" ? "Kubernetes workloads" : `Kubernetes namespace ${only}`),
182994
+ desc: `${countOf2(workloads, "workload")}${others > 0 ? ` and ${countOf2(others, "object")} around ${workloads === 1 ? "it" : "them"}` : ""}${grouped ? ` in ${countOf2(namespaces.length, "namespace")}` : ""}, imported from ${framing?.source ?? "Kubernetes manifests"}. ${boxes2.some((manifest) => manifest.kind === "Ingress") ? "Traffic flows from each Ingress to the pods behind its Services" : "Solid arrows are calls to a Service"}, and dashed arrows are configuration the pods read.`,
182995
+ direction: "down",
182996
+ nodes: boxes2.map((manifest) => {
182997
+ const node3 = nodeOf(manifest, grouped, servicePorts.get(keyOf(manifest.kind, manifest.namespace, manifest.name)) ?? null);
182998
+ return framed.has(manifest) ? { ...node3, group: framed.get(manifest) ?? null } : node3;
182999
+ }),
183000
+ groups,
183001
+ edges: edgesOf(ordered, folded, context),
183002
+ noun: "objects",
183003
+ capSpan: (key) => {
183004
+ const manifest = ordered.find((candidate2) => keyOf(candidate2.kind, candidate2.namespace, candidate2.name) === key);
183005
+ return spanAt3(manifest?.line ?? 1, manifest?.column ?? 1);
183006
+ },
183007
+ advice: "Check the arrows from each Service: they follow its selector, so a selector that matches no pod template draws none. Label the Ingress arrows with their paths if they carry none."
183008
+ },
183009
+ context.report
183010
+ );
183011
+ }
183012
+ var LOOKS, keyOf, selects, selected, kubernetesImporter;
183013
+ var init_kubernetes = __esm({
183014
+ "../../packages/diagrams/src/importers/kubernetes/index.ts"() {
183015
+ "use strict";
183016
+ init_constants();
183017
+ init_core();
183018
+ init_shared2();
183019
+ init_yaml();
183020
+ init_manifests();
183021
+ LOOKS = {
183022
+ Ingress: { type: "gateway", icon: "lucide:globe", rank: 0 },
183023
+ Service: { type: "gateway", icon: "lucide:network", rank: 1 },
183024
+ Deployment: { type: "service", icon: null, rank: 2 },
183025
+ StatefulSet: { type: "service", icon: null, rank: 2 },
183026
+ DaemonSet: { type: "service", icon: null, rank: 2 },
183027
+ ReplicaSet: { type: "service", icon: null, rank: 2 },
183028
+ Pod: { type: "service", icon: null, rank: 2 },
183029
+ Job: { type: "function", icon: "lucide:play", rank: 3 },
183030
+ CronJob: { type: "function", icon: "lucide:calendar-clock", rank: 3 },
183031
+ PersistentVolumeClaim: { type: "storage", icon: "lucide:hard-drive", rank: 4 },
183032
+ ConfigMap: { type: "storage", icon: "lucide:settings-2", rank: 5 },
183033
+ Secret: { type: "secret", icon: "lucide:key-round", rank: 5 }
183034
+ };
183035
+ keyOf = (kind, namespace, name2) => `${namespace}/${kind}/${name2}`;
183036
+ selects = (selector, labels2) => selector.size > 0 && [...selector].every(([key, value]) => labels2.get(key) === value);
183037
+ selected = (service, drawn) => drawn.filter((workload) => workload.namespace === service.namespace && workload.kind !== "Service" && workload.image !== null && selects(service.labels, workload.labels));
183038
+ kubernetesImporter = { format: "kubernetes", kinds: ["flow"], joins: "\n---\n", extensions: [".yaml", ".yml"], read: readKubernetes };
183039
+ }
183040
+ });
183041
+
183042
+ // ../../packages/diagrams/src/importers/kustomize/folder.ts
183043
+ function resolvePath(folder, path) {
183044
+ const parts = folder === "" ? [] : folder.split("/");
183045
+ for (const part of path.split("/")) {
183046
+ if (part === "" || part === ".") continue;
183047
+ if (part === "..") {
183048
+ if (parts.length === 0) return null;
183049
+ parts.pop();
183050
+ } else parts.push(part);
183051
+ }
183052
+ return parts.join("/");
183053
+ }
183054
+ function kustomizationOf(root, folder, start2) {
183055
+ const patches = [];
183056
+ for (const entry2 of items(get(root, "patches"))) {
183057
+ const target = get(entry2, "target");
183058
+ patches.push({ path: text(entry2) ?? text(get(entry2, "path")), target: target === void 0 ? null : { kind: text(get(target, "kind")), name: text(get(target, "name")) } });
183059
+ }
183060
+ for (const key of ["patchesStrategicMerge", "patchesJson6902"]) {
183061
+ for (const entry2 of items(get(root, key))) {
183062
+ const target = get(entry2, "target");
183063
+ patches.push({ path: text(entry2) ?? text(get(entry2, "path")), target: target === void 0 ? null : { kind: text(get(target, "kind")), name: text(get(target, "name")) } });
183064
+ }
183065
+ }
183066
+ const generators = [
183067
+ ...items(get(root, "configMapGenerator")).map((entry2) => ({ name: text(get(entry2, "name")), kind: "ConfigMap" })),
183068
+ ...items(get(root, "secretGenerator")).map((entry2) => ({ name: text(get(entry2, "name")), kind: "Secret" }))
183069
+ ].filter((entry2) => entry2.name !== null);
183070
+ const images = items(get(root, "images")).map((entry2) => {
183071
+ const name2 = text(get(entry2, "name"));
183072
+ const tag = text(get(entry2, "newTag")) ?? text(get(entry2, "digest"));
183073
+ return name2 === null ? null : `${baseName2(text(get(entry2, "newName")) ?? name2)}${tag === null ? "" : `:${tag}`}`;
183074
+ }).filter((entry2) => entry2 !== null);
183075
+ const replicas = items(get(root, "replicas")).map((entry2) => `${text(get(entry2, "name")) ?? ""} ${text(get(entry2, "count")) ?? "?"} replicas`);
183076
+ const namespace = text(get(root, "namespace"));
183077
+ const prefix = text(get(root, "namePrefix"));
183078
+ const suffix = text(get(root, "nameSuffix"));
183079
+ const details = [namespace === null ? null : `namespace ${namespace}`, prefix === null ? null : `prefix ${prefix}`, suffix === null ? null : `suffix ${suffix}`, ...images, ...replicas].filter((entry2) => entry2 !== null && entry2 !== "");
183080
+ return { folder, start: start2, component: text(get(root, "kind")) === "Component", resources: [...names5(get(root, "resources")), ...names5(get(root, "bases"))], components: names5(get(root, "components")), patches, generators, details };
183081
+ }
183082
+ function drawFolder(files, context, index2) {
183083
+ const read = (file2) => {
183084
+ const parsed = parseYaml(file2.text);
183085
+ if (parsed.error !== null) {
183086
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, index2.span(file2.start), `${cleanText(file2.path, "A file")} is not read: ${parsed.error.message}`, parsed.error.hint);
183087
+ return null;
183088
+ }
183089
+ return parsed.documents.map((document2) => document2.root).filter((root) => root !== null);
183090
+ };
183091
+ const kustomizations = [];
183092
+ const objects = /* @__PURE__ */ new Map();
183093
+ for (const file2 of files) {
183094
+ const roots = read(file2);
183095
+ if (roots === null) continue;
183096
+ const isOwn = KUSTOMIZATION_NAMES.has(baseName2(file2.path)) || file2.path === "" && isKustomization(file2.text);
183097
+ if (isOwn && roots[0] !== void 0) kustomizations.push(kustomizationOf(roots[0], folderOf2(file2.path), file2.start));
183098
+ else objects.set(file2.path, objectsOf(roots));
183099
+ }
183100
+ if (kustomizations.length === 0) {
183101
+ context.report.add(DIAGNOSTIC_CODES.importNoShape, START, "No kustomization.yaml was found.", "Send the folder with its kustomization.yaml files, or the output of kustomize build.");
183102
+ return null;
183103
+ }
183104
+ const byFolder = new Map(kustomizations.map((entry2) => [entry2.folder, entry2]));
183105
+ const missing = [];
183106
+ const roleOf3 = (entry2) => entry2.component ? "Component" : entry2.resources.some((resource) => isRemote(resource) || byFolder.has(resolvePath(entry2.folder, resource) ?? "\0")) ? "Overlay" : "Base";
183107
+ const rank = { Overlay: 0, Component: 1, Base: 2 };
183108
+ const ordered = [...kustomizations].sort((a, b) => rank[roleOf3(a)] - rank[roleOf3(b)] || (a.folder < b.folder ? -1 : 1));
183109
+ const firstParts = (kustomizations[0]?.folder ?? "").split("/");
183110
+ let shared = 0;
183111
+ while (shared < firstParts.length && kustomizations.every((entry2) => entry2.folder.split("/")[shared] === firstParts[shared] && entry2.folder.split("/").length > shared + 1)) shared += 1;
183112
+ const common = shared === 0 ? "" : `${firstParts.slice(0, shared).join("/")}/`;
183113
+ const groups = [];
183114
+ const nodes = [];
183115
+ const edges = [];
183116
+ const own2 = (entry2) => `kustomization:${entry2.folder}`;
183117
+ const fileOf2 = (entry2, kind, name2, depth = 0) => {
183118
+ if (depth > 16) return null;
183119
+ for (const resource of entry2.resources) {
183120
+ const path = isRemote(resource) ? null : resolvePath(entry2.folder, resource);
183121
+ if (path === null) continue;
183122
+ const inner = byFolder.get(path);
183123
+ if (inner !== void 0) {
183124
+ const found = fileOf2(inner, kind, name2, depth + 1);
183125
+ if (found !== null) return found;
183126
+ } else if ((objects.get(path) ?? []).some((object2) => (kind === null || object2.kind === kind) && (name2 === null || object2.name === name2))) return path;
183127
+ }
183128
+ return null;
183129
+ };
183130
+ for (const entry2 of ordered) {
183131
+ const role = roleOf3(entry2);
183132
+ const frame = `frame:${entry2.folder}`;
183133
+ groups.push({ key: frame, label: entry2.folder.slice(common.length) || baseName2(entry2.folder) || "kustomization", sub: role, icon: "lucide:layers", idHint: entry2.folder.slice(common.length) || baseName2(entry2.folder) || "root" });
183134
+ nodes.push({ key: own2(entry2), type: "service", label: baseName2(entry2.folder) || "kustomization", sub: cleanText(entry2.details.length === 0 ? "kustomization.yaml" : entry2.details.slice(0, 4).join(", ")), icon: "lucide:layers", group: frame, idHint: `${baseName2(entry2.folder) || "kustomization"}_kustomization` });
183135
+ for (const resource of entry2.resources) {
183136
+ if (isRemote(resource)) {
183137
+ nodes.push({ key: `remote:${resource}`, type: "service", label: cleanText(resource.replace(/^https?:\/\//, "").replace(/\?.*$/, "").split("/").slice(-2).join("/")), sub: "Remote resource", icon: "lucide:globe", group: frame, idHint: "remote" });
183138
+ edges.push({ from: own2(entry2), to: `remote:${resource}`, label: "builds on" });
183139
+ continue;
183140
+ }
183141
+ const path = resolvePath(entry2.folder, resource);
183142
+ const inner = path === null ? void 0 : byFolder.get(path);
183143
+ if (inner !== void 0) {
183144
+ edges.push({ from: own2(entry2), to: own2(inner), label: "builds on" });
183145
+ continue;
183146
+ }
183147
+ const found = path === null ? void 0 : objects.get(path);
183148
+ if (path === null || found === void 0) {
183149
+ missing.push(`${entry2.folder === "" ? "" : `${entry2.folder}/`}${resource}`);
183150
+ continue;
183151
+ }
183152
+ const first = found[0];
183153
+ const look = kindLook(first?.kind ?? "");
183154
+ nodes.push({ key: `file:${path}`, type: look.type, label: first === void 0 ? baseName2(path) : first.name, sub: cleanText(found.length <= 1 ? `${first?.kind ?? "No object"}, ${baseName2(path)}` : `${countOf2(found.length, "object")}, ${baseName2(path)}`), icon: look.icon, group: frame, idHint: baseName2(path).replace(/\.ya?ml$/, "") });
183155
+ }
183156
+ for (const component of entry2.components) {
183157
+ const inner = byFolder.get(resolvePath(entry2.folder, component) ?? "\0");
183158
+ if (inner !== void 0) edges.push({ from: own2(entry2), to: own2(inner), label: "includes" });
183159
+ else missing.push(`${entry2.folder === "" ? "" : `${entry2.folder}/`}${component}`);
183160
+ }
183161
+ for (const generator of entry2.generators) {
183162
+ const look = kindLook(generator.kind);
183163
+ nodes.push({ key: `generator:${entry2.folder}/${generator.kind}/${generator.name}`, type: look.type, label: generator.name, sub: `${generator.kind} generator`, icon: look.icon, group: frame, idHint: generator.name });
183164
+ }
183165
+ for (const patch of entry2.patches) {
183166
+ const path = patch.path === null ? null : resolvePath(entry2.folder, patch.path);
183167
+ const named = path === null ? void 0 : objects.get(path)?.[0];
183168
+ const kind = patch.target?.kind ?? named?.kind ?? null;
183169
+ const name2 = patch.target?.name ?? named?.name ?? null;
183170
+ const key = `patch:${entry2.folder}/${patch.path ?? `${kind ?? "any"}/${name2 ?? "any"}`}`;
183171
+ nodes.push({ key, type: "service", label: patch.path === null ? `${kind ?? "object"} ${name2 ?? ""}`.trim() : baseName2(patch.path), sub: cleanText(`Patch${kind === null ? "" : ` of ${kind}${name2 === null ? "" : ` ${name2}`}`}`), icon: "lucide:file-code", group: frame, idHint: `${baseName2(patch.path ?? "patch").replace(/\.ya?ml$/, "")}_patch` });
183172
+ const target = kind === null && name2 === null ? null : fileOf2(entry2, kind, name2) ?? [...objects].find(([, inside]) => inside.some((object2) => (kind === null || object2.kind === kind) && (name2 === null || object2.name === name2)))?.[0] ?? null;
183173
+ if (target !== null) edges.push({ from: key, to: `file:${target}`, label: "patches" });
183174
+ }
183175
+ }
183176
+ const drawnFiles = [...objects].filter(([path]) => nodes.some((node3) => node3.key === `file:${path}`));
183177
+ for (const [path, manifests] of drawnFiles) {
183178
+ for (const manifest of manifests) {
183179
+ for (const use of manifest.uses) {
183180
+ for (const node3 of nodes) if (node3.key.startsWith("generator:") && node3.key.endsWith(`/${use.kind}/${use.name}`)) edges.push({ from: `file:${path}`, to: node3.key, arrow: "-->", label: "reads" });
183181
+ const holder = drawnFiles.find(([, others]) => others.some((other) => other.kind === use.kind && other.name === use.name));
183182
+ if (holder !== void 0 && holder[0] !== path) edges.push({ from: `file:${path}`, to: `file:${holder[0]}`, arrow: "-->", label: use.kind === "PersistentVolumeClaim" ? "mounts" : "reads" });
183183
+ }
183184
+ for (const [other, inside] of drawnFiles) {
183185
+ if (other === path) continue;
183186
+ if (manifest.kind === "Service" && inside.some((workload) => workload.image !== null && selects(manifest.labels, workload.labels))) edges.push({ from: `file:${path}`, to: `file:${other}` });
183187
+ if (manifest.image !== null && inside.some((service) => service.kind === "Service" && manifest.values.some((value) => namesHost(value, service.name)))) edges.push({ from: `file:${path}`, to: `file:${other}` });
183188
+ }
183189
+ }
183190
+ }
183191
+ if (missing.length > 0) {
183192
+ context.report.add(DIAGNOSTIC_CODES.importUnresolved, START, `${countOf2(missing.length, "entry", "entries")} of the kustomizations ${missing.length === 1 ? "names a file that was" : "name files that were"} not sent, so ${missing.length === 1 ? "it is" : "they are"} not drawn: ${missing.slice(0, 3).map((entry2) => cleanText(entry2)).join(", ")}.`, "Send every file of the folder, with each base and component.");
183193
+ }
183194
+ const overlays = ordered.filter((entry2) => roleOf3(entry2) === "Overlay").length;
183195
+ return writeFlow(
183196
+ {
183197
+ title: context.title ?? "Kustomize overlays",
183198
+ desc: `${countOf2(kustomizations.length, "kustomization")}, ${countOf2(overlays, "overlay")} among them, imported from a Kustomize folder. Each frame is a kustomization with the files, patches, and generators it lists; an arrow shows what an overlay builds on or includes, and what a patch changes.`,
183199
+ direction: "down",
183200
+ nodes,
183201
+ groups,
183202
+ edges,
183203
+ noun: "entries",
183204
+ capSpan: () => index2.span(ordered[0]?.start ?? 0),
183205
+ advice: "Import the output of kustomize build for an overlay as kustomize to draw the objects it makes."
183206
+ },
183207
+ context.report
183208
+ );
183209
+ }
183210
+ var KUSTOMIZATION_NAMES, isKustomization, baseName2, folderOf2, isRemote, objectsOf;
183211
+ var init_folder = __esm({
183212
+ "../../packages/diagrams/src/importers/kustomize/folder.ts"() {
183213
+ "use strict";
183214
+ init_core();
183215
+ init_kubernetes();
183216
+ init_manifests();
183217
+ init_shared2();
183218
+ init_yaml();
183219
+ KUSTOMIZATION_NAMES = /* @__PURE__ */ new Set(["kustomization.yaml", "kustomization.yml", "Kustomization"]);
183220
+ isKustomization = (textIn) => /^apiVersion\s*:\s*["']?kustomize\.config\.k8s\.io\//m.test(textIn);
183221
+ baseName2 = (path) => path.slice(path.lastIndexOf("/") + 1);
183222
+ folderOf2 = (path) => path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
183223
+ isRemote = (entry2) => /^(?:https?:\/\/|git@|github\.com\/|gitlab\.com\/|bitbucket\.org\/|ssh:\/\/)/.test(entry2) || /\?ref=/.test(entry2);
183224
+ objectsOf = (roots) => manifestRoots(roots).flatMap((node3) => readManifest(node3) ?? []);
183225
+ }
183226
+ });
183227
+
183228
+ // ../../packages/diagrams/src/importers/detect.ts
183229
+ function graphCommits(text3) {
183230
+ let count3 = 0;
183231
+ for (const line3 of text3.split("\n")) {
183232
+ let index2 = 0;
183233
+ while (index2 < line3.length && "*|\\/ _".includes(line3.charAt(index2))) index2 += 1;
183234
+ if (line3.slice(0, index2).includes("*") && /^[0-9a-f]{7,40}\s/.test(line3.slice(index2))) count3 += 1;
183235
+ }
183236
+ return count3;
183237
+ }
183238
+ function everyLine(text3, test) {
183239
+ const lines = filled(text3);
183240
+ return lines.length >= 3 && lines.every((line3) => test(line3.trim()));
183241
+ }
183242
+ function isOutline(text3) {
183243
+ const lines = filled(text3);
183244
+ return lines.length >= 3 && lines.every((line3) => /^\s*[-*+] \S/.test(line3)) && lines.some((line3) => /^\s+[-*+] /.test(line3));
183245
+ }
183246
+ function separators(line3, separator) {
183247
+ let count3 = 0;
183248
+ let quoted5 = false;
183249
+ for (const char of line3) {
183250
+ if (char === '"') quoted5 = !quoted5;
183251
+ else if (char === separator && !quoted5) count3 += 1;
183252
+ }
183253
+ return count3;
183254
+ }
183255
+ function isTable(text3) {
183256
+ const lines = filled(text3).slice(0, 6);
183257
+ if (lines.length < 2) return false;
183258
+ for (const separator of [",", " ", ";"]) {
183259
+ const counts = lines.map((line3) => separators(line3, separator));
183260
+ if ((counts[0] ?? 0) > 0 && counts.every((count3) => count3 === counts[0])) return true;
183261
+ }
183262
+ return false;
183263
+ }
183264
+ function importFormatFor(request, data) {
183265
+ const fromData = detectImportFormat(data);
183266
+ if (fromData !== null && SURE_FROM_DATA.has(fromData)) return fromData;
183267
+ return NAMES.find((name2) => name2.words.test(request.slice(0, SNIFF_LENGTH)))?.format ?? null;
183268
+ }
183269
+ function detectImportFormat(text3) {
183270
+ const head = text3.slice(0, SNIFF_LENGTH);
183271
+ if (head.trim() === "") return null;
183272
+ return SIGNS.find((sign) => sign.test(head))?.format ?? null;
183273
+ }
183274
+ var SNIFF_LENGTH, SIGNS, filled, NAMES, SURE_FROM_DATA;
183275
+ var init_detect = __esm({
183276
+ "../../packages/diagrams/src/importers/detect.ts"() {
183277
+ "use strict";
183278
+ init_chart();
183279
+ init_folder();
183280
+ SNIFF_LENGTH = 2e4;
183281
+ SIGNS = [
183282
+ { format: "sql", test: (text3) => /^\s*create\s+(?:or\s+replace\s+)?(?:temporary\s+|temp\s+|unlogged\s+)?table\b/im.test(text3) },
183283
+ { format: "prisma", test: (text3) => /^\s*model\s+\w+\s*\{/m.test(text3) && /@id\b|@relation\b|^\s*datasource\s+\w+\s*\{/m.test(text3) },
183284
+ { format: "drizzle", test: (text3) => /\b(?:pgTable|sqliteTable|mysqlTable)\s*\(/.test(text3) },
183285
+ { format: "openapi", test: (text3) => /^\s*["']?openapi["']?\s*:\s*["']?3\./m.test(text3) || /"openapi"\s*:\s*"3\./.test(text3) },
183286
+ { format: "terraform-plan", test: (text3) => /^\s*\{\s*"format_version"\s*:/.test(text3) && /"terraform_version"\s*:/.test(text3) && /"(?:planned_values|resource_changes|root_module)"\s*:/.test(text3) },
183287
+ { format: "terraform", test: (text3) => /^\s*resource\s+"[A-Za-z0-9_]+"\s+"[^"]+"\s*\{/m.test(text3) },
183288
+ { format: "bicep", test: (text3) => /^\s*(?:resource|module)\s+[A-Za-z_]\w*\s+'[^'\n]+'\s*(?:existing\s*)?=/m.test(text3) || /^\s*targetScope\s*=\s*'/m.test(text3) },
183289
+ { format: "arm", test: (text3) => /"\$schema"\s*:\s*"https?:\/\/schema\.management\.azure\.com\/schemas\/[^"]*(?:deploymentTemplate|DeploymentTemplate)/.test(text3) },
183290
+ { format: "pulumi", test: (text3) => /^runtime\s*:\s*(?:yaml|\{\s*name\s*:\s*yaml)/m.test(text3) && /^resources\s*:/m.test(text3) || /"urn"\s*:\s*"urn:pulumi:/.test(text3) },
183291
+ { format: "cdk", test: (text3) => /"?aws:cdk:path"?\s*:/.test(text3) || /"artifacts"\s*:/.test(text3) && /"aws:cloudformation:stack"/.test(text3) },
183292
+ { format: "cloudformation", test: (text3) => /^\s*"?AWSTemplateFormatVersion"?\s*:/m.test(text3) || /^Transform\s*:\s*AWS::Serverless/m.test(text3) || /^\s*"?Resources"?\s*:/m.test(text3) && /"?Type"?\s*:\s*["']?AWS::[A-Za-z0-9]+::/.test(text3) },
183293
+ { format: "backstage", test: (text3) => /^apiVersion\s*:\s*["']?backstage\.io\//m.test(text3) },
183294
+ // A kustomization, or what kustomize build makes of one: a generated ConfigMap or Secret whose name ends in its hash.
183295
+ { format: "kustomize", test: (text3) => isKustomization(text3) || /^kind\s*:\s*(?:ConfigMap|Secret)\s*$/m.test(text3) && /^\s+name\s*:\s*[\w.-]+-(?=[^-\s]*\d)[b-df-hj-np-tv-z0-9]{10}\s*$/m.test(text3) },
183296
+ { format: "helm", test: (text3) => /^\s*apiVersion\s*:/m.test(text3) && /^\s*kind\s*:\s*\w+/m.test(text3) && (/^# Source: [^\n]*\/templates\//m.test(text3) || /^\s*helm\.sh\/chart\s*:/m.test(text3)) || isChartFile(text3) },
183297
+ { format: "kubernetes", test: (text3) => /^\s*apiVersion\s*:/m.test(text3) && /^\s*kind\s*:\s*\w+/m.test(text3) },
183298
+ { format: "github-actions", test: (text3) => /^jobs\s*:/m.test(text3) && (/^\s*runs-on\s*:/m.test(text3) || /^["']?on["']?\s*:/m.test(text3)) },
183299
+ { format: "compose", test: (text3) => /^services\s*:/m.test(text3) && /^\s+(?:image|build)\s*:/m.test(text3) },
183300
+ { format: "tree", test: (text3) => /(?:├──|└──|\|-- |`-- )/.test(text3) || everyLine(text3, (line3) => /^[^\s/]+(?:\/[^\s/]+)+\/?$/.test(line3)) || isOutline(text3) },
183301
+ { format: "git-log", test: (text3) => /^commit [0-9a-f]{7,40}\b/m.test(text3) || graphCommits(text3) >= 2 || everyLine(text3, (line3) => /^[0-9a-f]{7,40} \S/.test(line3)) },
183302
+ { format: "json", test: (text3) => /^\s*[[{]/.test(text3) && /[\]}]\s*$/.test(text3) },
183303
+ { format: "csv", test: (text3) => isTable(text3) }
183304
+ ];
183305
+ filled = (text3) => text3.split("\n", 200).map((line3) => line3.trimEnd()).filter((line3) => line3.trim() !== "");
183306
+ NAMES = [
183307
+ { format: "compose", words: /\b(?:docker[- ]compose|compose (?:file|yaml|yml|stack))\b/i },
183308
+ { format: "backstage", words: /\b(?:backstage|catalog-info(?:\.yaml)?|software catalog)\b/i },
183309
+ { format: "kustomize", words: /\b(?:kustomize|kustomization(?:\.yaml)?)\b/i },
183310
+ { format: "helm", words: /\b(?:helm (?:charts?|template|release)|chart\.yaml)\b/i },
183311
+ { format: "kubernetes", words: /\b(?:kubernetes|k8s)\b/i },
183312
+ { format: "github-actions", words: /\b(?:github actions?|workflow (?:file|yaml|yml)|ci workflow)\b/i },
183313
+ { format: "terraform-plan", words: /\b(?:terraform|tofu) (?:plan|state|show -json)\b|\bplan\.json\b/i },
183314
+ { format: "terraform", words: /\b(?:terraform|opentofu|hcl|\.tf files?)\b/i },
183315
+ { format: "bicep", words: /\b(?:bicep|\.bicep files?)\b/i },
183316
+ { format: "arm", words: /\b(?:arm templates?|azure resource manager|azuredeploy\.json)\b/i },
183317
+ { format: "pulumi", words: /\bpulumi\b/i },
183318
+ { format: "cdk", words: /\b(?:aws cdk|cdk (?:app|synth|stacks?)|cdk\.out|cloud assembly)\b/i },
183319
+ { format: "cloudformation", words: /\b(?:cloudformation|cfn templates?|sam templates?|serverless application model)\b/i },
183320
+ { format: "openapi", words: /\b(?:openapi|swagger|api spec(?:ification)?)\b/i },
183321
+ { format: "prisma", words: /\bprisma\b/i },
183322
+ { format: "drizzle", words: /\bdrizzle\b/i },
183323
+ { format: "sql", words: /\b(?:sql|ddl|create table|database schema dump)\b/i },
183324
+ { format: "git-log", words: /\bgit (?:log|history)\b/i },
183325
+ { format: "csv", words: /\b(?:csv|tsv|spreadsheet export)\b/i },
183326
+ { format: "json", words: /\bjson (?:data|file|records|export)\b/i },
183327
+ { format: "tree", words: /\b(?:tree output|folder (?:structure|tree|listing)|directory (?:tree|listing))\b/i }
183328
+ ];
183329
+ SURE_FROM_DATA = /* @__PURE__ */ new Set(["sql", "prisma", "drizzle", "openapi", "terraform", "terraform-plan", "bicep", "arm", "pulumi", "cloudformation", "cdk", "compose", "kubernetes", "helm", "kustomize", "backstage", "github-actions", "git-log"]);
183330
+ }
183331
+ });
183332
+
183333
+ // ../../packages/diagrams/src/importers/terraform/azure-families.ts
183334
+ function azureFamilyIcon(type) {
183335
+ if (!type.startsWith("azurerm_")) return null;
183336
+ const name2 = type.slice("azurerm_".length);
183337
+ let best;
183338
+ for (const family of AZURE_FAMILIES) if (name2.startsWith(family[0]) && family[0].length > (best?.[0].length ?? -1)) best = family;
183339
+ return best?.[1] ?? null;
183340
+ }
183341
+ var AZURE_FAMILIES;
183342
+ var init_azure_families = __esm({
183343
+ "../../packages/diagrams/src/importers/terraform/azure-families.ts"() {
183344
+ "use strict";
183345
+ AZURE_FAMILIES = [
183346
+ ["aadb2c_", "azure:ad-b2c"],
183347
+ ["active_directory_domain_service", "azure:entra-domain-services"],
183348
+ ["advanced_threat_protection", "azure:defender-for-cloud"],
183349
+ ["attestation_", "azure:azureattestation"],
183350
+ ["automanage_", "azure:automanaged-vm"],
183351
+ ["automation_", "azure:automation-accounts"],
183352
+ ["backup_", "azure:recovery-services-vaults"],
183353
+ ["batch_", "azure:batch-accounts"],
183354
+ ["bot_", "azure:bot-services"],
183355
+ ["cognitive_", "azure:cognitive-services"],
183356
+ ["container_connected_registry", "azure:container-registries"],
183357
+ ["container_group", "azure:container-instances"],
183358
+ ["cosmosdb_postgresql_", "azure:database-postgresql-server-group"],
183359
+ ["cosmosdb_", "azure:cosmos-db"],
183360
+ ["custom_provider", "azure:resource-groups"],
183361
+ ["data_protection_", "azure:backup-vault"],
183362
+ ["database_migration_", "azure:database-migration-services"],
183363
+ ["dev_center", "azure:dev-box"],
183364
+ ["dev_test_", "azure:devtest-labs"],
183365
+ ["elastic_cloud_", "simple:elasticsearch"],
183366
+ ["eventgrid_domain", "azure:event-grid-domains"],
183367
+ ["eventgrid_", "azure:event-grid"],
183368
+ ["eventhub_cluster", "azure:event-hub-clusters"],
183369
+ ["eventhub", "azure:event-hubs"],
183370
+ ["express_route_", "azure:expressroute-circuits"],
183371
+ ["fabric_", "azure:power-bi-embedded"],
183372
+ ["federated_identity_credential", "azure:managed-identities"],
183373
+ ["frontdoor", "azure:front-door"],
183374
+ ["gallery_application", "azure:image-definitions"],
183375
+ ["graph_services_", "azure:resource-graph-explorer"],
183376
+ ["hdinsight_", "azure:hd-insight-clusters"],
183377
+ ["healthbot", "azure:bot-services"],
183378
+ ["healthcare_", "azure:fhir-service"],
183379
+ ["iotcentral_", "azure:iot-central-applications"],
183380
+ ["iothub", "azure:iot-hub"],
183381
+ ["kubernetes_automatic", "azure:aks-automatic"],
183382
+ ["kubernetes_fleet", "azure:kubernetes-fleet-manager"],
183383
+ ["kubernetes_", "azure:kubernetes-services"],
183384
+ ["kusto_", "azure:data-explorer-clusters"],
183385
+ ["maintenance_", "azure:maintenance-configuration"],
183386
+ ["managed_application", "azure:managed-applications-center"],
183387
+ ["management_lock", "azure:resource-groups"],
183388
+ ["mongo_cluster", "azure:cosmos-db"],
183389
+ ["mssql_elasticpool", "azure:sql-elastic-pools"],
183390
+ ["mssql_job", "azure:elastic-job-agents"],
183391
+ ["mssql_managed", "azure:sql-managed-instance"],
183392
+ ["mssql_", "azure:sql-database"],
183393
+ ["mysql_", "azure:database-for-mysql"],
183394
+ ["netapp_", "azure:netapp-files"],
183395
+ ["network_function_", "azure:network-function-manager"],
183396
+ ["network_profile", "azure:virtual-network"],
183397
+ ["nginx_", "simple:nginx"],
183398
+ ["oracle_", "azure:oracle-database"],
183399
+ ["pim_", "azure:entra-identity-roles-and-administrators"],
183400
+ ["playwright_", "azure:load-testing"],
183401
+ ["point_to_site_vpn_gateway", "azure:virtual-network-gateways"],
183402
+ ["policy_", "azure:policy"],
183403
+ ["portal_", "azure:management-portal"],
183404
+ ["powerbi_", "azure:power-bi-embedded"],
183405
+ ["purview_", "azure:identity-governance"],
183406
+ ["redhat_openshift", "azure:red-hat-openshift"],
183407
+ ["redis_", "azure:cache-redis"],
183408
+ ["resource_deployment_script", "azure:templates"],
183409
+ ["resource_policy", "azure:policy"],
183410
+ ["resource_provider_", "azure:subscriptions"],
183411
+ ["role_definition", "azure:entra-identity-custom-roles"],
183412
+ ["role_management", "azure:entra-identity-roles-and-administrators"],
183413
+ ["route_map", "azure:virtual-router"],
183414
+ ["route_server", "azure:virtual-router"],
183415
+ ["route", "azure:route-tables"],
183416
+ ["service_fabric", "azure:service-fabric-clusters"],
183417
+ ["site_recovery_", "azure:recovery-services-vaults"],
183418
+ ["snapshot", "azure:disks-snapshots"],
183419
+ ["spring_cloud_", "azure:spring-apps"],
183420
+ ["ssh_public_key", "azure:ssh-keys"],
183421
+ ["stack_hci_", "azure:stack-hci-premium"],
183422
+ ["storage_blob", "azure:blob-storage"],
183423
+ ["storage_sync", "azure:storage-sync-services"],
183424
+ ["storage_", "azure:storage-accounts"],
183425
+ ["stream_analytics_", "azure:stream-analytics-jobs"],
183426
+ ["traffic_manager_", "azure:traffic-manager-profiles"],
183427
+ ["trusted_signing_", "azure:key-vaults"],
183428
+ ["virtual_hub", "azure:virtual-wan-hub"],
183429
+ ["vmware_", "azure:vmware-solution"],
183430
+ ["vpn_", "azure:virtual-network-gateways"],
183431
+ ["web_app", "azure:app-service"],
183432
+ ["workloads_sap_", "azure:virtual-instance-for-sap"]
183433
+ ];
183434
+ }
183435
+ });
183436
+
182246
183437
  // ../../packages/diagrams/src/importers/terraform/tokens.ts
182247
183438
  function stringEnd(text3, start2) {
182248
183439
  const modes = ["string"];
@@ -182350,12 +183541,12 @@ function allAttributes(block) {
182350
183541
  }
182351
183542
  return out;
182352
183543
  }
182353
- var MAX_DEPTH2, HclReader;
182354
- var init_parse21 = __esm({
183544
+ var MAX_DEPTH3, HclReader;
183545
+ var init_parse22 = __esm({
182355
183546
  "../../packages/diagrams/src/importers/terraform/parse.ts"() {
182356
183547
  "use strict";
182357
183548
  init_tokens();
182358
- MAX_DEPTH2 = 32;
183549
+ MAX_DEPTH3 = 32;
182359
183550
  HclReader = class {
182360
183551
  constructor(text3, tokens) {
182361
183552
  this.text = text3;
@@ -182417,7 +183608,7 @@ var init_parse21 = __esm({
182417
183608
  /** A block: its type, its labels, and its body in braces. */
182418
183609
  block(depth) {
182419
183610
  const head = this.peek();
182420
- if (depth >= MAX_DEPTH2) return { offset: head.start, message: "The blocks are nested too deep.", hint: `Keep nesting under ${MAX_DEPTH2} levels.` };
183611
+ if (depth >= MAX_DEPTH3) return { offset: head.start, message: "The blocks are nested too deep.", hint: `Keep nesting under ${MAX_DEPTH3} levels.` };
182421
183612
  const labels2 = [];
182422
183613
  this.index += 1;
182423
183614
  for (; ; ) {
@@ -182537,7 +183728,7 @@ var init_labels = __esm({
182537
183728
  "use strict";
182538
183729
  init_constants();
182539
183730
  init_shared2();
182540
- init_parse21();
183731
+ init_parse22();
182541
183732
  GENERIC_NAMES = /* @__PURE__ */ new Set(["main", "this", "default", "example", "primary", "the", "it", "resource", "self"]);
182542
183733
  NAME_ATTRIBUTES = ["name", "bucket", "identifier", "function_name", "cluster_identifier", "cluster_name", "domain_name"];
182543
183734
  ACRONYMS = /* @__PURE__ */ new Set(["api", "db", "sql", "vpc", "cdn", "dns", "alb", "nlb", "lb", "vm", "ecs", "eks", "aks", "gke", "nat", "waf", "kms", "iam", "s3", "sns", "sqs", "ui", "id", "kv", "acr", "apim", "aca", "nsg", "pe", "url"]);
@@ -183027,13 +184218,13 @@ function itemsToStack(written3, modules) {
183027
184218
  const byAttribute = /* @__PURE__ */ new Map();
183028
184219
  const vars = /* @__PURE__ */ new Map();
183029
184220
  const values = /* @__PURE__ */ new Map();
183030
- const readable2 = /* @__PURE__ */ new Map();
184221
+ const readable3 = /* @__PURE__ */ new Map();
183031
184222
  for (const attribute2 of item.attributes) {
183032
184223
  const refs = attribute2.refs.map((ref) => keyOf5.get(ref)).filter((ref) => ref !== void 0 && ref !== key);
183033
184224
  if (refs.length > 0) byAttribute.set(attribute2.name, [.../* @__PURE__ */ new Set([...byAttribute.get(attribute2.name) ?? [], ...refs])]);
183034
184225
  if ((attribute2.vars ?? []).length > 0) vars.set(attribute2.name, [.../* @__PURE__ */ new Set([...vars.get(attribute2.name) ?? [], ...attribute2.vars ?? []])]);
183035
184226
  if (attribute2.value !== null) values.set(attribute2.name, attribute2.value);
183036
- else if (attribute2.readable !== void 0 && attribute2.readable !== null) readable2.set(attribute2.name, attribute2.readable);
184227
+ else if (attribute2.readable !== void 0 && attribute2.readable !== null) readable3.set(attribute2.name, attribute2.readable);
183037
184228
  }
183038
184229
  if (item.source !== void 0) values.set("source", item.source);
183039
184230
  const all = [...byAttribute];
@@ -183050,7 +184241,7 @@ function itemsToStack(written3, modules) {
183050
184241
  byAttribute,
183051
184242
  vars,
183052
184243
  values,
183053
- readable: readable2,
184244
+ readable: readable3,
183054
184245
  local: item.type === "module" ? `module.${name2}` : `${item.type}.${name2}`,
183055
184246
  scope: item.scope,
183056
184247
  instance: item.copies?.kind === "instance",
@@ -183100,7 +184291,7 @@ function bundleArrows(nodes, groups, edges, ownBox, joinPairs = true) {
183100
184291
  const typeOf = new Map(groups.map((group2) => [group2.key, group2.type ?? ""]));
183101
184292
  const inside = (key, frame) => {
183102
184293
  let current = frameOf3.get(key) ?? null;
183103
- for (let depth = 0; current !== null && depth < MAX_DEPTH3; depth += 1) {
184294
+ for (let depth = 0; current !== null && depth < MAX_DEPTH4; depth += 1) {
183104
184295
  if (current === frame) return true;
183105
184296
  current = parents.get(current) ?? null;
183106
184297
  }
@@ -183152,14 +184343,14 @@ function bundleArrows(nodes, groups, edges, ownBox, joinPairs = true) {
183152
184343
  }
183153
184344
  return { nodes: [...nodes, ...added], edges: out };
183154
184345
  }
183155
- var BUNDLE_AT, FEW, HOSTS2, MAX_DEPTH3;
184346
+ var BUNDLE_AT, FEW, HOSTS2, MAX_DEPTH4;
183156
184347
  var init_bundle = __esm({
183157
184348
  "../../packages/diagrams/src/importers/terraform/bundle.ts"() {
183158
184349
  "use strict";
183159
184350
  BUNDLE_AT = 2;
183160
184351
  FEW = 3;
183161
184352
  HOSTS2 = /* @__PURE__ */ new Set(["service-plan", "cluster"]);
183162
- MAX_DEPTH3 = 32;
184353
+ MAX_DEPTH4 = 32;
183163
184354
  }
183164
184355
  });
183165
184356
 
@@ -183178,14 +184369,14 @@ function listAt(map2, key) {
183178
184369
  map2.set(key, made);
183179
184370
  return made;
183180
184371
  }
183181
- var NOT_SCOPES, MAX_PLACEMENT_HOPS, MAX_DEPTH4, GROUP_RANK, SELF_BOXED, VARIABLE_WORDS, sameKind, familyOf, containerFrame, anchorOf, StackPlan;
184372
+ var NOT_SCOPES, MAX_PLACEMENT_HOPS, MAX_DEPTH5, GROUP_RANK, SELF_BOXED, VARIABLE_WORDS, sameKind, familyOf, containerFrame, anchorOf, StackPlan;
183182
184373
  var init_plan9 = __esm({
183183
184374
  "../../packages/diagrams/src/importers/terraform/plan.ts"() {
183184
184375
  "use strict";
183185
184376
  init_resources();
183186
184377
  NOT_SCOPES = /* @__PURE__ */ new Set(["count", "for_each", "depends_on", "provider", "lifecycle"]);
183187
184378
  MAX_PLACEMENT_HOPS = 3;
183188
- MAX_DEPTH4 = 12;
184379
+ MAX_DEPTH5 = 12;
183189
184380
  GROUP_RANK = { account: 0, "resource-group": 1, network: 2, subnet: 4, "public-subnet": 4, "private-subnet": 4, "service-plan": 5, cluster: 6 };
183190
184381
  SELF_BOXED = /* @__PURE__ */ new Set(["service-plan", "cluster"]);
183191
184382
  VARIABLE_WORDS = { "resource-group": /resource_group|(?:^|_)rg(?:_|$)/, network: /vnet|vpc|network/, subnet: /subnet/ };
@@ -183296,7 +184487,7 @@ var init_plan9 = __esm({
183296
184487
  if (holder === null) continue;
183297
184488
  let current = holder;
183298
184489
  let loops = false;
183299
- for (let depth = 0; current !== void 0 && depth < MAX_DEPTH4; depth += 1) {
184490
+ for (let depth = 0; current !== void 0 && depth < MAX_DEPTH5; depth += 1) {
183300
184491
  if (current === resource.key) loops = true;
183301
184492
  current = this.holder.get(current);
183302
184493
  }
@@ -183515,7 +184706,7 @@ var init_plan9 = __esm({
183515
184706
  ancestors(key) {
183516
184707
  const out = [];
183517
184708
  let current = this.parents.get(key);
183518
- for (let depth = 0; current !== void 0 && depth < MAX_DEPTH4 && !out.includes(current); depth += 1) {
184709
+ for (let depth = 0; current !== void 0 && depth < MAX_DEPTH5 && !out.includes(current); depth += 1) {
183519
184710
  out.push(current);
183520
184711
  current = this.parents.get(current);
183521
184712
  }
@@ -183802,7 +184993,7 @@ function layerNetworks(plan) {
183802
184993
  const resource = plan.resources.get(key.replace(/^(?:frame|anchor):/, ""));
183803
184994
  if (resource === void 0 || NETWORK_TYPE.test(resource.type)) continue;
183804
184995
  let target = parent;
183805
- for (let depth = 0; target !== void 0 && depth < MAX_DEPTH5; depth += 1) {
184996
+ for (let depth = 0; target !== void 0 && depth < MAX_DEPTH6; depth += 1) {
183806
184997
  const type = plan.frameType(target);
183807
184998
  if (type === null || !NETWORK_FRAMES.has(type)) break;
183808
184999
  target = plan.parents.get(target);
@@ -183814,11 +185005,11 @@ function layerNetworks(plan) {
183814
185005
  }
183815
185006
  return moved3;
183816
185007
  }
183817
- var MAX_DEPTH5, NETWORK_FRAMES, NETWORK_TYPE;
185008
+ var MAX_DEPTH6, NETWORK_FRAMES, NETWORK_TYPE;
183818
185009
  var init_network_layer = __esm({
183819
185010
  "../../packages/diagrams/src/importers/terraform/network-layer.ts"() {
183820
185011
  "use strict";
183821
- MAX_DEPTH5 = 16;
185012
+ MAX_DEPTH6 = 16;
183822
185013
  NETWORK_FRAMES = /* @__PURE__ */ new Set(["network", "subnet", "public-subnet", "private-subnet", "management-subnet", "isolated-subnet"]);
183823
185014
  NETWORK_TYPE = /^(?:azurerm_(?:virtual_network|subnet|network_|route|nat_gateway|public_ip|firewall|application_gateway|lb$|lb_|private_dns)|aws_(?:vpc|subnet|security_group|network_acl|route|internet_gateway|nat_gateway|eip|egress_only)|google_compute_(?:network|subnetwork|firewall|router|route|address))/;
183824
185015
  }
@@ -184145,7 +185336,7 @@ function drawStack(input2) {
184145
185336
  const provider = providers.size === 1 ? [...providers][0] : providers.size === 0 ? input2.cloud ?? null : null;
184146
185337
  const owner = provider === null ? null : SHORT_NAMES2[provider] ?? CLOUD_PROVIDER_SPECS[provider].title;
184147
185338
  const where = owner === null ? "on more than one cloud" : `on ${owner}`;
184148
- const nesting2 = context.options.groupBy === "file" ? "framed by the file that declares them" : plan.modules.size > 0 ? "framed by the module that declares them and nested in the groups, networks, and subnets that hold them" : layered ? "nested in the groups that hold them, beside the network, with an arrow to the subnet each one sits in" : "nested in the groups, networks, and subnets that hold them";
185339
+ const nesting2 = context.options.groupBy === "file" ? "framed by the file that declares them" : plan.modules.size > 0 ? `framed by the ${input2.framing ?? "module"} that declares them and nested in the groups, networks, and subnets that hold them` : layered ? "nested in the groups that hold them, beside the network, with an arrow to the subnet each one sits in" : "nested in the groups, networks, and subnets that hold them";
184149
185340
  const title = context.title ?? `${owner === null ? "Cloud infrastructure" : `${owner} infrastructure`}${input2.summary === void 0 ? "" : `: ${input2.summary}`}`;
184150
185341
  const draft = {
184151
185342
  title,
@@ -184441,14 +185632,14 @@ function lastPart(value) {
184441
185632
  if (!value.includes("/")) return value;
184442
185633
  return value.split("/").filter((part) => part !== "").pop() ?? value;
184443
185634
  }
184444
- var MAX_DEPTH6, MAX_VALUE_LENGTH, MAX_ENTRIES, ValueResolver;
185635
+ var MAX_DEPTH7, MAX_VALUE_LENGTH2, MAX_ENTRIES, ValueResolver;
184445
185636
  var init_values2 = __esm({
184446
185637
  "../../packages/diagrams/src/importers/terraform/values.ts"() {
184447
185638
  "use strict";
184448
185639
  init_expressions();
184449
- init_parse21();
184450
- MAX_DEPTH6 = 8;
184451
- MAX_VALUE_LENGTH = 600;
185640
+ init_parse22();
185641
+ MAX_DEPTH7 = 8;
185642
+ MAX_VALUE_LENGTH2 = 600;
184452
185643
  MAX_ENTRIES = 64;
184453
185644
  ValueResolver = class _ValueResolver {
184454
185645
  constructor(variables, assigned, locals, inputs = /* @__PURE__ */ new Map(), binding = null, site = "") {
@@ -184505,13 +185696,13 @@ var init_values2 = __esm({
184505
185696
  }
184506
185697
  /** The value of an expression as written, or null when it cannot be known from the text. */
184507
185698
  value(raw, depth = 0) {
184508
- if (depth > MAX_DEPTH6) return null;
185699
+ if (depth > MAX_DEPTH7) return null;
184509
185700
  const text3 = raw.trim();
184510
185701
  const memoKey = `${depth}\0${text3}`;
184511
185702
  if (this.memo.has(memoKey)) return this.memo.get(memoKey) ?? null;
184512
185703
  this.memo.set(memoKey, null);
184513
185704
  const found = this.evaluate(text3, depth);
184514
- const kept2 = found === null || found.length > MAX_VALUE_LENGTH ? null : found;
185705
+ const kept2 = found === null || found.length > MAX_VALUE_LENGTH2 ? null : found;
184515
185706
  this.memo.set(memoKey, kept2);
184516
185707
  return kept2;
184517
185708
  }
@@ -184526,7 +185717,7 @@ var init_values2 = __esm({
184526
185717
  const part = (expression2) => this.value(expression2, depth + 1) ?? this.readable(expression2, depth + 1) ?? `{${shortName(expression2)}}`;
184527
185718
  if (text3.startsWith('"') && text3.endsWith('"') && text3.length >= 2 && interpolationsClosed(text3)) return this.template(text3.slice(1, -1), depth, part);
184528
185719
  const call = /^(format|join|lower|upper)\(([\s\S]*)\)$/.exec(text3);
184529
- if (call === null || depth > MAX_DEPTH6) return null;
185720
+ if (call === null || depth > MAX_DEPTH7) return null;
184530
185721
  const args = argumentsOf(call[2] ?? "");
184531
185722
  if (args === null || args.length === 0) return null;
184532
185723
  if (call[1] === "format") {
@@ -184552,7 +185743,7 @@ var init_values2 = __esm({
184552
185743
  * `toset`, `tolist`, `keys`, `values`, `merge`, `each.value`, and a `for` over a known map or list. Null when unknown.
184553
185744
  */
184554
185745
  collection(raw, depth = 0) {
184555
- if (depth > MAX_DEPTH6) return null;
185746
+ if (depth > MAX_DEPTH7) return null;
184556
185747
  const text3 = raw.trim();
184557
185748
  if (text3.startsWith("{") && text3.endsWith("}")) {
184558
185749
  const forMap = /^\{\s*for\s+([A-Za-z_]\w*)\s*(?:,\s*([A-Za-z_]\w*)\s*)?in\s+([^:]+):\s*([A-Za-z_]\w*)\s*=>/.exec(text3);
@@ -184646,7 +185837,7 @@ var init_values2 = __esm({
184646
185837
  fieldOf(base, fields, depth) {
184647
185838
  let current = base;
184648
185839
  for (const field of fields) {
184649
- if (current === null || depth > MAX_DEPTH6) return null;
185840
+ if (current === null || depth > MAX_DEPTH7) return null;
184650
185841
  const map2 = current.scope.collection(current.raw, depth + 1);
184651
185842
  if (map2?.kind !== "map") return null;
184652
185843
  current = map2.entries.find(([key]) => key === field)?.[1] ?? null;
@@ -184815,7 +186006,7 @@ function pathOf2(block, files) {
184815
186006
  function foldersOf(blocks2, files) {
184816
186007
  const byFolder = /* @__PURE__ */ new Map();
184817
186008
  for (const block of blocks2) {
184818
- const folder = folderOf(pathOf2(block, files));
186009
+ const folder = folderOf3(pathOf2(block, files));
184819
186010
  byFolder.set(folder, [...byFolder.get(folder) ?? [], block]);
184820
186011
  }
184821
186012
  const targets = /* @__PURE__ */ new Map();
@@ -184831,7 +186022,7 @@ function foldersOf(blocks2, files) {
184831
186022
  called.add(target);
184832
186023
  }
184833
186024
  }
184834
- const roots = [...byFolder.keys()].filter((folder) => !called.has(folder) && (byFolder.get(folder) ?? []).some(isItem)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
186025
+ const roots = [...byFolder.keys()].filter((folder) => !called.has(folder) && (byFolder.get(folder) ?? []).some(isItem2)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
184835
186026
  return { blocks: byFolder, targets, roots };
184836
186027
  }
184837
186028
  function outputsOf(blocks2) {
@@ -184890,7 +186081,7 @@ function allScopes(root) {
184890
186081
  }
184891
186082
  return out;
184892
186083
  }
184893
- var MAX_MODULE_DEPTH, CALL_SETTINGS, folderOf, isItem;
186084
+ var MAX_MODULE_DEPTH, CALL_SETTINGS, folderOf3, isItem2;
184894
186085
  var init_modules2 = __esm({
184895
186086
  "../../packages/diagrams/src/importers/terraform/modules.ts"() {
184896
186087
  "use strict";
@@ -184898,12 +186089,12 @@ var init_modules2 = __esm({
184898
186089
  init_values2();
184899
186090
  MAX_MODULE_DEPTH = 6;
184900
186091
  CALL_SETTINGS = /* @__PURE__ */ new Set(["source", "version", "providers", "count", "for_each", "depends_on"]);
184901
- folderOf = (path) => {
186092
+ folderOf3 = (path) => {
184902
186093
  const parts = path.split(/[\\/]/);
184903
186094
  parts.pop();
184904
186095
  return parts.join("/");
184905
186096
  };
184906
- isItem = (block) => block.type === "resource" || block.type === "data" || block.type === "module";
186097
+ isItem2 = (block) => block.type === "resource" || block.type === "data" || block.type === "module";
184907
186098
  }
184908
186099
  });
184909
186100
 
@@ -184973,21 +186164,21 @@ function readResources(root) {
184973
186164
  const vars = /* @__PURE__ */ new Map();
184974
186165
  for (const attribute2 of attributes2) {
184975
186166
  const found = reader.read(attribute2.raw).filter((ref) => ref !== key && ref !== base);
184976
- const items2 = found.filter(isItem2);
184977
- const named = found.filter((ref) => !isItem2(ref)).map((ref) => ref.slice("var.".length));
186167
+ const items2 = found.filter(isItem3);
186168
+ const named = found.filter((ref) => !isItem3(ref)).map((ref) => ref.slice("var.".length));
184978
186169
  if (items2.length > 0) byAttribute.set(attribute2.name, unique([...byAttribute.get(attribute2.name) ?? [], ...items2]));
184979
186170
  if (named.length > 0) vars.set(attribute2.name, unique([...vars.get(attribute2.name) ?? [], ...named]));
184980
186171
  }
184981
186172
  const from = (keep) => unique(attributes2.filter(keep).flatMap((attribute2) => byAttribute.get(attribute2.name) ?? []));
184982
186173
  const role = id.kind === "module" ? { role: "node", type: "service", icon: "lucide:package", product: "Module" } : roleOf2(id.type);
184983
186174
  const values = /* @__PURE__ */ new Map();
184984
- const readable2 = /* @__PURE__ */ new Map();
186175
+ const readable3 = /* @__PURE__ */ new Map();
184985
186176
  for (const attribute2 of block.attributes) {
184986
186177
  const value = attribute2.literal ?? resolver.value(attribute2.raw);
184987
186178
  if (value !== null) values.set(attribute2.name, value);
184988
186179
  else if (NAMED.includes(attribute2.name)) {
184989
186180
  const partly = resolver.readable(attribute2.raw);
184990
- if (partly !== null) readable2.set(attribute2.name, partly);
186181
+ if (partly !== null) readable3.set(attribute2.name, partly);
184991
186182
  }
184992
186183
  }
184993
186184
  const tags = block.attributes.find((attribute2) => attribute2.name === "tags");
@@ -185005,7 +186196,7 @@ function readResources(root) {
185005
186196
  byAttribute,
185006
186197
  vars,
185007
186198
  values,
185008
- readable: readable2,
186199
+ readable: readable3,
185009
186200
  local: id.local,
185010
186201
  scope: scope.prefix,
185011
186202
  instance: copy.key !== null,
@@ -185039,7 +186230,7 @@ function readResources(root) {
185039
186230
  }
185040
186231
  return { resources, modules, empty };
185041
186232
  }
185042
- var MAX_DEPTH7, TYPE, unique, ScopeReader, isItem2, PLAIN_NAMES, NAMED;
186233
+ var MAX_DEPTH8, TYPE, unique, ScopeReader, isItem3, PLAIN_NAMES, NAMED;
185043
186234
  var init_graph4 = __esm({
185044
186235
  "../../packages/diagrams/src/importers/terraform/graph.ts"() {
185045
186236
  "use strict";
@@ -185047,9 +186238,9 @@ var init_graph4 = __esm({
185047
186238
  init_generic();
185048
186239
  init_instances();
185049
186240
  init_modules2();
185050
- init_parse21();
186241
+ init_parse22();
185051
186242
  init_resources();
185052
- MAX_DEPTH7 = 6;
186243
+ MAX_DEPTH8 = 6;
185053
186244
  TYPE = /^[a-z][a-z0-9]*_[a-z0-9_]+$/;
185054
186245
  unique = (values) => [...new Set(values)];
185055
186246
  ScopeReader = class {
@@ -185069,7 +186260,7 @@ var init_graph4 = __esm({
185069
186260
  }
185070
186261
  /** Every address a text names, with `var.` names for the variables that name no item: `aws_vpc.main`, `module.network.azurerm_subnet.this["app"]`, `var.region`. */
185071
186262
  read(text3, depth = 0) {
185072
- if (depth > MAX_DEPTH7) return [];
186263
+ if (depth > MAX_DEPTH8) return [];
185073
186264
  const out = [];
185074
186265
  for (const steps of references(text3)) out.push(...this.interpret(steps, depth));
185075
186266
  return out;
@@ -185150,7 +186341,7 @@ var init_graph4 = __esm({
185150
186341
  return out;
185151
186342
  }
185152
186343
  };
185153
- isItem2 = (ref) => !ref.startsWith("var.");
186344
+ isItem3 = (ref) => !ref.startsWith("var.");
185154
186345
  PLAIN_NAMES = /* @__PURE__ */ new Set(["this", "main", "default", "these", "each", "all", "items"]);
185155
186346
  NAMED = ["name", "bucket", "identifier", "function_name", "cluster_name", "domain_name"];
185156
186347
  }
@@ -185158,28 +186349,28 @@ var init_graph4 = __esm({
185158
186349
 
185159
186350
  // ../../packages/diagrams/src/importers/terraform/json-syntax.ts
185160
186351
  function expression(value, depth) {
185161
- if (depth > MAX_DEPTH8) return "null";
186352
+ if (depth > MAX_DEPTH9) return "null";
185162
186353
  if (typeof value === "string") return JSON.stringify(value);
185163
186354
  if (typeof value === "number" || typeof value === "boolean") return String(value);
185164
186355
  if (value === null) return "null";
185165
186356
  if (Array.isArray(value)) return `[${value.map((item) => expression(item, depth + 1)).join(", ")}]`;
185166
- return `{ ${Object.entries(value).map(([key, item]) => `${keyOf(key)} = ${expression(item, depth + 1)}`).join(", ")} }`;
186357
+ return `{ ${Object.entries(value).map(([key, item]) => `${keyOf2(key)} = ${expression(item, depth + 1)}`).join(", ")} }`;
185167
186358
  }
185168
186359
  function body5(object2, depth, lines) {
185169
186360
  for (const [key, value] of Object.entries(object2)) {
185170
186361
  const isObject5 = (item) => typeof item === "object" && item !== null && !Array.isArray(item);
185171
- if (key === "locals" || key === "tags" || key === "labels" || key === "app_settings" || key === "default" || depth > MAX_DEPTH8) lines.push(`${keyOf(key)} = ${expression(value, depth)}`);
186362
+ if (key === "locals" || key === "tags" || key === "labels" || key === "app_settings" || key === "default" || depth > MAX_DEPTH9) lines.push(`${keyOf2(key)} = ${expression(value, depth)}`);
185172
186363
  else if (isObject5(value)) {
185173
- lines.push(`${keyOf(key)} {`);
186364
+ lines.push(`${keyOf2(key)} {`);
185174
186365
  body5(value, depth + 1, lines);
185175
186366
  lines.push("}");
185176
186367
  } else if (Array.isArray(value) && value.length > 0 && value.every(isObject5)) {
185177
186368
  for (const item of value) {
185178
- lines.push(`${keyOf(key)} {`);
186369
+ lines.push(`${keyOf2(key)} {`);
185179
186370
  body5(item, depth + 1, lines);
185180
186371
  lines.push("}");
185181
186372
  }
185182
- } else lines.push(`${keyOf(key)} = ${expression(value, depth)}`);
186373
+ } else lines.push(`${keyOf2(key)} = ${expression(value, depth)}`);
185183
186374
  }
185184
186375
  }
185185
186376
  function blocks(kind, value, labels2, depth, lines) {
@@ -185210,7 +186401,7 @@ function jsonToHcl(text3) {
185210
186401
  for (const entry2 of Array.isArray(value) ? value : [value]) {
185211
186402
  if (typeof entry2 !== "object" || entry2 === null || Array.isArray(entry2)) continue;
185212
186403
  lines.push("locals {");
185213
- for (const [name2, item] of Object.entries(entry2)) lines.push(`${keyOf(name2)} = ${expression(item, 1)}`);
186404
+ for (const [name2, item] of Object.entries(entry2)) lines.push(`${keyOf2(name2)} = ${expression(item, 1)}`);
185214
186405
  lines.push("}");
185215
186406
  }
185216
186407
  } else if (kind in LABELS) blocks(kind, value, [], 0, lines);
@@ -185234,15 +186425,15 @@ function readJsonFiles(text3, context, index2) {
185234
186425
  }
185235
186426
  return { text: blanked, blocks: out };
185236
186427
  }
185237
- var MAX_DEPTH8, LABELS, keyOf;
186428
+ var MAX_DEPTH9, LABELS, keyOf2;
185238
186429
  var init_json_syntax = __esm({
185239
186430
  "../../packages/diagrams/src/importers/terraform/json-syntax.ts"() {
185240
186431
  "use strict";
185241
186432
  init_core();
185242
- init_parse21();
185243
- MAX_DEPTH8 = 24;
186433
+ init_parse22();
186434
+ MAX_DEPTH9 = 24;
185244
186435
  LABELS = { resource: 2, data: 2, module: 1, variable: 1, output: 1, provider: 1 };
185245
- keyOf = (key) => /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key) ? key : JSON.stringify(key);
186436
+ keyOf2 = (key) => /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key) ? key : JSON.stringify(key);
185246
186437
  }
185247
186438
  });
185248
186439
 
@@ -185274,7 +186465,7 @@ function readVariableFiles(text3, files, context, index2, folder = null) {
185274
186465
  const all = variableSpansOf(text3, files);
185275
186466
  const variableSpans = all.filter((span) => inFolder(span.path, folder));
185276
186467
  if (all.length === 0) return { assigned: /* @__PURE__ */ new Map(), applied: [] };
185277
- const loaded = variableSpans.filter((span) => isLoaded(span.path)).sort((a, b) => Number(!/^terraform\./i.test(baseName(a.path))) - Number(!/^terraform\./i.test(baseName(b.path))) || a.path.localeCompare(b.path));
186468
+ const loaded = variableSpans.filter((span) => isLoaded(span.path)).sort((a, b) => Number(!/^terraform\./i.test(baseName3(a.path))) - Number(!/^terraform\./i.test(baseName3(b.path))) || a.path.localeCompare(b.path));
185278
186469
  const others = variableSpans.filter((span) => !isLoaded(span.path));
185279
186470
  const chosenNames = context.options.varFiles ?? [];
185280
186471
  let chosen;
@@ -185322,23 +186513,23 @@ function readVariableFiles(text3, files, context, index2, folder = null) {
185322
186513
  }
185323
186514
  return { assigned, applied };
185324
186515
  }
185325
- var LISTED5, isVariableFile, baseName, isLoaded, inFolder, picks;
186516
+ var LISTED5, isVariableFile, baseName3, isLoaded, inFolder, picks;
185326
186517
  var init_tfvars = __esm({
185327
186518
  "../../packages/diagrams/src/importers/terraform/tfvars.ts"() {
185328
186519
  "use strict";
185329
186520
  init_core();
185330
186521
  init_shared2();
185331
- init_parse21();
186522
+ init_parse22();
185332
186523
  LISTED5 = 5;
185333
186524
  isVariableFile = (path) => /\.tfvars(?:\.json)?$/i.test(path);
185334
- baseName = (path) => path.split(/[\\/]/).pop() ?? path;
185335
- isLoaded = (path) => /^terraform\.tfvars(?:\.json)?$/i.test(baseName(path)) || /\.auto\.tfvars(?:\.json)?$/i.test(path);
186525
+ baseName3 = (path) => path.split(/[\\/]/).pop() ?? path;
186526
+ isLoaded = (path) => /^terraform\.tfvars(?:\.json)?$/i.test(baseName3(path)) || /\.auto\.tfvars(?:\.json)?$/i.test(path);
185336
186527
  inFolder = (path, folder) => {
185337
186528
  if (folder === null) return true;
185338
186529
  const own2 = path.split(/[\\/]/).slice(0, -1).join("/");
185339
186530
  return own2 === folder || folder !== "" && own2.startsWith(`${folder}/`);
185340
186531
  };
185341
- picks = (chosen, path) => path === chosen || path.endsWith(`/${chosen.replace(/^\.?\//, "")}`) || baseName(path) === chosen;
186532
+ picks = (chosen, path) => path === chosen || path.endsWith(`/${chosen.replace(/^\.?\//, "")}`) || baseName3(path) === chosen;
185342
186533
  }
185343
186534
  });
185344
186535
 
@@ -185464,7 +186655,7 @@ function readTerraform(text3, context) {
185464
186655
  }
185465
186656
  const blocks2 = [...read.blocks, ...json2.blocks];
185466
186657
  const folders = foldersOf(blocks2, context.files);
185467
- const roots = folders.roots.length === 0 ? [folderOf(context.files[0]?.path ?? "")] : folders.roots;
186658
+ const roots = folders.roots.length === 0 ? [folderOf3(context.files[0]?.path ?? "")] : folders.roots;
185468
186659
  let chosen = roots;
185469
186660
  if (context.options.root !== void 0 && context.options.root !== "") {
185470
186661
  const root = chosenRoot(roots, context.options.root);
@@ -185491,7 +186682,7 @@ var init_terraform = __esm({
185491
186682
  init_graph4();
185492
186683
  init_labels();
185493
186684
  init_modules2();
185494
- init_parse21();
186685
+ init_parse22();
185495
186686
  init_resources();
185496
186687
  init_json_syntax();
185497
186688
  init_tfvars();
@@ -185570,13 +186761,13 @@ function bodyObject(value) {
185570
186761
  if (value.kind === "if" || value.kind === "for") return bodyObject(value.body);
185571
186762
  return null;
185572
186763
  }
185573
- var MAX_DEPTH9, MAX_ITEMS, Env, Evaluator;
186764
+ var MAX_DEPTH10, MAX_ITEMS, Env, Evaluator;
185574
186765
  var init_evaluate = __esm({
185575
186766
  "../../packages/diagrams/src/importers/bicep/evaluate.ts"() {
185576
186767
  "use strict";
185577
186768
  init_value();
185578
186769
  init_value();
185579
- MAX_DEPTH9 = 24;
186770
+ MAX_DEPTH10 = 24;
185580
186771
  MAX_ITEMS = 256;
185581
186772
  Env = class _Env {
185582
186773
  constructor(unit3, inputs, loop, prefix, children) {
@@ -185599,7 +186790,7 @@ var init_evaluate = __esm({
185599
186790
  Evaluator = class {
185600
186791
  memo = /* @__PURE__ */ new Map();
185601
186792
  evaluate(expr, env, depth = 0) {
185602
- if (depth > MAX_DEPTH9) return new Unknown(shortOf(expr));
186793
+ if (depth > MAX_DEPTH10) return new Unknown(shortOf(expr));
185603
186794
  switch (expr.kind) {
185604
186795
  case "literal":
185605
186796
  return expr.value;
@@ -186138,7 +187329,7 @@ var init_azure_roles = __esm({
186138
187329
  });
186139
187330
 
186140
187331
  // ../../packages/diagrams/src/importers/bicep/slots.ts
186141
- function keyOf2(item, index2) {
187332
+ function keyOf3(item, index2) {
186142
187333
  if (typeof item === "string") return item;
186143
187334
  if (typeof item === "object" && item !== null && !Array.isArray(item) && !(item instanceof Unknown)) {
186144
187335
  const name2 = item["name"];
@@ -186228,9 +187419,9 @@ var init_items3 = __esm({
186228
187419
  if (slot.transparent) return [];
186229
187420
  const body6 = bodyObject(slot.declaration.value);
186230
187421
  const attributes2 = [];
186231
- const add2 = (name2, refs, value = null, readable2 = null, vars = []) => {
186232
- if (refs.length === 0 && value === null && readable2 === null && vars.length === 0) return;
186233
- attributes2.push({ name: name2, refs, value, readable: readable2, vars });
187422
+ const add2 = (name2, refs, value = null, readable3 = null, vars = []) => {
187423
+ if (refs.length === 0 && value === null && readable3 === null && vars.length === 0) return;
187424
+ attributes2.push({ name: name2, refs, value, readable: readable3, vars });
186234
187425
  };
186235
187426
  for (const entry2 of body6?.entries ?? []) {
186236
187427
  if (entry2.key === "name") {
@@ -186605,7 +187796,7 @@ var init_model = __esm({
186605
187796
  let inner = env.bind(value.item, element === void 0 ? { value: item } : { expr: element, env });
186606
187797
  if (value.index !== null) inner = inner.bind(value.index, { value: index2 });
186607
187798
  if (value.body.kind === "if" && this.evaluator.truth(value.body.test, inner) === false) continue;
186608
- out.push({ key: keyOf2(item, index2), env: inner, copies: { kind: "instance" } });
187799
+ out.push({ key: keyOf3(item, index2), env: inner, copies: { kind: "instance" } });
186609
187800
  }
186610
187801
  return out;
186611
187802
  }
@@ -186667,7 +187858,7 @@ var init_model = __esm({
186667
187858
  if (Array.isArray(over)) for (const [index2, item] of over.slice(0, MAX_INSTANCES).entries()) {
186668
187859
  let inner = env.bind(list.item, { value: item });
186669
187860
  if (list.index !== null) inner = inner.bind(list.index, { value: index2 });
186670
- add2(list.body, inner, keyOf2(item, index2));
187861
+ add2(list.body, inner, keyOf3(item, index2));
186671
187862
  }
186672
187863
  }
186673
187864
  return out;
@@ -186773,7 +187964,7 @@ function piecesOf2(text3, start2, end) {
186773
187964
  for (let index2 = start2 + 1; index2 < end - 1; index2 += 1) {
186774
187965
  const char = text3.charAt(index2);
186775
187966
  if (char === "\\") {
186776
- current += ESCAPES[text3.charAt(index2 + 1)] ?? text3.charAt(index2 + 1);
187967
+ current += ESCAPES2[text3.charAt(index2 + 1)] ?? text3.charAt(index2 + 1);
186777
187968
  index2 += 1;
186778
187969
  } else if (char === "$" && text3.charAt(index2 + 1) === "{") {
186779
187970
  const close2 = interpolationEnd2(text3, index2 + 1);
@@ -186834,12 +188025,12 @@ function tokenize3(text3) {
186834
188025
  }
186835
188026
  return { tokens, problem: null };
186836
188027
  }
186837
- var TWO_CHAR_SIGNS2, ESCAPES, isWordStart2, isWordChar2;
188028
+ var TWO_CHAR_SIGNS2, ESCAPES2, isWordStart2, isWordChar2;
186838
188029
  var init_tokens2 = __esm({
186839
188030
  "../../packages/diagrams/src/importers/bicep/tokens.ts"() {
186840
188031
  "use strict";
186841
188032
  TWO_CHAR_SIGNS2 = /* @__PURE__ */ new Set(["==", "!=", "<=", ">=", "&&", "||", "??", "::", "=>", ".?", "=~", "!~"]);
186842
- ESCAPES = { n: "\n", r: "\r", t: " ", "\\": "\\", "'": "'", $: "$" };
188033
+ ESCAPES2 = { n: "\n", r: "\r", t: " ", "\\": "\\", "'": "'", $: "$" };
186843
188034
  isWordStart2 = (char) => /[A-Za-z_]/.test(char);
186844
188035
  isWordChar2 = (char) => /[A-Za-z0-9_]/.test(char);
186845
188036
  }
@@ -186868,12 +188059,12 @@ function parseExpression(text3) {
186868
188059
  throw error62;
186869
188060
  }
186870
188061
  }
186871
- var MAX_DEPTH10, SKIPPED, PRECEDENCE, ReadError, BicepReader;
186872
- var init_parse22 = __esm({
188062
+ var MAX_DEPTH11, SKIPPED, PRECEDENCE, ReadError, BicepReader;
188063
+ var init_parse23 = __esm({
186873
188064
  "../../packages/diagrams/src/importers/bicep/parse.ts"() {
186874
188065
  "use strict";
186875
188066
  init_tokens2();
186876
- MAX_DEPTH10 = 64;
188067
+ MAX_DEPTH11 = 64;
186877
188068
  SKIPPED = /* @__PURE__ */ new Set(["import", "type", "func", "metadata", "extension", "provider", "assert"]);
186878
188069
  PRECEDENCE = { "??": 1, "||": 2, "&&": 3, "==": 4, "!=": 4, "=~": 4, "!~": 4, "<": 5, ">": 5, "<=": 5, ">=": 5, "+": 6, "-": 6, "*": 7, "/": 7, "%": 7 };
186879
188070
  ReadError = class extends Error {
@@ -187058,7 +188249,7 @@ var init_parse22 = __esm({
187058
188249
  }
187059
188250
  /** An expression: a conditional over binary operators over unary ones over postfix chains. */
187060
188251
  expression(depth) {
187061
- if (depth > MAX_DEPTH10) this.fail("The expression is nested too deep.", `Keep nesting under ${MAX_DEPTH10} levels.`);
188252
+ if (depth > MAX_DEPTH11) this.fail("The expression is nested too deep.", `Keep nesting under ${MAX_DEPTH11} levels.`);
187062
188253
  const test = this.binary(0, depth + 1);
187063
188254
  if (this.isSign("=>")) {
187064
188255
  this.index += 1;
@@ -187182,7 +188373,7 @@ var init_parse22 = __esm({
187182
188373
  return value;
187183
188374
  }
187184
188375
  object(depth) {
187185
- if (depth > MAX_DEPTH10) this.fail("The objects are nested too deep.", `Keep nesting under ${MAX_DEPTH10} levels.`);
188376
+ if (depth > MAX_DEPTH11) this.fail("The objects are nested too deep.", `Keep nesting under ${MAX_DEPTH11} levels.`);
187186
188377
  const open2 = this.expectSign("{", "An object is written { key: value }.");
187187
188378
  const entries2 = [];
187188
188379
  const nested2 = [];
@@ -187220,7 +188411,7 @@ var init_parse22 = __esm({
187220
188411
  }
187221
188412
  }
187222
188413
  array(depth) {
187223
- if (depth > MAX_DEPTH10) this.fail("The lists are nested too deep.", `Keep nesting under ${MAX_DEPTH10} levels.`);
188414
+ if (depth > MAX_DEPTH11) this.fail("The lists are nested too deep.", `Keep nesting under ${MAX_DEPTH11} levels.`);
187224
188415
  const open2 = this.expectSign("[", "A list is written [ a, b ].");
187225
188416
  this.skipNewlines();
187226
188417
  if (this.isWord("for")) return this.loop(open2, depth);
@@ -187279,7 +188470,7 @@ function unitOf(file2) {
187279
188470
  else if (declaration.kind === "output") outputs.set(declaration.name, declaration.value);
187280
188471
  else if (declaration.kind === "targetScope" && declaration.value.kind === "string") targetScope = declaration.value.parts.filter((part) => typeof part === "string").join("");
187281
188472
  }
187282
- return { path: file2.path, folder: folderOf(file2.path), offset: file2.start, params, vars, resources, modules, outputs, declarations: file2.declarations, targetScope };
188473
+ return { path: file2.path, folder: folderOf3(file2.path), offset: file2.start, params, vars, resources, modules, outputs, declarations: file2.declarations, targetScope };
187283
188474
  }
187284
188475
  function assignedBy(file2) {
187285
188476
  const own2 = { ...unitOf(file2), params: /* @__PURE__ */ new Map() };
@@ -187291,7 +188482,7 @@ function assignedBy(file2) {
187291
188482
  return out;
187292
188483
  }
187293
188484
  function parametersFor(root, parameterFiles, context, index2) {
187294
- const mine = parameterFiles.filter((file2) => file2.declarations.some((declaration) => declaration.kind === "using" && joinFolder(folderOf(file2.path), declaration.path) === root.path));
188485
+ const mine = parameterFiles.filter((file2) => file2.declarations.some((declaration) => declaration.kind === "using" && joinFolder(folderOf3(file2.path), declaration.path) === root.path));
187295
188486
  if (mine.length === 0) return /* @__PURE__ */ new Map();
187296
188487
  const chosen = context.options.varFiles ?? [];
187297
188488
  const picked = chosen.length > 0 ? mine.filter((file2) => chosen.some((name2) => file2.path === name2 || file2.path.endsWith(`/${name2.replace(/^\.?\//, "")}`))) : mine.length === 1 ? mine : [];
@@ -187398,7 +188589,7 @@ var init_bicep = __esm({
187398
188589
  init_modules2();
187399
188590
  init_evaluate();
187400
188591
  init_model();
187401
- init_parse22();
188592
+ init_parse23();
187402
188593
  LISTED6 = 5;
187403
188594
  bicepImporter = { format: "bicep", kinds: ["flow"], joins: "\n", extensions: [".bicep", ".bicepparam"], read: readBicep };
187404
188595
  }
@@ -187446,7 +188637,7 @@ function bicepQuotes(text3) {
187446
188637
  return out;
187447
188638
  }
187448
188639
  function exprOf(value, start2, depth = 0) {
187449
- if (depth > MAX_DEPTH11) return { kind: "literal", value: null, start: start2 };
188640
+ if (depth > MAX_DEPTH12) return { kind: "literal", value: null, start: start2 };
187450
188641
  if (typeof value === "string") {
187451
188642
  if (value.startsWith("[[")) return { kind: "string", parts: [value.slice(1)], start: start2 };
187452
188643
  if (value.startsWith("[") && value.endsWith("]")) {
@@ -187540,13 +188731,13 @@ function templateDeclarations(template, offsetOf) {
187540
188731
  for (const [name2, output2] of Object.entries(outputs)) if (isRecord2(output2)) declarations.push({ kind: "output", name: name2, value: exprOf(output2["value"], offsetOf(name2)), start: offsetOf(name2) });
187541
188732
  return declarations;
187542
188733
  }
187543
- var COPY_INDEX, MAX_DEPTH11, isRecord2;
188734
+ var COPY_INDEX, MAX_DEPTH12, isRecord2;
187544
188735
  var init_convert2 = __esm({
187545
188736
  "../../packages/diagrams/src/importers/arm/convert.ts"() {
187546
188737
  "use strict";
187547
- init_parse22();
188738
+ init_parse23();
187548
188739
  COPY_INDEX = "copyIndex";
187549
- MAX_DEPTH11 = 48;
188740
+ MAX_DEPTH12 = 48;
187550
188741
  isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
187551
188742
  }
187552
188743
  });
@@ -187624,514 +188815,207 @@ var init_arm = __esm({
187624
188815
  }
187625
188816
  });
187626
188817
 
187627
- // ../../packages/diagrams/src/importers/shared/yaml/access.ts
187628
- function get(node3, key) {
187629
- if (node3?.kind !== "map") return void 0;
187630
- return node3.entries.find((entry2) => entry2.key === key)?.value;
188818
+ // ../../packages/diagrams/src/importers/backstage/entities.ts
188819
+ function refOf(written3, fallbackKind, namespace) {
188820
+ const colon = written3.indexOf(":");
188821
+ const kind = colon === -1 ? fallbackKind : written3.slice(0, colon).toLowerCase();
188822
+ const rest = colon === -1 ? written3 : written3.slice(colon + 1);
188823
+ const slash = rest.indexOf("/");
188824
+ return slash === -1 ? `${kind}:${namespace}/${rest}` : `${kind}:${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
187631
188825
  }
187632
- function text(node3) {
187633
- return node3?.kind === "scalar" && !node3.isNull ? node3.value : null;
187634
- }
187635
- function items(node3) {
187636
- if (node3 === null || node3 === void 0) return [];
187637
- if (node3.kind === "seq") return node3.items;
187638
- if (node3.kind === "scalar" && node3.isNull) return [];
187639
- return [node3];
187640
- }
187641
- function names5(node3) {
187642
- if (node3?.kind === "map") return node3.entries.map((entry2) => entry2.key);
187643
- return items(node3).map((item) => text(item)).filter((value) => value !== null && value !== "");
187644
- }
187645
- function entries(node3) {
187646
- return node3?.kind === "map" ? node3.entries : [];
187647
- }
187648
- var init_access = __esm({
187649
- "../../packages/diagrams/src/importers/shared/yaml/access.ts"() {
187650
- "use strict";
187651
- }
187652
- });
187653
-
187654
- // ../../packages/diagrams/src/importers/shared/yaml/lines.ts
187655
- function opensQuote(text3, index2) {
187656
- if (index2 === 0) return true;
187657
- const before = text3.charAt(index2 - 1);
187658
- return before === " " || before === " " || before === "[" || before === "{" || before === ",";
187659
- }
187660
- function cutComment(text3) {
187661
- let quote3 = null;
187662
- for (let index2 = 0; index2 < text3.length; index2 += 1) {
187663
- const char = text3.charAt(index2);
187664
- if (quote3 === '"') {
187665
- if (char === "\\") index2 += 1;
187666
- else if (char === '"') quote3 = null;
187667
- } else if (quote3 === "'") {
187668
- if (char === "'" && text3.charAt(index2 + 1) === "'") index2 += 1;
187669
- else if (char === "'") quote3 = null;
187670
- } else if ((char === '"' || char === "'") && opensQuote(text3, index2)) {
187671
- quote3 = char;
187672
- } else if (char === "#" && (index2 === 0 || text3.charAt(index2 - 1) === " " || text3.charAt(index2 - 1) === " ")) {
187673
- return text3.slice(0, index2);
187674
- }
187675
- }
187676
- return text3;
187677
- }
187678
- function blockIndicator(text3) {
187679
- const lastSpace = text3.lastIndexOf(" ");
187680
- const tail = text3.slice(lastSpace + 1);
187681
- if (!/^[|>][-+0-9]{0,2}$/.test(tail)) return null;
187682
- const words2 = text3.slice(0, lastSpace + 1).trim().split(" ").filter((part) => part !== "");
187683
- while (words2.length > 0 && /^[&!]/.test(words2[words2.length - 1] ?? "")) words2.pop();
187684
- const head = words2.join(" ");
187685
- if (head !== "" && !head.endsWith(":") && head !== "-") return null;
187686
- return { folded: tail.startsWith(">"), keep: tail.includes("+"), strip: tail.includes("-") };
187687
- }
187688
- function fold4(lines) {
187689
- let out = "";
187690
- for (const line3 of lines) {
187691
- if (line3 === "") out += "\n";
187692
- else out += out === "" || out.endsWith("\n") ? line3 : ` ${line3}`;
187693
- }
187694
- return out;
187695
- }
187696
- function splitDocuments(text3) {
187697
- const raw = text3.split("\n");
187698
- const documents = [];
187699
- let current = { lines: [], line: 1 };
187700
- let started = false;
187701
- for (let index2 = 0; index2 < raw.length; index2 += 1) {
187702
- const full = (raw[index2] ?? "").replace(/\r$/, "");
187703
- const lineNumber = index2 + 1;
187704
- if (/^---(\s|$)/.test(full) || /^\.\.\.\s*$/.test(full)) {
187705
- if (started || current.lines.length > 0) documents.push(current);
187706
- current = { lines: [], line: lineNumber };
187707
- started = full.startsWith("---");
187708
- const rest = cutComment(full.slice(3)).trim();
187709
- if (full.startsWith("---") && rest !== "" && !/^![^\s]*$/.test(rest)) current.lines.push({ indent: 0, text: rest.replace(/^![^\s]*\s+/, ""), line: lineNumber, block: null });
187710
- continue;
187711
- }
187712
- if (full.startsWith("%") && current.lines.length === 0) continue;
187713
- const indent = full.length - full.replace(/^ +/, "").length;
187714
- if (full.charAt(indent) === " " && full.trim() !== "") {
187715
- return { documents, problem: { line: lineNumber, column: indent + 1, message: "A tab is used to indent this line.", hint: "YAML indents with spaces only. Replace the tab with spaces." } };
187716
- }
187717
- const textPart = cutComment(full.slice(indent)).trimEnd();
187718
- if (textPart === "") continue;
187719
- const indicator = blockIndicator(textPart);
187720
- let block = null;
187721
- if (indicator !== null) {
187722
- const body6 = [];
187723
- let bodyIndent = -1;
187724
- while (index2 + 1 < raw.length) {
187725
- const next = (raw[index2 + 1] ?? "").replace(/\r$/, "");
187726
- const nextIndent = next.length - next.replace(/^ +/, "").length;
187727
- if (next.trim() !== "" && nextIndent <= indent) break;
187728
- if (next.trim() !== "" && bodyIndent === -1) bodyIndent = nextIndent;
187729
- body6.push(next.trim() === "" ? "" : next.slice(Math.max(0, bodyIndent)));
187730
- index2 += 1;
187731
- }
187732
- while (!indicator.keep && body6.length > 0 && body6[body6.length - 1] === "") body6.pop();
187733
- const joined2 = indicator.folded ? fold4(body6) : body6.join("\n");
187734
- block = indicator.strip || joined2 === "" ? joined2 : `${joined2}
187735
- `;
187736
- }
187737
- current.lines.push({ indent, text: indicator === null ? textPart : textPart.replace(/[|>][-+0-9]*$/, "").trimEnd(), line: lineNumber, block });
187738
- }
187739
- if (started || current.lines.length > 0) documents.push(current);
187740
- return { documents, problem: null };
188826
+ function readEntity2(root) {
188827
+ const kindText = text(get(root, "kind"));
188828
+ const name2 = text(get(get(root, "metadata"), "name"));
188829
+ if (kindText === null || name2 === null) return null;
188830
+ const kind = DRAWN_KINDS2.find((entry2) => entry2.toLowerCase() === kindText.toLowerCase());
188831
+ if (kind === void 0) return QUIET_KINDS2.has(kindText) ? null : { skipped: kindText, line: root.line, column: root.column };
188832
+ const namespace = text(get(get(root, "metadata"), "namespace")) ?? "default";
188833
+ const spec = get(root, "spec");
188834
+ const one = (key, fallback) => {
188835
+ const value = text(get(spec, key));
188836
+ return value === null || value === "" ? null : refOf(value, fallback, namespace);
188837
+ };
188838
+ const many = (key, fallback) => names5(get(spec, key)).map((value) => refOf(value, fallback, namespace));
188839
+ return {
188840
+ kind,
188841
+ ref: `${kind.toLowerCase()}:${namespace}/${name2}`,
188842
+ name: name2,
188843
+ title: text(get(get(root, "metadata"), "title")) ?? text(get(get(spec, "profile"), "displayName")),
188844
+ type: text(get(spec, "type")),
188845
+ lifecycle: text(get(spec, "lifecycle")),
188846
+ owner: one("owner", "group"),
188847
+ system: one("system", "system"),
188848
+ domain: kind === "System" ? one("domain", "domain") : null,
188849
+ parent: kind === "Group" ? one("parent", "group") : one("subcomponentOf", "component"),
188850
+ providesApis: many("providesApis", "api"),
188851
+ consumesApis: many("consumesApis", "api"),
188852
+ dependsOn: many("dependsOn", "component"),
188853
+ dependencyOf: many("dependencyOf", "component"),
188854
+ memberOf: many("memberOf", "group"),
188855
+ line: root.line,
188856
+ column: root.column
188857
+ };
187741
188858
  }
187742
- var init_lines = __esm({
187743
- "../../packages/diagrams/src/importers/shared/yaml/lines.ts"() {
188859
+ var DRAWN_KINDS2, QUIET_KINDS2, nameOfRef;
188860
+ var init_entities = __esm({
188861
+ "../../packages/diagrams/src/importers/backstage/entities.ts"() {
187744
188862
  "use strict";
188863
+ init_yaml();
188864
+ DRAWN_KINDS2 = ["Domain", "System", "Component", "API", "Resource", "Group", "User"];
188865
+ QUIET_KINDS2 = /* @__PURE__ */ new Set(["Location"]);
188866
+ nameOfRef = (ref) => ref.slice(ref.lastIndexOf("/") + 1);
187745
188867
  }
187746
188868
  });
187747
188869
 
187748
- // ../../packages/diagrams/src/importers/shared/yaml/scalars.ts
187749
- function scalar(value, quoted5, line3, column) {
187750
- const isNull = !quoted5 && (value === "" || value === "~" || value === "null" || value === "Null" || value === "NULL");
187751
- return { kind: "scalar", value: isNull ? "" : value, quoted: quoted5, isNull, line: line3, column };
187752
- }
187753
- function readQuoted(text3, start2) {
187754
- const quote3 = text3.charAt(start2);
187755
- let value = "";
187756
- for (let index2 = start2 + 1; index2 < text3.length; index2 += 1) {
187757
- const char = text3.charAt(index2);
187758
- if (quote3 === "'") {
187759
- if (char !== "'") value += char;
187760
- else if (text3.charAt(index2 + 1) === "'") {
187761
- value += "'";
187762
- index2 += 1;
187763
- } else return { value, end: index2 + 1 };
187764
- } else if (char === '"') {
187765
- return { value, end: index2 + 1 };
187766
- } else if (char === "\\") {
187767
- const next = text3.charAt(index2 + 1);
187768
- const width = next === "u" ? 4 : next === "x" ? 2 : next === "U" ? 8 : 0;
187769
- if (width > 0) {
187770
- const code = Number.parseInt(text3.slice(index2 + 2, index2 + 2 + width), 16);
187771
- value += Number.isFinite(code) && code <= 1114111 ? String.fromCodePoint(code) : "";
187772
- index2 += 1 + width;
187773
- } else {
187774
- value += ESCAPES2[next] ?? next;
187775
- index2 += 1;
187776
- }
187777
- } else {
187778
- value += char;
187779
- }
188870
+ // ../../packages/diagrams/src/importers/backstage/index.ts
188871
+ function brandOf(...texts) {
188872
+ for (const word2 of texts.flatMap((entry2) => (entry2 ?? "").split(/[^A-Za-z0-9]+/))) {
188873
+ const icon = word2.length < 3 ? null : iconForName(word2, "service");
188874
+ if (icon?.startsWith("simple:") === true) return icon;
187780
188875
  }
187781
188876
  return null;
187782
188877
  }
187783
- function stripProperties(text3) {
187784
- let rest = text3;
187785
- let anchor2 = null;
187786
- let tag = null;
187787
- let offset = 0;
187788
- for (let round2 = 0; round2 < 2; round2 += 1) {
187789
- const match = /^([!&])([^\s,[\]{}]*)\s*/.exec(rest);
187790
- if (match === null) break;
187791
- if (match[1] === "&") anchor2 = match[2] ?? null;
187792
- else tag = `!${match[2] ?? ""}`;
187793
- offset += match[0].length;
187794
- rest = rest.slice(match[0].length);
187795
- }
187796
- return { text: rest, anchor: anchor2, tag, offset };
187797
- }
187798
- function tagged(node3, tag) {
187799
- return tag === null || tag.startsWith("!!") ? node3 : { ...node3, tag };
187800
- }
187801
- function readFlow2(text3, line3, column, resolveAlias) {
187802
- const reader = new FlowReader(text3, line3, column, resolveAlias);
187803
- const node3 = reader.value(0);
187804
- if (!reader.done()) throw new FlowError("Text follows the end of the flow collection.");
187805
- return node3;
187806
- }
187807
- function flowClosed(text3) {
187808
- let depth = 0;
187809
- let quote3 = null;
187810
- for (let index2 = 0; index2 < text3.length; index2 += 1) {
187811
- const char = text3.charAt(index2);
187812
- if (quote3 !== null) {
187813
- if (quote3 === '"' && char === "\\") index2 += 1;
187814
- else if (char === quote3) quote3 = null;
187815
- } else if (char === '"' || char === "'") quote3 = char;
187816
- else if (char === "[" || char === "{") depth += 1;
187817
- else if (char === "]" || char === "}") depth -= 1;
188878
+ function boxOf3(entity, byOwner) {
188879
+ const type = (entity.type ?? "").toLowerCase();
188880
+ const said = (words3) => cleanText([words3, entity.lifecycle === null || entity.lifecycle === "production" ? null : entity.lifecycle, byOwner || entity.owner === null ? null : `owned by ${nameOfRef(entity.owner)}`].filter((part) => part !== null).join(", "));
188881
+ const label = entity.title ?? entity.name;
188882
+ if (entity.kind === "API") return { type: "gateway", label, sub: said(`${API_NAMES[type] ?? entity.type ?? ""} API`.trim()), icon: API_ICONS[type] ?? "lucide:plug", idHint: entity.name };
188883
+ if (entity.kind === "Resource") {
188884
+ const nodeType = RESOURCE_TYPES.find(([pattern3]) => pattern3.test(type))?.[1] ?? "storage";
188885
+ return { type: nodeType, label, sub: said(entity.type === null ? "Resource" : `Resource, ${entity.type}`), icon: brandOf(entity.title, entity.name, entity.type), idHint: entity.name };
188886
+ }
188887
+ if (entity.kind === "User") return { type: "person", label, sub: "User", icon: null, idHint: entity.name };
188888
+ const kindOf3 = type === "website" ? "client" : "service";
188889
+ const words2 = type === "" ? "Component" : `${type.charAt(0).toUpperCase()}${type.slice(1)}`;
188890
+ return { type: kindOf3, label, sub: said(words2), icon: type === "library" ? "lucide:package" : type === "website" ? "lucide:monitor-smartphone" : brandOf(entity.title, entity.name), idHint: entity.name };
188891
+ }
188892
+ function readBackstage(textIn, context) {
188893
+ const read = parseYaml(textIn);
188894
+ if (read.error !== null) {
188895
+ context.report.add(DIAGNOSTIC_CODES.importUnreadable, spanAt3(read.error.line, read.error.column), read.error.message, read.error.hint);
188896
+ return null;
187818
188897
  }
187819
- return depth <= 0 && quote3 === null;
187820
- }
187821
- var MAX_FLOW_DEPTH, ESCAPES2, FlowReader, FlowError;
187822
- var init_scalars = __esm({
187823
- "../../packages/diagrams/src/importers/shared/yaml/scalars.ts"() {
187824
- "use strict";
187825
- MAX_FLOW_DEPTH = 32;
187826
- ESCAPES2 = { n: "\n", t: " ", r: "\r", '"': '"', "\\": "\\", "/": "/", "0": "\0", " ": " ", e: "\x1B", N: "\x85", _: "\xA0" };
187827
- FlowReader = class {
187828
- constructor(text3, line3, column, resolveAlias) {
187829
- this.text = text3;
187830
- this.line = line3;
187831
- this.column = column;
187832
- this.resolveAlias = resolveAlias;
187833
- }
187834
- text;
187835
- line;
187836
- column;
187837
- resolveAlias;
187838
- index = 0;
187839
- skipSpace() {
187840
- while (this.index < this.text.length && /\s/.test(this.text.charAt(this.index))) this.index += 1;
187841
- }
187842
- /** The column in the file of the reader's position, for a node it reads. */
187843
- at() {
187844
- return this.column + this.index;
187845
- }
187846
- /** One value: a collection, a quoted scalar, an alias, or a plain scalar up to `,`, `]`, `}`, or `: `. */
187847
- value(depth) {
187848
- if (depth > MAX_FLOW_DEPTH) throw new FlowError("The flow collection is nested too deep.");
187849
- this.skipSpace();
187850
- const char = this.text.charAt(this.index);
187851
- const column = this.at();
187852
- if (char === "[") return this.sequence(depth);
187853
- if (char === "{") return this.mapping(depth);
187854
- if (char === '"' || char === "'") {
187855
- const read = readQuoted(this.text, this.index);
187856
- if (read === null) throw new FlowError("A quoted string is never closed.");
187857
- this.index = read.end;
187858
- return scalar(read.value, true, this.line, column);
187859
- }
187860
- const start2 = this.index;
187861
- while (this.index < this.text.length) {
187862
- const next = this.text.charAt(this.index);
187863
- if (next === "," || next === "]" || next === "}") break;
187864
- if (next === ":" && /[\s,\]}]/.test(this.text.charAt(this.index + 1) || " ")) break;
187865
- this.index += 1;
187866
- }
187867
- const word2 = this.text.slice(start2, this.index).trim();
187868
- if (word2.startsWith("*")) return this.resolveAlias(word2.slice(1)) ?? scalar("", false, this.line, column);
187869
- const props = stripProperties(word2);
187870
- return tagged(scalar(props.text, false, this.line, column), props.tag);
187871
- }
187872
- sequence(depth) {
187873
- const column = this.at();
187874
- this.index += 1;
187875
- const items2 = [];
187876
- for (; ; ) {
187877
- this.skipSpace();
187878
- if (this.index >= this.text.length) throw new FlowError("A [ list is never closed with ].");
187879
- if (this.text.charAt(this.index) === "]") {
187880
- this.index += 1;
187881
- return { kind: "seq", items: items2, line: this.line, column };
187882
- }
187883
- items2.push(this.value(depth + 1));
187884
- this.skipSpace();
187885
- if (this.text.charAt(this.index) === ",") this.index += 1;
187886
- else if (this.text.charAt(this.index) !== "]") throw new FlowError("Items of a [ list are separated by commas.");
187887
- }
187888
- }
187889
- mapping(depth) {
187890
- const column = this.at();
187891
- this.index += 1;
187892
- const entries2 = [];
187893
- for (; ; ) {
187894
- this.skipSpace();
187895
- if (this.index >= this.text.length) throw new FlowError("A { mapping is never closed with }.");
187896
- if (this.text.charAt(this.index) === "}") {
187897
- this.index += 1;
187898
- return { kind: "map", entries: entries2, line: this.line, column };
187899
- }
187900
- const keyColumn = this.at();
187901
- const key = this.value(depth + 1);
187902
- this.skipSpace();
187903
- let value = scalar("", false, this.line, this.at());
187904
- if (this.text.charAt(this.index) === ":") {
187905
- this.index += 1;
187906
- value = this.value(depth + 1);
187907
- }
187908
- entries2.push({ key: key.kind === "scalar" ? key.value : "", value, line: this.line, column: keyColumn });
187909
- this.skipSpace();
187910
- if (this.text.charAt(this.index) === ",") this.index += 1;
187911
- else if (this.text.charAt(this.index) !== "}") throw new FlowError("Entries of a { mapping are separated by commas.");
187912
- }
187913
- }
187914
- /** True when only spaces are left. */
187915
- done() {
187916
- this.skipSpace();
187917
- return this.index >= this.text.length;
187918
- }
187919
- };
187920
- FlowError = class extends Error {
187921
- };
188898
+ const entities = [];
188899
+ const skipped = [];
188900
+ for (const root of read.documents.map((document2) => document2.root).filter((node3) => node3 !== null)) {
188901
+ const entity = readEntity2(root);
188902
+ if (entity === null) continue;
188903
+ if ("skipped" in entity) skipped.push({ kind: entity.skipped, line: entity.line, column: entity.column });
188904
+ else if (!entities.some((other) => other.ref === entity.ref)) entities.push(entity);
188905
+ }
188906
+ const firstSkipped = skipped[0];
188907
+ if (firstSkipped !== void 0) {
188908
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, spanAt3(firstSkipped.line, firstSkipped.column), `${countOf2(skipped.length, "entity", "entities")} of a kind this reader does not draw ${skipped.length === 1 ? "was" : "were"} left out: ${[...new Set(skipped.map((entry2) => entry2.kind))].slice(0, 4).join(", ")}.`, "The reader draws Components, APIs, Resources, Systems, Domains, Groups, and Users.");
188909
+ }
188910
+ const byRef = new Map(entities.map((entity) => [entity.ref, entity]));
188911
+ const byOwner = context.options.groupBy === "owner";
188912
+ const boxes2 = entities.filter((entity) => entity.kind === "Component" || entity.kind === "API" || entity.kind === "Resource");
188913
+ if (boxes2.length === 0) {
188914
+ context.report.add(DIAGNOSTIC_CODES.importNoShape, START, "No Component, API, or Resource was found.", "Send catalog-info.yaml files with apiVersion backstage.io/v1alpha1, a kind, and metadata.name.");
188915
+ return null;
187922
188916
  }
187923
- });
187924
-
187925
- // ../../packages/diagrams/src/importers/shared/yaml/parse.ts
187926
- function splitKey(text3) {
187927
- if (text3.startsWith('"') || text3.startsWith("'")) {
187928
- const read = readQuoted(text3, 0);
187929
- if (read === null) return null;
187930
- const after = text3.slice(read.end).trimStart();
187931
- if (!after.startsWith(":")) return null;
187932
- const colon = text3.length - after.length;
187933
- return /^:(\s|$)/.test(text3.slice(colon)) ? { key: read.value, colon } : null;
188917
+ const groups = [];
188918
+ const frameOf3 = /* @__PURE__ */ new Map();
188919
+ if (byOwner) {
188920
+ for (const group2 of entities.filter((entity) => entity.kind === "Group")) {
188921
+ const members2 = entities.filter((entity) => entity.kind === "User" && entity.memberOf.includes(group2.ref)).map((user) => user.title ?? user.name);
188922
+ const said = members2.length === 0 ? null : members2.length <= 2 ? members2.join(", ") : countOf2(members2.length, "member");
188923
+ groups.push({ key: group2.ref, label: group2.title ?? group2.name, sub: cleanText(["Group", group2.type, said].filter((part) => part !== null).join(", ")), icon: "lucide:users", parent: group2.parent !== null && byRef.has(group2.parent) ? group2.parent : null, idHint: group2.name });
188924
+ }
188925
+ for (const entity of boxes2) if (entity.owner !== null && byRef.get(entity.owner)?.kind === "Group") frameOf3.set(entity.ref, entity.owner);
188926
+ } else {
188927
+ for (const domain2 of entities.filter((entity) => entity.kind === "Domain")) groups.push({ key: domain2.ref, label: domain2.title ?? domain2.name, sub: cleanText(`Domain${domain2.owner === null ? "" : `, owned by ${nameOfRef(domain2.owner)}`}`), icon: "lucide:map", idHint: domain2.name });
188928
+ for (const system of entities.filter((entity) => entity.kind === "System")) groups.push({ key: system.ref, label: system.title ?? system.name, sub: cleanText(`System${system.owner === null ? "" : `, owned by ${nameOfRef(system.owner)}`}`), icon: "lucide:boxes", parent: system.domain !== null && byRef.has(system.domain) ? system.domain : null, idHint: system.name });
188929
+ for (const entity of boxes2) if (entity.system !== null && byRef.get(entity.system)?.kind === "System") frameOf3.set(entity.ref, entity.system);
188930
+ }
188931
+ const order = { Component: 0, API: 1, Resource: 2 };
188932
+ const ordered = [...boxes2].sort((a, b) => order[a.kind] - order[b.kind]);
188933
+ const nodes = ordered.map((entity) => {
188934
+ const box = boxOf3(entity, byOwner);
188935
+ const system = byOwner && entity.system !== null ? `, in ${nameOfRef(entity.system)}` : "";
188936
+ return { key: entity.ref, ...box, sub: box.sub === null || box.sub === void 0 ? null : cleanText(`${box.sub}${system}`), group: frameOf3.get(entity.ref) ?? null };
188937
+ });
188938
+ const drawn = new Set(nodes.map((node3) => node3.key));
188939
+ const edges = [];
188940
+ const unknown2 = /* @__PURE__ */ new Set();
188941
+ const link2 = (from, to, label, arrow) => {
188942
+ if (!drawn.has(to)) {
188943
+ if (!byRef.has(to)) unknown2.add(to);
188944
+ return;
188945
+ }
188946
+ edges.push({ from, to, label, ...arrow === void 0 ? {} : { arrow } });
188947
+ };
188948
+ for (const entity of ordered) {
188949
+ for (const api of entity.providesApis) link2(entity.ref, api, "provides");
188950
+ for (const api of entity.consumesApis) link2(entity.ref, api, "consumes");
188951
+ for (const target of entity.dependsOn) link2(entity.ref, target, "depends on");
188952
+ for (const source of entity.dependencyOf) if (drawn.has(source)) link2(source, entity.ref, "depends on");
188953
+ if (entity.kind === "Component" && entity.parent !== null) link2(entity.ref, entity.parent, "part of", "-->");
187934
188954
  }
187935
- if (/^[[{]/.test(text3) || text3.startsWith("- ") || text3 === "-") return null;
187936
- for (let index2 = text3.indexOf(":"); index2 !== -1; index2 = text3.indexOf(":", index2 + 1)) {
187937
- const next = text3.charAt(index2 + 1);
187938
- if (next === "" || next === " " || next === " ") return { key: text3.slice(0, index2).trim(), colon: index2 };
188955
+ if (unknown2.size > 0) {
188956
+ context.report.add(DIAGNOSTIC_CODES.importUnresolved, START, `${countOf2(unknown2.size, "relation")} name${unknown2.size === 1 ? "s an entity" : " entities"} the files do not describe, so no arrow is drawn: ${[...unknown2].slice(0, 3).join(", ")}.`, "Send the catalog-info.yaml files that describe them, or check that each reference names its kind, such as resource:orders-db.");
187939
188957
  }
187940
- return null;
188958
+ const shown = /* @__PURE__ */ new Set([...nodes.map((node3) => node3.key), ...groups.map((group2) => group2.key)]);
188959
+ const held = new Set([...frameOf3.values()].flatMap((frame) => [frame, ...ancestorsOf2(frame, groups)]));
188960
+ const keptFrames = entities.filter((entity) => held.has(entity.ref));
188961
+ const named = new Set(
188962
+ byOwner ? [...ordered.flatMap((entity) => entity.system === null ? [] : [entity.system, byRef.get(entity.system)?.domain ?? ""]), ...entities.filter((entity) => entity.kind === "User" && entity.memberOf.some((ref) => held.has(ref))).map((user) => user.ref)] : [...ordered, ...keptFrames].flatMap((entity) => entity.owner === null ? [] : [entity.owner])
188963
+ );
188964
+ const framesKept = groups.filter((group2) => held.has(group2.key)).length;
188965
+ const summarised = entities.filter((entity) => !shown.has(entity.ref) && named.has(entity.ref)).length;
188966
+ const left = entities.filter((entity) => (!shown.has(entity.ref) || groups.some((group2) => group2.key === entity.ref) && !held.has(entity.ref)) && !named.has(entity.ref));
188967
+ const firstLeft = left[0];
188968
+ if (firstLeft !== void 0) {
188969
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, spanAt3(firstLeft.line, firstLeft.column), `${countOf2(left.length, "entity", "entities")} ${left.length === 1 ? "holds or owns" : "hold or own"} nothing drawn and ${left.length === 1 ? "is" : "are"} left out: ${left.slice(0, 3).map((entity) => entity.ref).join(", ")}.`, byOwner ? "A group is drawn when it owns something, with its members named on its tab." : "Set options.groupBy to owner to draw the groups and users that own the entities.");
188970
+ }
188971
+ const systems = entities.filter((entity) => entity.kind === "System").length;
188972
+ return writeFlow(
188973
+ {
188974
+ title: context.title ?? (byOwner ? "Software catalog by owner" : "Software catalog"),
188975
+ desc: `${countOf2(nodes.length, "entity", "entities")} of a Backstage catalog, ${byOwner ? "framed by the group that owns them" : `framed by ${countOf2(systems, "system")} and the domains that hold them`}. An arrow says a component provides or consumes an API, or depends on another entity.`,
188976
+ direction: "down",
188977
+ nodes,
188978
+ groups,
188979
+ edges,
188980
+ noun: "entities",
188981
+ capSpan: (key) => {
188982
+ const entity = byRef.get(key);
188983
+ return spanAt3(entity?.line ?? 1, entity?.column ?? 1);
188984
+ },
188985
+ advice: byOwner ? "Check that every entity names its owner; one with none sits outside the frames." : "Check that every component names its system; one with none sits outside the frames.",
188986
+ coverage: { noun: "entities", total: entities.length, drawn: nodes.length + framesKept, summarised }
188987
+ },
188988
+ context.report
188989
+ );
187941
188990
  }
187942
- function parseYaml(text3) {
187943
- const split = splitDocuments(text3);
187944
- if (split.problem !== null) return { documents: [], error: split.problem };
187945
- const documents = [];
187946
- for (const document2 of split.documents) {
187947
- try {
187948
- documents.push({ root: new BlockReader(document2.lines).document(), line: document2.line });
187949
- } catch (error62) {
187950
- if (!(error62 instanceof YamlProblem)) throw error62;
187951
- const problem = { line: error62.line, column: error62.column, message: error62.message, hint: error62.hint };
187952
- return { documents, error: problem };
187953
- }
188991
+ function ancestorsOf2(key, groups) {
188992
+ const out = [];
188993
+ let parent = groups.find((group2) => group2.key === key)?.parent ?? null;
188994
+ for (let depth = 0; parent !== null && depth < groups.length && !out.includes(parent); depth += 1) {
188995
+ out.push(parent);
188996
+ parent = groups.find((group2) => group2.key === parent)?.parent ?? null;
187954
188997
  }
187955
- return { documents, error: null };
188998
+ return out;
187956
188999
  }
187957
- var MAX_DEPTH12, MAX_JOINED_LINES, YamlProblem, isItem3, BlockReader;
187958
- var init_parse23 = __esm({
187959
- "../../packages/diagrams/src/importers/shared/yaml/parse.ts"() {
189000
+ var API_ICONS, RESOURCE_TYPES, API_NAMES, backstageImporter;
189001
+ var init_backstage = __esm({
189002
+ "../../packages/diagrams/src/importers/backstage/index.ts"() {
187960
189003
  "use strict";
187961
- init_lines();
187962
- init_scalars();
187963
- MAX_DEPTH12 = 64;
187964
- MAX_JOINED_LINES = 200;
187965
- YamlProblem = class extends Error {
187966
- constructor(line3, column, message2, hint2) {
187967
- super(message2);
187968
- this.line = line3;
187969
- this.column = column;
187970
- this.hint = hint2;
187971
- }
187972
- line;
187973
- column;
187974
- hint;
187975
- };
187976
- isItem3 = (text3) => text3 === "-" || text3.startsWith("- ");
187977
- BlockReader = class {
187978
- constructor(lines) {
187979
- this.lines = lines;
187980
- }
187981
- lines;
187982
- index = 0;
187983
- anchors = /* @__PURE__ */ new Map();
187984
- problem(line3, message2, hint2) {
187985
- return new YamlProblem(line3.line, line3.indent + 1, message2, hint2);
187986
- }
187987
- /** The whole document: one block, and nothing left over. */
187988
- document() {
187989
- const first = this.lines[0];
187990
- if (first === void 0) return null;
187991
- const root = this.block(first.indent, 0);
187992
- const extra = this.lines[this.index];
187993
- if (extra !== void 0) throw this.problem(extra, "This line is indented less than the block it follows, or not like the lines before it.", "Line it up with the other keys at its level.");
187994
- return root;
187995
- }
187996
- /** A block that starts at the current line, indented `indent` spaces: a sequence, a mapping, or a scalar. */
187997
- block(indent, depth) {
187998
- const line3 = this.lines[this.index];
187999
- if (depth > MAX_DEPTH12) throw this.problem(line3, "The YAML is nested too deep.", `Keep nesting under ${MAX_DEPTH12} levels.`);
188000
- if (isItem3(line3.text)) return this.sequence(indent, depth);
188001
- if (splitKey(line3.text) !== null) return this.mapping(indent, depth);
188002
- this.index += 1;
188003
- return this.value(line3.text, line3, indent, depth, line3.indent + 1);
188004
- }
188005
- mapping(indent, depth) {
188006
- const first = this.lines[this.index];
188007
- const entries2 = [];
188008
- const merges = [];
188009
- while (this.index < this.lines.length) {
188010
- const line3 = this.lines[this.index];
188011
- if (line3.indent < indent) break;
188012
- if (line3.indent > indent) throw this.problem(line3, "This line is indented more than the keys around it.", "Indent it like the other keys of the same mapping.");
188013
- if (isItem3(line3.text)) break;
188014
- const split = splitKey(line3.text);
188015
- if (split === null) throw this.problem(line3, "Expected a key followed by a colon.", "Write it as key: value, with a space after the colon.");
188016
- this.index += 1;
188017
- const rest = line3.text.slice(split.colon + 1).trim();
188018
- const value = this.entryValue(rest, line3, indent, depth, line3.indent + split.colon + 2);
188019
- if (split.key === "<<") merges.push(value);
188020
- else entries2.push({ key: split.key, value, line: line3.line, column: line3.indent + 1 });
188021
- }
188022
- for (const merge3 of merges) {
188023
- const sources = merge3.kind === "seq" ? merge3.items : [merge3];
188024
- for (const source of sources) {
188025
- if (source.kind !== "map") continue;
188026
- for (const entry2 of source.entries) if (!entries2.some((own2) => own2.key === entry2.key)) entries2.push(entry2);
188027
- }
188028
- }
188029
- return { kind: "map", entries: entries2, line: first.line, column: first.indent + 1 };
188030
- }
188031
- /** The value of a mapping entry: on its own line, in a block below it, or a sequence at the same indent. */
188032
- entryValue(rest, line3, indent, depth, column) {
188033
- const next = this.lines[this.index];
188034
- const props = stripProperties(rest);
188035
- if (props.text === "" && line3.block === null && next !== void 0 && (next.indent > indent || next.indent === indent && isItem3(next.text))) {
188036
- const node3 = tagged(this.block(next.indent, depth + 1), props.tag);
188037
- if (props.anchor !== null) this.anchors.set(props.anchor, node3);
188038
- return node3;
188039
- }
188040
- return this.value(rest, line3, indent, depth, column, true);
188041
- }
188042
- sequence(indent, depth) {
188043
- const first = this.lines[this.index];
188044
- const items2 = [];
188045
- while (this.index < this.lines.length) {
188046
- const line3 = this.lines[this.index];
188047
- if (line3.indent !== indent || !isItem3(line3.text)) {
188048
- if (line3.indent > indent) throw this.problem(line3, "This line is indented more than the items around it.", "Indent it like the other items, or under the item it belongs to.");
188049
- break;
188050
- }
188051
- const rest = line3.text.slice(1).trimStart();
188052
- const restColumn = line3.indent + (line3.text.length - rest.length);
188053
- if (rest === "" && line3.block === null) {
188054
- this.index += 1;
188055
- const next = this.lines[this.index];
188056
- items2.push(next !== void 0 && next.indent > indent ? this.block(next.indent, depth + 1) : scalar("", false, line3.line, restColumn + 1));
188057
- } else if (line3.block === null && /^![A-Za-z:]+$/.test(rest) && this.lines[this.index + 1] !== void 0 && this.lines[this.index + 1].indent > indent) {
188058
- const tag = rest;
188059
- this.index += 1;
188060
- items2.push(tagged(this.block(this.lines[this.index].indent, depth + 1), tag));
188061
- } else if (line3.block === null && (isItem3(rest) || splitKey(stripProperties(rest).text) !== null)) {
188062
- const props = stripProperties(rest);
188063
- line3.indent = restColumn + props.offset;
188064
- line3.text = props.text;
188065
- const node3 = this.block(line3.indent, depth + 1);
188066
- if (props.anchor !== null) this.anchors.set(props.anchor, node3);
188067
- items2.push(node3);
188068
- } else {
188069
- this.index += 1;
188070
- items2.push(this.value(rest, line3, indent, depth, restColumn + 1));
188071
- }
188072
- }
188073
- return { kind: "seq", items: items2, line: first.line, column: first.indent + 1 };
188074
- }
188075
- /** A value written on a line: a block scalar, an alias, a flow collection, a quoted scalar, or a plain scalar that may run on. */
188076
- value(text3, line3, indent, depth, column, ofKey = false) {
188077
- const props = stripProperties(text3);
188078
- const body6 = props.text;
188079
- let node3;
188080
- if (line3.block !== null) {
188081
- node3 = scalar(line3.block, true, line3.line, column);
188082
- } else if (body6.startsWith("*")) {
188083
- const target = this.anchors.get(body6.slice(1).trim());
188084
- if (target === void 0) throw this.problem(line3, `The alias ${body6} names no anchor written before it.`, "Write the anchor (&name) before the alias that uses it.");
188085
- node3 = target;
188086
- } else if (body6.startsWith("[") || body6.startsWith("{")) {
188087
- node3 = this.flow(this.joined(body6, flowClosed), line3, column + props.offset);
188088
- } else if (body6.startsWith('"') || body6.startsWith("'")) {
188089
- const whole2 = this.joined(body6, (joined2) => readQuoted(joined2, 0) !== null);
188090
- const read = readQuoted(whole2, 0);
188091
- if (read === null) throw this.problem(line3, "A quoted string is never closed.", "Close it with the same quote it opened with.");
188092
- node3 = scalar(read.value, true, line3.line, column + props.offset);
188093
- } else {
188094
- let plain3 = body6;
188095
- while (this.index < this.lines.length) {
188096
- const more = this.lines[this.index];
188097
- if (more.indent < indent || more.indent === indent && !ofKey) break;
188098
- if (splitKey(more.text) !== null || isItem3(more.text)) break;
188099
- plain3 = `${plain3} ${more.text}`;
188100
- this.index += 1;
188101
- }
188102
- node3 = scalar(plain3.trim(), false, line3.line, column + props.offset);
188103
- }
188104
- node3 = tagged(node3, props.tag);
188105
- if (props.anchor !== null) this.anchors.set(props.anchor, node3);
188106
- return node3;
188107
- }
188108
- /** The text of a value that runs over several lines until `complete` says it is whole, consuming those lines. */
188109
- joined(start2, complete) {
188110
- let text3 = start2;
188111
- for (let count3 = 0; !complete(text3) && count3 < MAX_JOINED_LINES && this.index < this.lines.length; count3 += 1) {
188112
- text3 = `${text3} ${this.lines[this.index].text}`;
188113
- this.index += 1;
188114
- }
188115
- return text3;
188116
- }
188117
- flow(text3, line3, column) {
188118
- try {
188119
- return readFlow2(text3, line3.line, column, (name2) => this.anchors.get(name2) ?? null);
188120
- } catch (error62) {
188121
- if (error62 instanceof FlowError) throw this.problem(line3, error62.message, "Close every [ and { on the line it opens, and separate items with commas.");
188122
- throw error62;
188123
- }
188124
- }
188125
- };
188126
- }
188127
- });
188128
-
188129
- // ../../packages/diagrams/src/importers/shared/yaml/index.ts
188130
- var init_yaml = __esm({
188131
- "../../packages/diagrams/src/importers/shared/yaml/index.ts"() {
188132
- "use strict";
188133
- init_access();
188134
- init_parse23();
189004
+ init_core();
189005
+ init_shared2();
189006
+ init_yaml();
189007
+ init_entities();
189008
+ API_ICONS = { openapi: "simple:swagger", graphql: "simple:graphql", trpc: "simple:trpc", grpc: "lucide:plug-zap", asyncapi: "lucide:radio" };
189009
+ RESOURCE_TYPES = [
189010
+ [/database|db|postgres|mysql|sql|mongo|dynamo|table/, "database"],
189011
+ [/cache|redis|memcache/, "cache"],
189012
+ [/queue|topic|stream|kafka|bus|sqs|sns|pubsub/, "queue"],
189013
+ [/secret|vault|key/, "secret"],
189014
+ [/bucket|storage|s3|blob|file/, "storage"],
189015
+ [/cluster|kubernetes|k8s/, "service"]
189016
+ ];
189017
+ API_NAMES = { openapi: "OpenAPI", graphql: "GraphQL", trpc: "tRPC", grpc: "gRPC", asyncapi: "AsyncAPI" };
189018
+ backstageImporter = { format: "backstage", kinds: ["flow"], joins: "\n---\n", extensions: [".yaml", ".yml"], read: readBackstage };
188135
189019
  }
188136
189020
  });
188137
189021
 
@@ -188364,18 +189248,20 @@ var init_template = __esm({
188364
189248
  itemsOf = (node3) => node3.kind === "seq" ? node3.items : [node3];
188365
189249
  subNames = (template) => [...template.matchAll(/\$\{([A-Za-z0-9_:.]+)\}/g)].map((match) => match[1] ?? "");
188366
189250
  Template = class {
188367
- parameters;
188368
- conditions;
188369
- mappings;
188370
- resources;
188371
- conditionMemo = /* @__PURE__ */ new Map();
188372
- constructor(root) {
189251
+ constructor(root, external = null) {
189252
+ this.external = external;
188373
189253
  const section = (key) => entryOf(root, key)?.kind === "map" ? entryOf(root, key).entries : [];
188374
189254
  this.parameters = new Map(section("Parameters").map((entry2) => [entry2.key, entry2.value]));
188375
189255
  this.conditions = new Map(section("Conditions").map((entry2) => [entry2.key, entry2.value]));
188376
189256
  this.mappings = entryOf(root, "Mappings");
188377
189257
  this.resources = new Map(section("Resources").filter((entry2) => entry2.value.kind === "map").map((entry2) => [entry2.key, entry2.value]));
188378
189258
  }
189259
+ external;
189260
+ parameters;
189261
+ conditions;
189262
+ mappings;
189263
+ resources;
189264
+ conditionMemo = /* @__PURE__ */ new Map();
188379
189265
  /** The value of a node as far as the template's defaults give it. */
188380
189266
  evaluate(node3, depth = 0) {
188381
189267
  if (node3 === void 0 || depth > MAX_DEPTH13) return new Unknown("value");
@@ -188492,11 +189378,18 @@ var init_template = __esm({
188492
189378
  const argument = intrinsic.argument;
188493
189379
  if (intrinsic.name === "Ref") {
188494
189380
  const name2 = scalarOf(argument) ?? "";
188495
- return this.resources.has(name2) ? [name2] : [];
189381
+ return this.resources.has(name2) ? [name2] : [...this.external?.parameter(name2) ?? []];
188496
189382
  }
188497
189383
  if (intrinsic.name === "GetAtt") {
188498
- const target = getAttOf(argument)?.resource ?? "";
188499
- return this.resources.has(target) ? [target] : [];
189384
+ const target = getAttOf(argument);
189385
+ const outside = target === null ? null : this.external?.attribute(target.resource, target.attribute) ?? null;
189386
+ if (outside !== null) return [...outside];
189387
+ return this.resources.has(target?.resource ?? "") ? [target?.resource ?? ""] : [];
189388
+ }
189389
+ if (intrinsic.name === "ImportValue" && this.external !== null) {
189390
+ const name2 = textOf5(this.evaluate(argument, depth + 1));
189391
+ const outside = name2 === null ? null : this.external.imported(name2);
189392
+ if (outside !== null) return [...outside];
188500
189393
  }
188501
189394
  if (intrinsic.name === "Sub") {
188502
189395
  const template = argument.kind === "seq" ? scalarOf(argument.items[0]) : scalarOf(argument);
@@ -188533,17 +189426,17 @@ var init_template = __esm({
188533
189426
  });
188534
189427
 
188535
189428
  // ../../packages/diagrams/src/importers/cloudformation/index.ts
188536
- function nodeOf(value, depth = 0) {
189429
+ function nodeOf2(value, depth = 0) {
188537
189430
  const at3 = { line: 1, column: 1 };
188538
189431
  if (depth > 64 || value === null || value === void 0) return { kind: "scalar", value: "", quoted: false, isNull: true, ...at3 };
188539
- if (Array.isArray(value)) return { kind: "seq", items: value.map((item) => nodeOf(item, depth + 1)), ...at3 };
188540
- if (typeof value === "object") return { kind: "map", entries: Object.entries(value).map(([key, entry2]) => ({ key, value: nodeOf(entry2, depth + 1), ...at3 })), ...at3 };
189432
+ if (Array.isArray(value)) return { kind: "seq", items: value.map((item) => nodeOf2(item, depth + 1)), ...at3 };
189433
+ if (typeof value === "object") return { kind: "map", entries: Object.entries(value).map(([key, entry2]) => ({ key, value: nodeOf2(entry2, depth + 1), ...at3 })), ...at3 };
188541
189434
  return { kind: "scalar", value: String(value), quoted: typeof value === "string", isNull: false, ...at3 };
188542
189435
  }
188543
189436
  function readFile(text3) {
188544
189437
  if (/^\s*\{/.test(text3)) {
188545
189438
  try {
188546
- return { root: nodeOf(JSON.parse(text3)), problem: null };
189439
+ return { root: nodeOf2(JSON.parse(text3)), problem: null };
188547
189440
  } catch (error62) {
188548
189441
  return { root: null, problem: { line: 1, column: 1, message: `The template is not JSON: ${error62 instanceof Error ? error62.message : "it cannot be read"}.`, hint: "Send the template as CloudFormation writes it, in JSON or YAML." } };
188549
189442
  }
@@ -188633,6 +189526,451 @@ var init_cloudformation = __esm({
188633
189526
  }
188634
189527
  });
188635
189528
 
189529
+ // ../../packages/diagrams/src/importers/cdk/assembly.ts
189530
+ function jsonOf(file2) {
189531
+ if (!/^\s*\{/.test(file2.text)) return void 0;
189532
+ try {
189533
+ return JSON.parse(file2.text);
189534
+ } catch {
189535
+ return void 0;
189536
+ }
189537
+ }
189538
+ function manifestStacks(json2, file2) {
189539
+ const out = [];
189540
+ const artifacts = isRecord4(json2["artifacts"]) ? json2["artifacts"] : {};
189541
+ for (const [id, artifact] of Object.entries(artifacts)) {
189542
+ if (!isRecord4(artifact) || artifact["type"] !== "aws:cloudformation:stack") continue;
189543
+ const properties = isRecord4(artifact["properties"]) ? artifact["properties"] : {};
189544
+ const template = typeof properties["templateFile"] === "string" ? properties["templateFile"] : `${id}.template.json`;
189545
+ const logicalPaths = /* @__PURE__ */ new Map();
189546
+ if (isRecord4(artifact["metadata"])) {
189547
+ for (const [path, entries2] of Object.entries(artifact["metadata"])) {
189548
+ if (!Array.isArray(entries2)) continue;
189549
+ for (const entry2 of entries2) if (isRecord4(entry2) && entry2["type"] === "aws:cdk:logicalId" && typeof entry2["data"] === "string") logicalPaths.set(entry2["data"], path.replace(/^\//, ""));
189550
+ }
189551
+ }
189552
+ const at3 = file2.text.indexOf(JSON.stringify(id));
189553
+ out.push({ path: typeof artifact["displayName"] === "string" ? artifact["displayName"] : id, template: `${folderOf4(file2.path)}${template}`, logicalPaths, start: file2.start + Math.max(0, at3) });
189554
+ }
189555
+ return out;
189556
+ }
189557
+ function treeClasses(json2) {
189558
+ const out = /* @__PURE__ */ new Map();
189559
+ const walk = (node3, depth) => {
189560
+ if (!isRecord4(node3) || depth > 64 || out.size > 5e4) return;
189561
+ const info = isRecord4(node3["constructInfo"]) ? node3["constructInfo"] : {};
189562
+ if (typeof node3["path"] === "string" && typeof info["fqn"] === "string") out.set(node3["path"], info["fqn"]);
189563
+ if (isRecord4(node3["children"])) for (const child of Object.values(node3["children"])) walk(child, depth + 1);
189564
+ };
189565
+ walk(json2["tree"], 0);
189566
+ return out;
189567
+ }
189568
+ function cdkPaths(root) {
189569
+ const out = /* @__PURE__ */ new Map();
189570
+ const resources = get(root, "Resources");
189571
+ for (const entry2 of resources?.kind === "map" ? resources.entries : []) {
189572
+ const path = text(get(get(entry2.value, "Metadata"), "aws:cdk:path"));
189573
+ if (path !== null) out.set(entry2.key, path);
189574
+ }
189575
+ return out;
189576
+ }
189577
+ function ownStackPath(root, file2) {
189578
+ const paths = [...cdkPaths(root).values()];
189579
+ const metadata = paths.find((path) => path.endsWith("/CDKMetadata/Default"));
189580
+ if (metadata !== void 0) return metadata.slice(0, -"/CDKMetadata/Default".length);
189581
+ const named = /^(.+?)\.template\.(?:json|ya?ml)$/i.exec(baseName4(file2.path))?.[1];
189582
+ if (named !== void 0) return named;
189583
+ return paths[0]?.split("/")[0] ?? "Stack";
189584
+ }
189585
+ function readAssembly(files) {
189586
+ const stacks = [];
189587
+ const classes = /* @__PURE__ */ new Map();
189588
+ const unknown2 = [];
189589
+ const problems = [];
189590
+ const read = [];
189591
+ for (const file2 of files) {
189592
+ const name2 = baseName4(file2.path);
189593
+ if (name2 === "cdk.out" || name2.endsWith(".assets.json")) continue;
189594
+ const json2 = jsonOf(file2);
189595
+ if (isRecord4(json2) && isRecord4(json2["artifacts"])) {
189596
+ stacks.push(...manifestStacks(json2, file2));
189597
+ continue;
189598
+ }
189599
+ if (isRecord4(json2) && isRecord4(json2["tree"])) {
189600
+ for (const [path, fqn] of treeClasses(json2)) classes.set(path, fqn);
189601
+ continue;
189602
+ }
189603
+ if (isRecord4(json2) && json2["Resources"] === void 0 && (isRecord4(json2["files"]) || isRecord4(json2["dockerImages"]))) continue;
189604
+ const template = readFile(file2.text);
189605
+ if (template.problem !== null || template.root === null) {
189606
+ problems.push({ file: file2, ...template.problem ?? { line: 1, column: 1, message: "The file is empty.", hint: "Send the templates of cdk.out, with manifest.json." } });
189607
+ continue;
189608
+ }
189609
+ if (get(template.root, "Resources") === void 0) unknown2.push(file2);
189610
+ else read.push({ file: file2, root: template.root });
189611
+ }
189612
+ const templates = [];
189613
+ const claimed = /* @__PURE__ */ new Set();
189614
+ const byStack = (stack) => read.find((entry2) => entry2.file.path === stack.template) ?? read.find((entry2) => baseName4(entry2.file.path) === baseName4(stack.template) && !claimed.has(entry2.file));
189615
+ const missing = [];
189616
+ for (const stack of stacks) {
189617
+ const found = byStack(stack);
189618
+ if (found === void 0 || claimed.has(found.file)) {
189619
+ if (found === void 0) missing.push({ stack: stack.path, file: stack.template, start: stack.start });
189620
+ continue;
189621
+ }
189622
+ claimed.add(found.file);
189623
+ templates.push({ file: found.file, root: found.root, stackPath: stack.path, topPath: stack.path, parent: null, manifestPaths: stack.logicalPaths });
189624
+ }
189625
+ const nestedOf = /* @__PURE__ */ new Map();
189626
+ for (const entry2 of read) if (!claimed.has(entry2.file) && /\.nested\.template\.json$/i.test(entry2.file.path)) nestedOf.set(baseName4(entry2.file.path), entry2);
189627
+ for (const entry2 of read) {
189628
+ if (claimed.has(entry2.file) || nestedOf.get(baseName4(entry2.file.path)) === entry2) continue;
189629
+ claimed.add(entry2.file);
189630
+ const path = ownStackPath(entry2.root, entry2.file);
189631
+ templates.push({ file: entry2.file, root: entry2.root, stackPath: path, topPath: path, parent: null, manifestPaths: /* @__PURE__ */ new Map() });
189632
+ }
189633
+ for (let index2 = 0; index2 < templates.length; index2 += 1) {
189634
+ const template = templates[index2];
189635
+ const resources = get(template.root, "Resources");
189636
+ for (const entry2 of resources?.kind === "map" ? resources.entries : []) {
189637
+ if (text(get(entry2.value, "Type")) !== "AWS::CloudFormation::Stack") continue;
189638
+ const asset = text(get(get(entry2.value, "Metadata"), "aws:asset:path"));
189639
+ const nested2 = asset === null ? void 0 : nestedOf.get(baseName4(asset));
189640
+ if (nested2 === void 0 || claimed.has(nested2.file)) continue;
189641
+ claimed.add(nested2.file);
189642
+ const path = text(get(get(entry2.value, "Metadata"), "aws:cdk:path")) ?? template.manifestPaths.get(entry2.key) ?? `${template.stackPath}/${entry2.key}`;
189643
+ templates.push({ file: nested2.file, root: nested2.root, stackPath: nestedStackPath(path), topPath: template.topPath, parent: { template, logical: entry2.key }, manifestPaths: /* @__PURE__ */ new Map() });
189644
+ }
189645
+ }
189646
+ for (const entry2 of nestedOf.values()) {
189647
+ if (claimed.has(entry2.file)) continue;
189648
+ const path = ownStackPath(entry2.root, entry2.file);
189649
+ templates.push({ file: entry2.file, root: entry2.root, stackPath: path, topPath: path, parent: null, manifestPaths: /* @__PURE__ */ new Map() });
189650
+ }
189651
+ return { templates, classes, missing, unknown: unknown2, problems };
189652
+ }
189653
+ var isRecord4, baseName4, folderOf4, nestedStackPath;
189654
+ var init_assembly = __esm({
189655
+ "../../packages/diagrams/src/importers/cdk/assembly.ts"() {
189656
+ "use strict";
189657
+ init_cloudformation();
189658
+ init_yaml();
189659
+ isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
189660
+ baseName4 = (path) => path.slice(path.lastIndexOf("/") + 1);
189661
+ folderOf4 = (path) => path.includes("/") ? path.slice(0, path.lastIndexOf("/") + 1) : "";
189662
+ nestedStackPath = (path) => path.replace(/\.NestedStack\/[^/]*\.NestedStackResource$/, "");
189663
+ }
189664
+ });
189665
+
189666
+ // ../../packages/diagrams/src/importers/cdk/constructs.ts
189667
+ function readable(segment, stack) {
189668
+ const own2 = (segment.split(/[.:]/)[0] || segment).replace(/[0-9A-F]{8}$/, "");
189669
+ const at3 = stack.length >= 3 ? own2.indexOf(stack) : -1;
189670
+ return (at3 > 0 ? own2.slice(0, at3) : own2) || segment;
189671
+ }
189672
+ function frameModules2(tree, classes, startOf3) {
189673
+ const scopes = /* @__PURE__ */ new Map();
189674
+ const used = /* @__PURE__ */ new Set();
189675
+ const modules = [];
189676
+ for (const path of [...tree.frames.keys()].sort((a, b) => a.split("/").length - b.split("/").length || (a < b ? -1 : 1))) {
189677
+ const parent = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
189678
+ const id = path.slice(path.lastIndexOf("/") + 1);
189679
+ const base = `${scopes.get(parent) ?? ""}module.${safe2(id)}`;
189680
+ let scope = `${base}.`;
189681
+ for (let count3 = 2; used.has(scope); count3 += 1) scope = `${base}_${count3}.`;
189682
+ used.add(scope);
189683
+ scopes.set(path, scope);
189684
+ const kind = tree.frames.get(path) ?? "Construct";
189685
+ const fqn = classes.get(path) ?? "";
189686
+ const own2 = /^(?:aws-cdk-lib|@aws-cdk\/[^.]+)\./.test(fqn) && !/\.(?:Stack|NestedStack|Stage)$/.test(fqn) ? fqn.split(".").pop() ?? "" : "";
189687
+ modules.push({ scope, name: path, label: id, instance: null, source: own2, start: startOf3(path), noun: kind });
189688
+ }
189689
+ return { modules, scopes };
189690
+ }
189691
+ var OWN_RESOURCE, constructOf, ConstructTree, safe2;
189692
+ var init_constructs = __esm({
189693
+ "../../packages/diagrams/src/importers/cdk/constructs.ts"() {
189694
+ "use strict";
189695
+ OWN_RESOURCE = /\/(?:Resource|Default)$/;
189696
+ constructOf = (path) => path.replace(OWN_RESOURCE, "");
189697
+ ConstructTree = class {
189698
+ constructor(paths, roots) {
189699
+ this.roots = roots;
189700
+ for (const path of paths) if (OWN_RESOURCE.test(path)) this.wrapping.add(constructOf(path));
189701
+ }
189702
+ roots;
189703
+ /** Construct paths that wrap a resource of their own. */
189704
+ wrapping = /* @__PURE__ */ new Set();
189705
+ /** Frames by path, with what each is. */
189706
+ frames = /* @__PURE__ */ new Map();
189707
+ /** The top stack or nested stack a path belongs to: the longest root it starts with. */
189708
+ rootOf(path) {
189709
+ let best = "";
189710
+ for (const root of this.roots.keys()) if ((path === root || path.startsWith(`${root}/`)) && root.length > best.length) best = root;
189711
+ return best;
189712
+ }
189713
+ /** The frames a resource's construct sits in, and its label: its own id, or, made by a level 2 construct, that construct's id and its own. */
189714
+ place(path, framedStacks) {
189715
+ const construct = constructOf(path);
189716
+ const frames = [];
189717
+ const root = this.rootOf(construct);
189718
+ const top = [...this.roots].filter(([candidate2, kind]) => kind !== "Nested stack" && (root === candidate2 || root.startsWith(`${candidate2}/`))).sort((a, b) => a[0].length - b[0].length)[0]?.[0] ?? "";
189719
+ const segments = construct.split("/");
189720
+ let wrapper = null;
189721
+ for (let depth = 1; depth < segments.length; depth += 1) {
189722
+ const ancestor = segments.slice(0, depth).join("/");
189723
+ const kind = this.roots.get(ancestor);
189724
+ if (ancestor.length <= top.length) {
189725
+ if (framedStacks && top !== "") this.addFrame(frames, ancestor, ancestor === top ? "Stack" : "Stage");
189726
+ continue;
189727
+ }
189728
+ if (kind === "Nested stack") {
189729
+ this.addFrame(frames, ancestor, kind);
189730
+ wrapper = null;
189731
+ continue;
189732
+ }
189733
+ if (wrapper !== null) continue;
189734
+ if (this.wrapping.has(ancestor)) wrapper = depth - 1;
189735
+ else this.addFrame(frames, ancestor, "Construct");
189736
+ }
189737
+ const stack = top.slice(top.lastIndexOf("/") + 1);
189738
+ const own2 = segments.map((segment) => readable(segment, stack));
189739
+ const last = own2[own2.length - 1] ?? construct;
189740
+ if (wrapper === null) return { frames, label: last, maker: null };
189741
+ const rest = own2.slice(wrapper + 1).filter((segment) => segment !== "Default");
189742
+ const maker = own2[wrapper] ?? "";
189743
+ return { frames, label: rest.length >= 2 ? [rest[0], rest[rest.length - 1]].join("_") : [maker, ...rest].join("_"), maker };
189744
+ }
189745
+ addFrame(frames, path, kind) {
189746
+ frames.push(path);
189747
+ if (!this.frames.has(path)) this.frames.set(path, kind);
189748
+ }
189749
+ };
189750
+ safe2 = (name2) => name2.replace(/[^A-Za-z0-9_-]+/g, "_") || "construct";
189751
+ }
189752
+ });
189753
+
189754
+ // ../../packages/diagrams/src/importers/cdk/index.ts
189755
+ function attributeOf2(attribute2, logical, names8) {
189756
+ const name2 = names8.get(logical) ?? logical;
189757
+ const refs = attribute2.refs.map((ref) => names8.get(ref) ?? ref).filter((ref) => ref !== name2);
189758
+ if (attribute2.name !== "name" || attribute2.value === null) return [{ ...attribute2, refs }];
189759
+ if (attribute2.value === logical) return [];
189760
+ if (!/^[A-Za-z][A-Za-z0-9-]*(?:\/[A-Za-z0-9.:_-]+)+$/.test(attribute2.value)) return [{ ...attribute2, refs }];
189761
+ const last = attribute2.value.slice(attribute2.value.lastIndexOf("/") + 1);
189762
+ return [{ ...attribute2, refs, value: last, readable: last }];
189763
+ }
189764
+ function chosenStacks(templates, context) {
189765
+ const wanted2 = context.options.root?.replace(/^\.\//, "").replace(/\/+$/, "");
189766
+ if (wanted2 === void 0 || wanted2 === "") return templates;
189767
+ const tops = [...new Set(templates.map((template) => template.topPath))];
189768
+ const top = tops.find((path) => path === wanted2) ?? tops.find((path) => path.endsWith(`/${wanted2}`)) ?? templates.find((template) => template.file.path === wanted2 || template.file.path.endsWith(`/${wanted2}`))?.topPath;
189769
+ if (top === void 0) {
189770
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, START, `options.root names ${cleanText(wanted2)}, which is not a stack of the app; every stack is drawn.`, `The stacks sent are: ${tops.join(", ")}.`);
189771
+ return templates;
189772
+ }
189773
+ return templates.filter((template) => template.topPath === top);
189774
+ }
189775
+ function readCdk(joined2, context) {
189776
+ const index2 = new LineIndex(joined2);
189777
+ const files = context.files.map((file2, position) => ({ path: file2.path, start: file2.start, text: joined2.slice(file2.start, context.files[position + 1]?.start ?? joined2.length) }));
189778
+ const assembly = readAssembly(files);
189779
+ for (const problem of assembly.problems) {
189780
+ const lineStart = problem.file.text.split("\n").slice(0, problem.line - 1).join("\n").length + (problem.line > 1 ? 1 : 0);
189781
+ const span = index2.span(problem.file.start + lineStart + problem.column - 1);
189782
+ if (files.length === 1) {
189783
+ context.report.add(DIAGNOSTIC_CODES.importUnreadable, span, problem.message, problem.hint);
189784
+ return null;
189785
+ }
189786
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, span, `${cleanText(problem.file.path)} is not drawn: ${problem.message}`, problem.hint);
189787
+ }
189788
+ if (assembly.unknown.length > 0) {
189789
+ const first = assembly.unknown[0];
189790
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, index2.span(first.start), `${countOf2(assembly.unknown.length, "file")} of the folder ${assembly.unknown.length === 1 ? "is" : "are"} not part of a cloud assembly and ${assembly.unknown.length === 1 ? "is" : "are"} not drawn: ${assembly.unknown.slice(0, LISTED9).map((file2) => cleanText(file2.path, "(no name)")).join(", ")}.`, "Send the cdk.out folder that cdk synth writes: manifest.json, tree.json, and the templates.");
189791
+ }
189792
+ for (const missing of assembly.missing.slice(0, 1)) {
189793
+ const names8 = assembly.missing.map((entry2) => entry2.stack);
189794
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, index2.span(missing.start), `The manifest names ${countOf2(names8.length, "stack")} whose template was not sent, so ${names8.length === 1 ? "it is" : "they are"} not drawn: ${names8.slice(0, LISTED9).map((name2) => cleanText(name2)).join(", ")}.`, `Send ${cleanText(missing.file)} with the other files of cdk.out.`);
189795
+ }
189796
+ if (assembly.templates.length === 0) {
189797
+ context.report.add(DIAGNOSTIC_CODES.importNoShape, START, "The files hold no CloudFormation template of a CDK app.", "Run cdk synth and send the cdk.out folder it writes, or the template it prints.");
189798
+ return null;
189799
+ }
189800
+ const templates = chosenStacks(assembly.templates, context);
189801
+ const tops = [...new Set(templates.map((template) => template.topPath))];
189802
+ const roots = /* @__PURE__ */ new Map();
189803
+ for (const template of templates) roots.set(template.stackPath, template.parent === null ? "Stack" : "Nested stack");
189804
+ const reads = [];
189805
+ const byFile = /* @__PURE__ */ new Map();
189806
+ const allPaths = [];
189807
+ const nestedTemplates = /* @__PURE__ */ new Map();
189808
+ for (const template of templates) if (template.parent !== null) nestedTemplates.set(`${template.parent.template.file.path}\0${template.parent.logical}`, template);
189809
+ const usedNames = /* @__PURE__ */ new Set();
189810
+ const unsent = [];
189811
+ for (const template of templates) {
189812
+ const paths = cdkPaths(template.root);
189813
+ const names8 = /* @__PURE__ */ new Map();
189814
+ const frames = /* @__PURE__ */ new Set();
189815
+ const resources = get(template.root, "Resources");
189816
+ for (const entry2 of resources?.kind === "map" ? resources.entries : []) {
189817
+ const type = text(get(entry2.value, "Type"));
189818
+ if (type === METADATA_TYPE) continue;
189819
+ if (type === "AWS::CloudFormation::Stack" && nestedTemplates.has(`${template.file.path}\0${entry2.key}`)) {
189820
+ frames.add(entry2.key);
189821
+ continue;
189822
+ }
189823
+ const path = paths.get(entry2.key) ?? template.manifestPaths.get(entry2.key) ?? `${template.stackPath}/${withoutHash(entry2.key)}`;
189824
+ const asset = type === "AWS::CloudFormation::Stack" ? text(get(get(entry2.value, "Metadata"), "aws:asset:path")) : null;
189825
+ if (asset !== null && /\.nested\.template\.json$/i.test(asset)) unsent.push({ path: nestedStackPath(path), file: asset, start: template.file.start + Math.max(0, template.file.text.indexOf(asset)) });
189826
+ let name2 = constructOf(path);
189827
+ if (usedNames.has(name2)) name2 = path;
189828
+ for (let count3 = 2; usedNames.has(name2); count3 += 1) name2 = `${path}_${count3}`;
189829
+ usedNames.add(name2);
189830
+ names8.set(entry2.key, name2);
189831
+ allPaths.push(path);
189832
+ }
189833
+ const read = { template, model: new Template(template.root, null), names: names8, frames };
189834
+ reads.push(read);
189835
+ byFile.set(template, read);
189836
+ }
189837
+ const firstUnsent = unsent[0];
189838
+ if (firstUnsent !== void 0) {
189839
+ context.report.add(DIAGNOSTIC_CODES.importUnresolved, index2.span(firstUnsent.start), `${countOf2(unsent.length, "nested stack")} ${unsent.length === 1 ? "is" : "are"} drawn as one box, because ${unsent.length === 1 ? "its template was" : "their templates were"} not sent: ${unsent.slice(0, LISTED9).map((entry2) => cleanText(entry2.path)).join(", ")}.`, `Send ${cleanText(firstUnsent.file)} with the other files of cdk.out to draw what it makes.`);
189840
+ }
189841
+ const exports = /* @__PURE__ */ new Map();
189842
+ for (const read of reads) {
189843
+ const outputs = get(read.template.root, "Outputs");
189844
+ for (const entry2 of outputs?.kind === "map" ? outputs.entries : []) {
189845
+ const name2 = textOf5(read.model.evaluate(get(get(entry2.value, "Export"), "Name")));
189846
+ const value = get(entry2.value, "Value");
189847
+ if (name2 !== null && value !== void 0) exports.set(name2, { read, value });
189848
+ }
189849
+ }
189850
+ let hops = 0;
189851
+ const refsOf = (read, node3) => {
189852
+ if (node3 === void 0 || hops >= MAX_HOPS) return [];
189853
+ hops += 1;
189854
+ try {
189855
+ return read.model.refs(node3).map((ref) => read.names.get(ref) ?? ref);
189856
+ } finally {
189857
+ hops -= 1;
189858
+ }
189859
+ };
189860
+ for (const read of reads) {
189861
+ const parent = read.template.parent === null ? void 0 : byFile.get(read.template.parent.template);
189862
+ const declaration = parent === void 0 || read.template.parent === null ? void 0 : get(get(parent.template.root, "Resources"), read.template.parent.logical);
189863
+ const model = new Template(read.template.root, {
189864
+ parameter: (name2) => {
189865
+ const passed = get(get(declaration, "Properties"), "Parameters");
189866
+ const value = get(passed, name2);
189867
+ return parent === void 0 || value === void 0 ? null : refsOf(parent, value);
189868
+ },
189869
+ attribute: (resource, attribute2) => {
189870
+ if (!read.frames.has(resource) || !attribute2.startsWith("Outputs.")) return null;
189871
+ const nested2 = nestedTemplates.get(`${read.template.file.path}\0${resource}`);
189872
+ const inner = nested2 === void 0 ? void 0 : byFile.get(nested2);
189873
+ return inner === void 0 ? [] : refsOf(inner, get(get(get(inner.template.root, "Outputs"), attribute2.slice("Outputs.".length)), "Value"));
189874
+ },
189875
+ imported: (name2) => {
189876
+ const exported = exports.get(name2);
189877
+ return exported === void 0 ? null : refsOf(exported.read, exported.value);
189878
+ }
189879
+ });
189880
+ read.model = model;
189881
+ }
189882
+ const tree = new ConstructTree(allPaths, roots);
189883
+ const framed = tops.length > 1;
189884
+ const items2 = [];
189885
+ const empty = [];
189886
+ const placements = /* @__PURE__ */ new Map();
189887
+ const makers = /* @__PURE__ */ new Map();
189888
+ for (const read of reads) {
189889
+ const offsetOf = (logical) => {
189890
+ const at3 = read.template.file.text.search(new RegExp(`^\\s*"?${logical.replace(/[^A-Za-z0-9]/g, "")}"?\\s*:`, "m"));
189891
+ return read.template.file.start + Math.max(0, at3);
189892
+ };
189893
+ const listed2 = templateItems(read.model, offsetOf);
189894
+ empty.push(...listed2.empty.map((logical) => read.names.get(logical) ?? logical));
189895
+ for (const item of listed2.items) {
189896
+ const name2 = read.names.get(item.name);
189897
+ if (name2 === void 0) continue;
189898
+ const path = cdkPaths(read.template.root).get(item.name) ?? read.template.manifestPaths.get(item.name) ?? name2;
189899
+ const place2 = tree.place(path, framed);
189900
+ placements.set(name2, place2.frames);
189901
+ makers.set(name2, place2.maker);
189902
+ items2.push({ ...item, name: name2, label: place2.label, attributes: item.attributes.flatMap((attribute2) => attributeOf2(attribute2, item.name, read.names)) });
189903
+ }
189904
+ }
189905
+ if (empty.length > 0) {
189906
+ context.report.add(DIAGNOSTIC_CODES.importSkipped, START, `${countOf2(empty.length, "resource")} ${empty.length === 1 ? "has a Condition" : "have Conditions"} that the parameters' defaults make false, so ${empty.length === 1 ? "it is" : "they are"} not drawn: ${empty.slice(0, LISTED9).join(", ")}.`, "Give the parameter its value as a default, or synthesize with that value, to draw it.");
189907
+ }
189908
+ if (items2.length === 0) {
189909
+ context.report.add(DIAGNOSTIC_CODES.importNoShape, START, "The templates declare no resource that is drawn.", "Send the cdk.out of an app whose stacks make something.");
189910
+ return null;
189911
+ }
189912
+ const labelCount = /* @__PURE__ */ new Map();
189913
+ for (const item of items2) labelCount.set(item.label, (labelCount.get(item.label) ?? 0) + 1);
189914
+ for (const [position, item] of items2.entries()) {
189915
+ const maker = makers.get(item.name) ?? null;
189916
+ if ((labelCount.get(item.label) ?? 0) > 1 && maker !== null && !item.label.startsWith(`${maker}_`)) items2[position] = { ...item, label: `${maker}_${item.label}` };
189917
+ }
189918
+ const startOf3 = (path) => {
189919
+ const owner = reads.find((read) => read.template.stackPath === path) ?? reads.find((read) => read.template.topPath === path.split("/")[0]);
189920
+ const at3 = owner === void 0 ? -1 : owner.template.file.text.indexOf(`"${path}/`);
189921
+ return owner === void 0 ? 0 : owner.template.file.start + Math.max(0, at3);
189922
+ };
189923
+ const { modules, scopes } = frameModules2(tree, assembly.classes, startOf3);
189924
+ const scoped = items2.map((item) => {
189925
+ const frames = placements.get(item.name) ?? [];
189926
+ const innermost = frames[frames.length - 1];
189927
+ return innermost === void 0 ? item : { ...item, scope: scopes.get(innermost) ?? "" };
189928
+ });
189929
+ const stack = itemsToStack(scoped, modules);
189930
+ const byName = new Map(scoped.map((item) => [item.name, item]));
189931
+ const stableName = (key) => {
189932
+ const named = stack.stableName(key);
189933
+ return named.startsWith("module:") ? named.slice("module:".length) : named;
189934
+ };
189935
+ const only = tops.length === 1 ? tops[0] : void 0;
189936
+ return drawStack({
189937
+ resources: stack.resources,
189938
+ modules: stack.modules,
189939
+ providers: /* @__PURE__ */ new Map([["aws", null]]),
189940
+ cloud: "aws",
189941
+ variableName: (name2) => name2,
189942
+ variableValue: () => null,
189943
+ context: context.title === null && only !== void 0 ? { ...context, title: `${cleanText(only)} on AWS` } : context,
189944
+ span: (key) => {
189945
+ const item = byName.get(stack.stableName(key));
189946
+ return item === void 0 ? { line: 1, column: 1, length: 1 } : index2.span(item.start);
189947
+ },
189948
+ tool: "the AWS CDK",
189949
+ stableName,
189950
+ framing: "construct"
189951
+ });
189952
+ }
189953
+ var LISTED9, MAX_HOPS, METADATA_TYPE, withoutHash, cdkImporter;
189954
+ var init_cdk = __esm({
189955
+ "../../packages/diagrams/src/importers/cdk/index.ts"() {
189956
+ "use strict";
189957
+ init_core();
189958
+ init_template();
189959
+ init_items2();
189960
+ init_value();
189961
+ init_shared2();
189962
+ init_yaml();
189963
+ init_terraform();
189964
+ init_assembly();
189965
+ init_constructs();
189966
+ LISTED9 = 3;
189967
+ MAX_HOPS = 8;
189968
+ METADATA_TYPE = "AWS::CDK::Metadata";
189969
+ withoutHash = (logical) => logical.replace(/[0-9A-F]{8}$/, "") || logical;
189970
+ cdkImporter = { format: "cdk", kinds: ["flow"], joins: "\n", extensions: [".json", ".yaml", ".yml", ".out"], read: readCdk };
189971
+ }
189972
+ });
189973
+
188636
189974
  // ../../packages/diagrams/src/importers/compose/interpolate.ts
188637
189975
  function interpolate(value, depth = 0) {
188638
189976
  if (!value.includes("$")) return value;
@@ -188763,7 +190101,7 @@ function readServices(root) {
188763
190101
  function declared(root, key) {
188764
190102
  return names6(get(root, key));
188765
190103
  }
188766
- function namesHost(value, host) {
190104
+ function namesHost2(value, host) {
188767
190105
  if (host === "") return false;
188768
190106
  for (let index2 = value.indexOf(host); index2 !== -1; index2 = value.indexOf(host, index2 + 1)) {
188769
190107
  const before = index2 === 0 ? "" : value.charAt(index2 - 1);
@@ -188788,7 +190126,7 @@ function subOf(service) {
188788
190126
  const parts = [runs, ports, replicas].filter((part) => part !== null);
188789
190127
  return parts.length === 0 ? null : parts.join(", ");
188790
190128
  }
188791
- function nodeOf2(service, group2) {
190129
+ function nodeOf3(service, group2) {
188792
190130
  const look = (service.image === null ? null : imageLook(service.image)) ?? imageLook(service.name);
188793
190131
  const type = look?.type ?? "service";
188794
190132
  return { key: `service:${service.name}`, type, label: service.name, sub: subOf(service), icon: look?.icon ?? iconForName(service.name, type), group: group2, idHint: service.name };
@@ -188813,14 +190151,14 @@ function groupsOf(services, networks) {
188813
190151
  const used = networks.filter((network) => [...home.values()].includes(`network:${network}`));
188814
190152
  return { groups: used.map((network) => ({ key: `network:${network}`, label: network, type: "network", idHint: `${network}_net` })), home };
188815
190153
  }
188816
- function edgesOf(services, volumes) {
190154
+ function edgesOf2(services, volumes) {
188817
190155
  const known3 = new Set(services.map((service) => service.name));
188818
190156
  const edges = [];
188819
190157
  for (const service of services) {
188820
190158
  if (service.sharesNetworkOf !== null && known3.has(service.sharesNetworkOf)) edges.push({ from: `service:${service.name}`, to: `service:${service.sharesNetworkOf}`, label: "shares network" });
188821
190159
  for (const target of service.dependsOn) if (known3.has(target)) edges.push({ from: `service:${service.name}`, to: `service:${target}` });
188822
190160
  for (const other of services) {
188823
- if (other.name !== service.name && service.environment.some((value) => namesHost(value, other.name))) edges.push({ from: `service:${service.name}`, to: `service:${other.name}` });
190161
+ if (other.name !== service.name && service.environment.some((value) => namesHost2(value, other.name))) edges.push({ from: `service:${service.name}`, to: `service:${other.name}` });
188824
190162
  }
188825
190163
  for (const volume of service.volumes) if (volumes.has(volume)) edges.push({ from: `service:${service.name}`, to: `volume:${volume}`, label: "mounts" });
188826
190164
  }
@@ -188849,7 +190187,7 @@ function readCompose(source, context) {
188849
190187
  const mounted = new Set(services.flatMap((service) => service.volumes));
188850
190188
  const volumes = declared(root, "volumes").filter((volume) => mounted.has(volume));
188851
190189
  const nodes = [
188852
- ...services.map((service) => nodeOf2(service, home.get(service.name) ?? null)),
190190
+ ...services.map((service) => nodeOf3(service, home.get(service.name) ?? null)),
188853
190191
  ...volumes.map((volume) => ({ key: `volume:${volume}`, type: "storage", label: volume, sub: "Named volume", idHint: `${volume}_volume` }))
188854
190192
  ];
188855
190193
  const name2 = text(get(root, "name"));
@@ -188862,7 +190200,7 @@ function readCompose(source, context) {
188862
190200
  direction: "down",
188863
190201
  nodes,
188864
190202
  groups,
188865
- edges: edgesOf(services, new Set(volumes)),
190203
+ edges: edgesOf2(services, new Set(volumes)),
188866
190204
  noun: "services",
188867
190205
  capSpan: (key) => {
188868
190206
  const service = services.find((candidate2) => `service:${candidate2.name}` === key);
@@ -190515,6 +191853,79 @@ var init_git_log = __esm({
190515
191853
  }
190516
191854
  });
190517
191855
 
191856
+ // ../../packages/diagrams/src/importers/helm/index.ts
191857
+ function chartOf(label) {
191858
+ const match = /^(.+?)-(v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]*)?)$/.exec(label);
191859
+ return match === null ? { name: label, version: null } : { name: match[1] ?? label, version: match[2] ?? null };
191860
+ }
191861
+ function chartLabel(manifest) {
191862
+ const written3 = manifest.meta.get("helm.sh/chart") ?? manifest.meta.get("chart");
191863
+ if (written3 !== void 0 && written3 !== "") return chartOf(written3);
191864
+ const name2 = manifest.meta.get("app.kubernetes.io/name");
191865
+ return name2 === void 0 || name2 === "" ? null : { name: name2, version: null };
191866
+ }
191867
+ function helmFrames(manifests) {
191868
+ const charts = /* @__PURE__ */ new Map();
191869
+ for (const manifest of manifests) {
191870
+ const release = releaseOf(manifest);
191871
+ const chart = chartLabel(manifest);
191872
+ if (release !== null && chart !== null) charts.set(release, /* @__PURE__ */ new Set([...charts.get(release) ?? [], chart.name]));
191873
+ }
191874
+ return (manifest) => {
191875
+ const frames = [];
191876
+ const release = releaseOf(manifest);
191877
+ const chart = chartLabel(manifest);
191878
+ const partOf2 = manifest.meta.get("app.kubernetes.io/part-of");
191879
+ if (partOf2 !== void 0 && partOf2 !== "" && partOf2 !== release) frames.push({ key: `part-of:${partOf2}`, label: partOf2, sub: "Application", icon: "lucide:boxes", idHint: partOf2 });
191880
+ if (release === null) return frames;
191881
+ const several = (charts.get(release)?.size ?? 0) > 1;
191882
+ const only = several || chart === null ? null : chart;
191883
+ frames.push({ key: `release:${release}`, label: release, sub: cleanText(only === null ? "Release" : `Release of ${only.name}${only.version === null ? "" : ` ${only.version}`}`), icon: "simple:helm", idHint: release });
191884
+ if (several && chart !== null) frames.push({ key: `chart:${release}/${chart.name}`, label: chart.name, sub: cleanText(`Chart${chart.version === null ? "" : ` ${chart.version}`}`), icon: "lucide:package", idHint: chart.name });
191885
+ return frames;
191886
+ };
191887
+ }
191888
+ function readHelm(joined2, context) {
191889
+ const index2 = new LineIndex(joined2);
191890
+ const files = context.files.map((file2, position) => ({ path: file2.path, start: file2.start, text: joined2.slice(file2.start, context.files[position + 1]?.start ?? joined2.length) }));
191891
+ if (files.some((file2) => /(?:^|\/)Chart\.yaml$/.test(file2.path) || file2.path === "" && isChartFile(file2.text))) return drawCharts(files, context, index2);
191892
+ const read = parseYaml(joined2);
191893
+ if (read.error !== null) {
191894
+ context.report.add(DIAGNOSTIC_CODES.importUnreadable, spanAt3(read.error.line, read.error.column), read.error.message, `${read.error.hint} Send the output of helm template, which renders the chart's templates first.`);
191895
+ return null;
191896
+ }
191897
+ const documents = read.documents.map((document2) => document2.root).filter((root) => root !== null);
191898
+ const releases = /* @__PURE__ */ new Set();
191899
+ const labelled2 = [];
191900
+ for (const root of manifestRoots(documents)) {
191901
+ const manifest = readManifest(root);
191902
+ if (manifest === null) continue;
191903
+ labelled2.push(manifest);
191904
+ const release = releaseOf(manifest);
191905
+ if (release !== null) releases.add(release);
191906
+ }
191907
+ const [only] = releases;
191908
+ return drawManifests(documents, context, {
191909
+ framesOf: helmFrames(labelled2),
191910
+ title: releases.size === 1 && only !== void 0 ? `Helm release ${cleanText(only)}` : "Helm releases",
191911
+ source: "a Helm chart rendered with helm template"
191912
+ });
191913
+ }
191914
+ var releaseOf, helmImporter;
191915
+ var init_helm = __esm({
191916
+ "../../packages/diagrams/src/importers/helm/index.ts"() {
191917
+ "use strict";
191918
+ init_core();
191919
+ init_kubernetes();
191920
+ init_manifests();
191921
+ init_shared2();
191922
+ init_yaml();
191923
+ init_chart();
191924
+ releaseOf = (manifest) => manifest.meta.get("app.kubernetes.io/instance") ?? manifest.meta.get("release") ?? null;
191925
+ helmImporter = { format: "helm", kinds: ["flow"], joins: "\n---\n", extensions: [".yaml", ".yml", ".tpl", ".txt", ".lock"], read: readHelm };
191926
+ }
191927
+ });
191928
+
190518
191929
  // ../../packages/diagrams/src/importers/github-actions/workflow.ts
190519
191930
  function humanize2(id) {
190520
191931
  const words2 = id.replace(/[_-]+/g, " ").trim();
@@ -190536,7 +191947,7 @@ function commandLabel(run3) {
190536
191947
  const space = cut.lastIndexOf(" ");
190537
191948
  return space > 12 ? cut.slice(0, space) : cut;
190538
191949
  }
190539
- function readable(name2) {
191950
+ function readable2(name2) {
190540
191951
  let out = "";
190541
191952
  let at3 = 0;
190542
191953
  for (let open2 = name2.indexOf("${{", at3); open2 !== -1; open2 = name2.indexOf("${{", at3)) {
@@ -190556,7 +191967,7 @@ function readStep2(step) {
190556
191967
  const command = run3 === null ? null : commandLabel(run3).split(" ")[0] ?? "";
190557
191968
  const icon = (action === null ? null : ACTION_ICONS[action] ?? (action.startsWith("docker/") ? "simple:docker" : null)) ?? (command === null ? null : COMMAND_ICONS[command] ?? null);
190558
191969
  const label = name2 ?? (uses !== null ? actionLabel(uses) : run3 !== null ? commandLabel(run3) : "Step");
190559
- return { label: cleanText(readable(label), "Step"), icon, line: step.line, column: step.column };
191970
+ return { label: cleanText(readable2(label), "Step"), icon, line: step.line, column: step.column };
190560
191971
  }
190561
191972
  function jobDetail(job) {
190562
191973
  const workflow = text(get(job, "uses"));
@@ -190572,7 +191983,7 @@ function jobDetail(job) {
190572
191983
  function readJobs(root) {
190573
191984
  return entries(get(root, "jobs")).map((entry2) => ({
190574
191985
  id: entry2.key,
190575
- label: cleanText(readable(text(get(entry2.value, "name")) ?? humanize2(entry2.key)), entry2.key),
191986
+ label: cleanText(readable2(text(get(entry2.value, "name")) ?? humanize2(entry2.key)), entry2.key),
190576
191987
  needs: names5(get(entry2.value, "needs")),
190577
191988
  detail: jobDetail(entry2.value),
190578
191989
  steps: items(get(entry2.value, "steps")).filter((step) => step.kind === "map").map(readStep2),
@@ -190961,298 +192372,46 @@ var init_json = __esm({
190961
192372
  }
190962
192373
  });
190963
192374
 
190964
- // ../../packages/diagrams/src/importers/kubernetes/manifests.ts
190965
- function valuesOf(kind, root, podSpec) {
190966
- const raw = kind === "ConfigMap" ? entries(get(root, "data")).map((entry2) => text(entry2.value)) : items(get(podSpec, "containers")).flatMap((container) => items(get(container, "env")).map((variable) => text(get(variable, "value"))));
190967
- return raw.filter((value) => value !== null && value.length <= MAX_VALUE_LENGTH2);
190968
- }
190969
- function stringMap(node3) {
190970
- const out = /* @__PURE__ */ new Map();
190971
- if (node3?.kind === "map") for (const entry2 of node3.entries) out.set(entry2.key, text(entry2.value) ?? "");
190972
- return out;
190973
- }
190974
- function podUses(spec) {
190975
- const uses = [];
190976
- const add2 = (kind, name2) => {
190977
- if (name2 !== null && name2 !== "" && !uses.some((use) => use.kind === kind && use.name === name2)) uses.push({ kind, name: name2 });
190978
- };
190979
- for (const container of [...items(get(spec, "initContainers")), ...items(get(spec, "containers"))]) {
190980
- for (const source of items(get(container, "envFrom"))) {
190981
- add2("ConfigMap", text(at2(source, ["configMapRef", "name"])));
190982
- add2("Secret", text(at2(source, ["secretRef", "name"])));
190983
- }
190984
- for (const variable of items(get(container, "env"))) {
190985
- add2("ConfigMap", text(at2(variable, ["valueFrom", "configMapKeyRef", "name"])));
190986
- add2("Secret", text(at2(variable, ["valueFrom", "secretKeyRef", "name"])));
190987
- }
190988
- }
190989
- for (const volume of items(get(spec, "volumes"))) {
190990
- add2("ConfigMap", text(at2(volume, ["configMap", "name"])));
190991
- add2("Secret", text(at2(volume, ["secret", "secretName"])));
190992
- add2("PersistentVolumeClaim", text(at2(volume, ["persistentVolumeClaim", "claimName"])));
190993
- }
190994
- return uses;
190995
- }
190996
- function ingressRoutes(spec) {
190997
- const routes = [];
190998
- const backend = (node3) => text(at2(node3, ["service", "name"])) ?? text(get(node3, "serviceName"));
190999
- const add2 = (service, label) => {
191000
- if (service !== null && !routes.some((route) => route.service === service)) routes.push({ service, label });
191001
- };
191002
- add2(backend(get(spec, "defaultBackend") ?? get(spec, "backend")), null);
191003
- for (const rule of items(get(spec, "rules"))) {
191004
- for (const path of items(at2(rule, ["http", "paths"]))) {
191005
- const route = text(get(path, "path"));
191006
- add2(backend(get(path, "backend")), route === null || route === "/" ? text(get(rule, "host")) : route);
191007
- }
191008
- }
191009
- return routes;
191010
- }
191011
- function detailOf(kind, spec) {
191012
- if (kind === "Service") {
191013
- const ports = items(get(spec, "ports")).map((port) => text(get(port, "port"))).filter((port) => port !== null);
191014
- return [text(get(spec, "type")) ?? "ClusterIP", ports.length === 0 ? "" : `port ${ports.slice(0, 3).join(", ")}`].filter((part) => part !== "").join(", ");
191015
- }
191016
- if (kind === "Ingress") return text(get(items(get(spec, "rules"))[0], "host")) ?? names5(get(items(get(spec, "tls"))[0], "hosts"))[0] ?? null;
191017
- if (kind === "PersistentVolumeClaim") return text(at2(spec, ["resources", "requests", "storage"]));
191018
- return null;
191019
- }
191020
- function readManifest(root) {
191021
- const kind = text(get(root, "kind"));
191022
- const name2 = text(at2(root, ["metadata", "name"]));
191023
- if (kind === null || name2 === null) return null;
191024
- const spec = get(root, "spec");
191025
- const templatePath = WORKLOAD_TEMPLATES[kind];
191026
- const template = templatePath === void 0 ? kind === "Pod" ? root : void 0 : at2(root, templatePath);
191027
- const podSpec = get(template, "spec");
191028
- const firstContainer = items(get(podSpec, "containers"))[0];
191029
- return {
191030
- kind,
191031
- name: name2,
191032
- namespace: text(at2(root, ["metadata", "namespace"])) ?? "default",
191033
- line: root.line,
191034
- column: root.column,
191035
- labels: template !== void 0 ? stringMap(at2(template, ["metadata", "labels"])) : kind === "Service" ? stringMap(get(spec, "selector")) : /* @__PURE__ */ new Map(),
191036
- image: text(get(firstContainer, "image")),
191037
- replicas: text(get(spec, "replicas")),
191038
- uses: podSpec === void 0 ? [] : podUses(podSpec),
191039
- routes: kind === "Ingress" ? ingressRoutes(spec) : [],
191040
- detail: detailOf(kind, spec),
191041
- values: valuesOf(kind, root, podSpec)
191042
- };
191043
- }
191044
- function manifestRoots(roots) {
191045
- const out = [];
191046
- for (const root of roots) {
191047
- if (text(get(root, "kind")) === "List") out.push(...items(get(root, "items")));
191048
- else out.push(root);
191049
- }
191050
- return out;
191051
- }
191052
- var WORKLOAD_TEMPLATES, DRAWN_KINDS, QUIET_KINDS, MAX_VALUE_LENGTH2, at2;
191053
- var init_manifests = __esm({
191054
- "../../packages/diagrams/src/importers/kubernetes/manifests.ts"() {
191055
- "use strict";
191056
- init_yaml();
191057
- WORKLOAD_TEMPLATES = {
191058
- Deployment: ["spec", "template"],
191059
- StatefulSet: ["spec", "template"],
191060
- DaemonSet: ["spec", "template"],
191061
- ReplicaSet: ["spec", "template"],
191062
- Job: ["spec", "template"],
191063
- CronJob: ["spec", "jobTemplate", "spec", "template"]
191064
- };
191065
- DRAWN_KINDS = /* @__PURE__ */ new Set([...Object.keys(WORKLOAD_TEMPLATES), "Pod", "Service", "Ingress", "ConfigMap", "Secret", "PersistentVolumeClaim"]);
191066
- QUIET_KINDS = /* @__PURE__ */ new Set([
191067
- "Namespace",
191068
- "ServiceAccount",
191069
- "Role",
191070
- "ClusterRole",
191071
- "RoleBinding",
191072
- "ClusterRoleBinding",
191073
- "HorizontalPodAutoscaler",
191074
- "PodDisruptionBudget",
191075
- "NetworkPolicy",
191076
- "LimitRange",
191077
- "ResourceQuota",
191078
- "PriorityClass",
191079
- "ServiceMonitor",
191080
- "PodMonitor",
191081
- "VerticalPodAutoscaler",
191082
- "Certificate",
191083
- "IngressClass",
191084
- "StorageClass"
191085
- ]);
191086
- MAX_VALUE_LENGTH2 = 500;
191087
- at2 = (node3, path) => path.reduce((current, key) => get(current, key), node3);
191088
- }
191089
- });
191090
-
191091
- // ../../packages/diagrams/src/importers/kubernetes/index.ts
191092
- function namespaceType(name2) {
191093
- const phrase2 = name2.toLowerCase().replace(/[-_]+/g, " ").trim();
191094
- const matches3 = GROUP_TYPES.filter((type) => GROUP_TYPE_SPECS[type].phrases.includes(phrase2));
191095
- return matches3.length === 1 ? matches3[0] : null;
191096
- }
191097
- function nodeOf3(manifest, grouped, serviceDetail) {
191098
- const look = LOOKS[manifest.kind] ?? { type: "service", icon: null, rank: 2 };
191099
- const image = manifest.image === null ? null : imageLook(manifest.image);
191100
- const replicas = manifest.replicas !== null && manifest.replicas !== "1" ? `, ${manifest.replicas} replicas` : "";
191101
- const detail = (serviceDetail ?? manifest.detail) === null ? "" : `, ${serviceDetail ?? manifest.detail}`;
191102
- return {
191103
- key: keyOf3(manifest.kind, manifest.namespace, manifest.name),
191104
- type: image?.type ?? look.type,
191105
- label: manifest.name,
191106
- sub: `${manifest.kind}${replicas}${detail}`,
191107
- // A workload whose image this reader does not know may still be named after a product, such as kafka; lint's own rule finds it.
191108
- icon: image?.icon ?? look.icon ?? iconForName(manifest.name, image?.type ?? look.type),
191109
- group: grouped ? `ns:${manifest.namespace}` : null,
191110
- idHint: manifest.name
191111
- };
191112
- }
191113
- function namesHost2(value, host) {
191114
- if (host === "") return false;
191115
- for (let index2 = value.indexOf(host); index2 !== -1; index2 = value.indexOf(host, index2 + 1)) {
191116
- const before = index2 === 0 ? "" : value.charAt(index2 - 1);
191117
- const after = value.charAt(index2 + host.length);
191118
- if (!/[A-Za-z0-9.-]/.test(before) && !/[A-Za-z0-9_-]/.test(after)) return true;
191119
- }
191120
- return false;
191121
- }
191122
- function foldedServices(drawn) {
191123
- const folded = /* @__PURE__ */ new Map();
191124
- for (const service of drawn) {
191125
- if (service.kind !== "Service") continue;
191126
- const workloads = selected(service, drawn);
191127
- const only = workloads[0];
191128
- if (workloads.length === 1 && only !== void 0) folded.set(keyOf3("Service", service.namespace, service.name), keyOf3(only.kind, only.namespace, only.name));
191129
- }
191130
- return folded;
191131
- }
191132
- function edgesOf2(drawn, folded, context) {
191133
- const edges = [];
191134
- const has = (kind, namespace, name2) => drawn.some((other) => other.kind === kind && other.namespace === namespace && other.name === name2);
191135
- const unresolved = (manifest, what) => {
191136
- context.report.add(DIAGNOSTIC_CODES.importUnresolved, spanAt3(manifest.line, manifest.column), `${manifest.kind} ${manifest.name} names the ${what}, which is not in the text.`, "Include its manifest, or leave it out if it is created elsewhere; the arrow was not drawn.");
191137
- };
191138
- for (const manifest of drawn) {
191139
- const from = keyOf3(manifest.kind, manifest.namespace, manifest.name);
191140
- for (const route of manifest.routes) {
191141
- const service = keyOf3("Service", manifest.namespace, route.service);
191142
- if (has("Service", manifest.namespace, route.service)) edges.push({ from, to: folded.get(service) ?? service, label: route.label });
191143
- else unresolved(manifest, `Service ${route.service}`);
191144
- }
191145
- if (manifest.kind === "Service" && !folded.has(from)) {
191146
- for (const workload of selected(manifest, drawn)) edges.push({ from, to: keyOf3(workload.kind, workload.namespace, workload.name) });
191147
- }
191148
- if (manifest.image !== null) {
191149
- const read = drawn.filter((other) => other.kind === "ConfigMap" && other.namespace === manifest.namespace && manifest.uses.some((use) => use.kind === "ConfigMap" && use.name === other.name));
191150
- const values = [...manifest.values, ...read.flatMap((config2) => config2.values)];
191151
- for (const service of drawn) {
191152
- const host = service.namespace === manifest.namespace ? service.name : `${service.name}.${service.namespace}`;
191153
- if (service.kind !== "Service" || !values.some((value) => namesHost2(value, host))) continue;
191154
- const key = keyOf3("Service", service.namespace, service.name);
191155
- const to = folded.get(key) ?? key;
191156
- if (to !== from) edges.push({ from, to });
191157
- }
191158
- }
191159
- for (const use of manifest.uses) {
191160
- if (has(use.kind, manifest.namespace, use.name)) edges.push({ from, to: keyOf3(use.kind, manifest.namespace, use.name), arrow: use.kind === "PersistentVolumeClaim" ? "->" : "-->", label: use.kind === "PersistentVolumeClaim" ? "mounts" : "reads" });
191161
- else unresolved(manifest, `${use.kind} ${use.name}`);
191162
- }
191163
- }
191164
- return edges;
191165
- }
191166
- function readKubernetes(textIn, context) {
191167
- const read = parseYaml(textIn);
192375
+ // ../../packages/diagrams/src/importers/kustomize/index.ts
192376
+ function withoutHashes(documents) {
192377
+ const hashed = /* @__PURE__ */ new Map();
192378
+ for (const root of documents) {
192379
+ const manifest = readManifest(root);
192380
+ if (manifest !== null && (manifest.kind === "ConfigMap" || manifest.kind === "Secret") && GENERATED_HASH.test(manifest.name)) hashed.set(manifest.name, manifest.name.replace(GENERATED_HASH, ""));
192381
+ }
192382
+ if (hashed.size === 0) return [...documents];
192383
+ const strip = (node3, depth) => {
192384
+ if (depth > 64) return node3;
192385
+ if (node3.kind === "scalar") return hashed.has(node3.value) ? { ...node3, value: hashed.get(node3.value) ?? node3.value } : node3;
192386
+ if (node3.kind === "seq") return { ...node3, items: node3.items.map((item) => strip(item, depth + 1)) };
192387
+ return { ...node3, entries: node3.entries.map((entry2) => ({ ...entry2, value: strip(entry2.value, depth + 1) })) };
192388
+ };
192389
+ return documents.map((root) => strip(root, 0));
192390
+ }
192391
+ function readKustomize(joined2, context) {
192392
+ const index2 = new LineIndex(joined2);
192393
+ const files = context.files.map((file2, position) => ({ path: file2.path, start: file2.start, text: joined2.slice(file2.start, context.files[position + 1]?.start ?? joined2.length) }));
192394
+ if (files.some((file2) => KUSTOMIZATION_NAMES.has(file2.path.slice(file2.path.lastIndexOf("/") + 1)) || file2.path === "" && isKustomization(file2.text))) return drawFolder(files, context, index2);
192395
+ const read = parseYaml(joined2);
191168
192396
  if (read.error !== null) {
191169
192397
  context.report.add(DIAGNOSTIC_CODES.importUnreadable, spanAt3(read.error.line, read.error.column), read.error.message, read.error.hint);
191170
192398
  return null;
191171
192399
  }
191172
- const roots = manifestRoots(read.documents.map((document2) => document2.root).filter((root) => root !== null));
191173
- const drawn = [];
191174
- const unknown2 = [];
191175
- for (const root of roots) {
191176
- const manifest = readManifest(root);
191177
- if (manifest === null || QUIET_KINDS.has(manifest.kind)) continue;
191178
- if (DRAWN_KINDS.has(manifest.kind)) {
191179
- if (!drawn.some((other) => other.kind === manifest.kind && other.namespace === manifest.namespace && other.name === manifest.name)) drawn.push(manifest);
191180
- } else unknown2.push(manifest);
191181
- }
191182
- const firstUnknown = unknown2[0];
191183
- if (firstUnknown !== void 0) {
191184
- const kinds2 = [...new Set(unknown2.map((manifest) => manifest.kind))];
191185
- context.report.add(
191186
- DIAGNOSTIC_CODES.importSkipped,
191187
- spanAt3(firstUnknown.line, firstUnknown.column),
191188
- `${countOf2(unknown2.length, "object")} of a kind this reader does not draw ${unknown2.length === 1 ? "was" : "were"} left out: ${kinds2.slice(0, 6).join(", ")}.`,
191189
- "The reader draws workloads, Services, Ingresses, ConfigMaps, Secrets, and PersistentVolumeClaims. Add the others by hand if they matter."
191190
- );
191191
- }
191192
- if (drawn.length === 0) {
191193
- context.report.add(DIAGNOSTIC_CODES.importNoShape, spanAt3(1, 1), "No workload, Service, Ingress, ConfigMap, Secret, or claim was found.", "Send manifests with apiVersion, kind, and metadata.name, separated by ---.");
191194
- return null;
191195
- }
191196
- const ordered = [...drawn].sort((a, b) => (LOOKS[a.kind]?.rank ?? 2) - (LOOKS[b.kind]?.rank ?? 2));
191197
- const namespaces = [...new Set(ordered.map((manifest) => manifest.namespace))];
191198
- const grouped = namespaces.length > 1;
191199
- const groups = grouped ? namespaces.map((namespace) => ({ key: `ns:${namespace}`, label: namespace, type: namespaceType(namespace), sub: "Namespace", idHint: `ns_${namespace}` })) : [];
191200
- const only = namespaces[0] ?? "default";
191201
- const folded = foldedServices(ordered);
191202
- const servicePorts = /* @__PURE__ */ new Map();
191203
- for (const manifest of ordered) {
191204
- const workload = folded.get(keyOf3(manifest.kind, manifest.namespace, manifest.name));
191205
- const ports = manifest.detail?.replace(/^ClusterIP(, )?/, "") ?? "";
191206
- if (workload !== void 0 && ports !== "" && !servicePorts.has(workload)) servicePorts.set(workload, ports);
191207
- }
191208
- const boxes2 = ordered.filter((manifest) => !folded.has(keyOf3(manifest.kind, manifest.namespace, manifest.name)));
191209
- const workloads = boxes2.filter((manifest) => manifest.image !== null).length;
191210
- const others = boxes2.length - workloads;
191211
- return writeFlow(
191212
- {
191213
- title: context.title ?? (grouped || only === "default" ? "Kubernetes workloads" : `Kubernetes namespace ${only}`),
191214
- desc: `${countOf2(workloads, "workload")}${others > 0 ? ` and ${countOf2(others, "object")} around ${workloads === 1 ? "it" : "them"}` : ""}${grouped ? ` in ${countOf2(namespaces.length, "namespace")}` : ""}, imported from Kubernetes manifests. ${boxes2.some((manifest) => manifest.kind === "Ingress") ? "Traffic flows from each Ingress to the pods behind its Services" : "Solid arrows are calls to a Service"}, and dashed arrows are configuration the pods read.`,
191215
- direction: "down",
191216
- nodes: boxes2.map((manifest) => nodeOf3(manifest, grouped, servicePorts.get(keyOf3(manifest.kind, manifest.namespace, manifest.name)) ?? null)),
191217
- groups,
191218
- edges: edgesOf2(ordered, folded, context),
191219
- noun: "objects",
191220
- capSpan: (key) => {
191221
- const manifest = ordered.find((candidate2) => keyOf3(candidate2.kind, candidate2.namespace, candidate2.name) === key);
191222
- return spanAt3(manifest?.line ?? 1, manifest?.column ?? 1);
191223
- },
191224
- advice: "Check the arrows from each Service: they follow its selector, so a selector that matches no pod template draws none. Label the Ingress arrows with their paths if they carry none."
191225
- },
191226
- context.report
191227
- );
192400
+ const documents = manifestRoots(read.documents.map((document2) => document2.root).filter((root) => root !== null));
192401
+ return drawManifests(withoutHashes(documents), context, { framesOf: () => [], title: "Kustomize build", source: "the output of kustomize build" });
191228
192402
  }
191229
- var LOOKS, keyOf3, selects, selected, kubernetesImporter;
191230
- var init_kubernetes = __esm({
191231
- "../../packages/diagrams/src/importers/kubernetes/index.ts"() {
192403
+ var GENERATED_HASH, kustomizeImporter;
192404
+ var init_kustomize = __esm({
192405
+ "../../packages/diagrams/src/importers/kustomize/index.ts"() {
191232
192406
  "use strict";
191233
- init_constants();
191234
192407
  init_core();
192408
+ init_kubernetes();
192409
+ init_manifests();
191235
192410
  init_shared2();
191236
192411
  init_yaml();
191237
- init_manifests();
191238
- LOOKS = {
191239
- Ingress: { type: "gateway", icon: "lucide:globe", rank: 0 },
191240
- Service: { type: "gateway", icon: "lucide:network", rank: 1 },
191241
- Deployment: { type: "service", icon: null, rank: 2 },
191242
- StatefulSet: { type: "service", icon: null, rank: 2 },
191243
- DaemonSet: { type: "service", icon: null, rank: 2 },
191244
- ReplicaSet: { type: "service", icon: null, rank: 2 },
191245
- Pod: { type: "service", icon: null, rank: 2 },
191246
- Job: { type: "function", icon: "lucide:play", rank: 3 },
191247
- CronJob: { type: "function", icon: "lucide:calendar-clock", rank: 3 },
191248
- PersistentVolumeClaim: { type: "storage", icon: "lucide:hard-drive", rank: 4 },
191249
- ConfigMap: { type: "storage", icon: "lucide:settings-2", rank: 5 },
191250
- Secret: { type: "secret", icon: "lucide:key-round", rank: 5 }
191251
- };
191252
- keyOf3 = (kind, namespace, name2) => `${namespace}/${kind}/${name2}`;
191253
- selects = (selector, labels2) => selector.size > 0 && [...selector].every(([key, value]) => labels2.get(key) === value);
191254
- selected = (service, drawn) => drawn.filter((workload) => workload.namespace === service.namespace && workload.kind !== "Service" && workload.image !== null && selects(service.labels, workload.labels));
191255
- kubernetesImporter = { format: "kubernetes", kinds: ["flow"], joins: "\n---\n", extensions: [".yaml", ".yml"], read: readKubernetes };
192412
+ init_folder();
192413
+ GENERATED_HASH = /-(?=[^-]*\d)[b-df-hj-np-tv-z0-9]{10}$/;
192414
+ kustomizeImporter = { format: "kustomize", kinds: ["flow"], joins: "\n---\n", extensions: [".yaml", ".yml", ".json", ".env", ".properties", "Kustomization"], read: readKustomize };
191256
192415
  }
191257
192416
  });
191258
192417
 
@@ -192024,7 +193183,7 @@ var init_prisma = __esm({
192024
193183
  });
192025
193184
 
192026
193185
  // ../../packages/diagrams/src/importers/pulumi/item.ts
192027
- function valueAt(properties, path) {
193186
+ function valueAt2(properties, path) {
192028
193187
  let current = properties;
192029
193188
  for (const key of path) {
192030
193189
  if (!isObject2(current)) return null;
@@ -192048,7 +193207,7 @@ function nameKeyOf2(token, keys) {
192048
193207
  function itemOf(resource) {
192049
193208
  const tokenType = pulumiToTerraform(resource.token);
192050
193209
  if (tokenType === null) return null;
192051
- const type = refineByKind(tokenType, valueAt(resource.properties, ["kind"]));
193210
+ const type = refineByKind(tokenType, valueAt2(resource.properties, ["kind"]));
192052
193211
  const attributes2 = [];
192053
193212
  const nameKey3 = nameProperty(resource.token, resource.properties);
192054
193213
  if (nameKey3 !== null && isObject2(resource.properties)) {
@@ -192059,7 +193218,7 @@ function itemOf(resource) {
192059
193218
  const role = roleOf2(type);
192060
193219
  for (const [path, attribute2] of DETAILS2) {
192061
193220
  if (!placed(attribute2)) continue;
192062
- const value = valueAt(resource.properties, path);
193221
+ const value = valueAt2(resource.properties, path);
192063
193222
  if (value !== null) attributes2.push({ name: attribute2 === "sku" ? skuAttribute(role.role === "node" ? role.detail ?? [] : []) : attribute2, refs: [], value });
192064
193223
  }
192065
193224
  if (isObject2(resource.properties)) {
@@ -192072,7 +193231,7 @@ function itemOf(resource) {
192072
193231
  if (resource.dependsOn.length > 0) attributes2.push({ name: "depends_on", refs: resource.dependsOn, value: null });
192073
193232
  if (resource.parent !== null) attributes2.push({ name: "parent_id", refs: [resource.parent], value: null });
192074
193233
  if (type === "azurerm_role_assignment") {
192075
- const role2 = roleName(valueAt(resource.properties, ["roleDefinitionId"]) ?? JSON.stringify(resource.properties));
193234
+ const role2 = roleName(valueAt2(resource.properties, ["roleDefinitionId"]) ?? JSON.stringify(resource.properties));
192076
193235
  if (role2 !== null) attributes2.push({ name: "role_definition_name", refs: [], value: role2 });
192077
193236
  }
192078
193237
  const group2 = attributes2.find((attribute2) => attribute2.name === "resource_group_name");
@@ -192332,7 +193491,7 @@ function valueOf2(value, depth = 0) {
192332
193491
  if (depth > 32) return null;
192333
193492
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) return value;
192334
193493
  if (Array.isArray(value)) return value.map((item) => valueOf2(item, depth + 1));
192335
- if (isRecord4(value)) {
193494
+ if (isRecord5(value)) {
192336
193495
  if (value["4dabf18193072939515e22adb298388d"] === "1b47061264138c4ac30d75fd1eb44270") return null;
192337
193496
  const out = {};
192338
193497
  for (const [key, entry2] of Object.entries(value)) out[key] = valueOf2(entry2, depth + 1);
@@ -192341,9 +193500,9 @@ function valueOf2(value, depth = 0) {
192341
193500
  return null;
192342
193501
  }
192343
193502
  function resourceOf2(raw) {
192344
- if (!isRecord4(raw) || typeof raw["urn"] !== "string" || typeof raw["type"] !== "string") return null;
193503
+ if (!isRecord5(raw) || typeof raw["urn"] !== "string" || typeof raw["type"] !== "string") return null;
192345
193504
  const dependencies = {};
192346
- if (isRecord4(raw["propertyDependencies"])) for (const [key, value] of Object.entries(raw["propertyDependencies"])) dependencies[key] = strings(value);
193505
+ if (isRecord5(raw["propertyDependencies"])) for (const [key, value] of Object.entries(raw["propertyDependencies"])) dependencies[key] = strings(value);
192347
193506
  return {
192348
193507
  urn: raw["urn"],
192349
193508
  type: raw["type"],
@@ -192375,8 +193534,8 @@ function pathTo(value, wanted2, path, depth = 0) {
192375
193534
  return null;
192376
193535
  }
192377
193536
  function readState(json2) {
192378
- if (!isRecord4(json2)) return null;
192379
- const deployment = isRecord4(json2["deployment"]) ? json2["deployment"] : isRecord4(json2["checkpoint"]) && isRecord4(json2["checkpoint"]["latest"]) ? json2["checkpoint"]["latest"] : null;
193537
+ if (!isRecord5(json2)) return null;
193538
+ const deployment = isRecord5(json2["deployment"]) ? json2["deployment"] : isRecord5(json2["checkpoint"]) && isRecord5(json2["checkpoint"]["latest"]) ? json2["checkpoint"]["latest"] : null;
192380
193539
  if (deployment !== null && Array.isArray(deployment["resources"])) {
192381
193540
  return { resources: deployment["resources"].map(resourceOf2).filter((resource) => resource !== null), deleted: 0, kind: "export" };
192382
193541
  }
@@ -192384,7 +193543,7 @@ function readState(json2) {
192384
193543
  const resources = [];
192385
193544
  let deleted = 0;
192386
193545
  for (const step of json2["steps"]) {
192387
- if (!isRecord4(step)) continue;
193546
+ if (!isRecord5(step)) continue;
192388
193547
  const op = typeof step["op"] === "string" ? step["op"] : "";
192389
193548
  if (op === "delete" || op === "discard" || op === "delete-replaced" || op === "read-discard" || op === "remove-pending-replace") {
192390
193549
  deleted += 1;
@@ -192403,18 +193562,18 @@ function stateResources(state, offsetOf) {
192403
193562
  const counts = /* @__PURE__ */ new Map();
192404
193563
  for (const resource of all) counts.set(logicalName(resource.urn), (counts.get(logicalName(resource.urn)) ?? 0) + 1);
192405
193564
  const nameOf10 = (urn) => (counts.get(logicalName(urn)) ?? 0) > 1 ? urn : logicalName(urn);
192406
- const safe2 = (name2) => name2.replace(/[^A-Za-z0-9_-]+/g, "_");
193565
+ const safe3 = (name2) => name2.replace(/[^A-Za-z0-9_-]+/g, "_");
192407
193566
  const scopeOf2 = (resource) => {
192408
193567
  const chain2 = [];
192409
193568
  let parent = resource.parent === null ? void 0 : byUrn.get(resource.parent);
192410
193569
  for (let depth = 0; parent !== void 0 && depth < 16; depth += 1) {
192411
- if (!parent.custom) chain2.unshift(`module.${safe2(logicalName(parent.urn))}.`);
193570
+ if (!parent.custom) chain2.unshift(`module.${safe3(logicalName(parent.urn))}.`);
192412
193571
  parent = parent.parent === null ? void 0 : byUrn.get(parent.parent);
192413
193572
  }
192414
193573
  return chain2.join("");
192415
193574
  };
192416
193575
  const modules = all.filter((resource) => !resource.custom).map((resource) => {
192417
- const scope = `${scopeOf2(resource)}module.${safe2(logicalName(resource.urn))}.`;
193576
+ const scope = `${scopeOf2(resource)}module.${safe3(logicalName(resource.urn))}.`;
192418
193577
  return { scope, name: scope.slice(0, -1), label: logicalName(resource.urn), instance: null, source: resource.type, start: offsetOf(resource.urn), noun: "Component" };
192419
193578
  });
192420
193579
  const resources = all.filter((resource) => resource.custom).map((resource) => {
@@ -192450,15 +193609,15 @@ function stateResources(state, offsetOf) {
192450
193609
  return { resources, modules };
192451
193610
  }
192452
193611
  function mergeValues2(outputs, inputs) {
192453
- if (!isRecord4(outputs)) return inputs;
192454
- if (!isRecord4(inputs)) return outputs;
193612
+ if (!isRecord5(outputs)) return inputs;
193613
+ if (!isRecord5(inputs)) return outputs;
192455
193614
  return { ...outputs, ...inputs };
192456
193615
  }
192457
- var isRecord4, strings, logicalName, isPlumbing;
193616
+ var isRecord5, strings, logicalName, isPlumbing;
192458
193617
  var init_state10 = __esm({
192459
193618
  "../../packages/diagrams/src/importers/pulumi/state.ts"() {
192460
193619
  "use strict";
192461
- isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
193620
+ isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
192462
193621
  strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
192463
193622
  logicalName = (urn) => urn.slice(urn.lastIndexOf("::") + 2);
192464
193623
  isPlumbing = (resource) => resource.type === "pulumi:pulumi:Stack" || resource.type.startsWith("pulumi:providers:");
@@ -192524,7 +193683,7 @@ function readPulumi(joinedText, context) {
192524
193683
  const chosenNames = context.options.varFiles ?? [];
192525
193684
  const picked = chosenNames.length > 0 ? stacks.filter((stack) => chosenNames.some((name2) => stack.path === name2 || stack.path.endsWith(`/${name2.replace(/^\.?\//, "")}`))) : stacks.length === 1 ? stacks : [];
192526
193685
  if (chosenNames.length === 0 && stacks.length === 1) context.report.add(DIAGNOSTIC_CODES.importChanged, index2.span(stacks[0]?.start ?? 0), `The values of ${cleanText(stacks[0]?.path ?? "")} are drawn: it is the only stack configuration sent.`, "Set options.varFiles to draw with another stack.");
192527
- if (chosenNames.length === 0 && stacks.length > 1) context.report.add(DIAGNOSTIC_CODES.importSkipped, START, `${countOf2(stacks.length, "stack configuration")} are not applied, because each sets the same values: ${stacks.slice(0, LISTED9).map((stack) => cleanText(stack.path)).join(", ")}.`, `Set options.varFiles to the stack to draw, such as ["${stacks[0]?.path ?? ""}"]; until then, values show their defaults or their names.`);
193686
+ if (chosenNames.length === 0 && stacks.length > 1) context.report.add(DIAGNOSTIC_CODES.importSkipped, START, `${countOf2(stacks.length, "stack configuration")} are not applied, because each sets the same values: ${stacks.slice(0, LISTED10).map((stack) => cleanText(stack.path)).join(", ")}.`, `Set options.varFiles to the stack to draw, such as ["${stacks[0]?.path ?? ""}"]; until then, values show their defaults or their names.`);
192528
193687
  const config2 = /* @__PURE__ */ new Map();
192529
193688
  for (const stack of picked) for (const [key, value] of configOf(stack.root)) config2.set(key, value);
192530
193689
  const stackName = picked.length === 1 ? stackOf(picked[0]?.path ?? "") : null;
@@ -192544,7 +193703,7 @@ function readPulumi(joinedText, context) {
192544
193703
  else chosen = [match];
192545
193704
  }
192546
193705
  if (chosen.length === 1) return drawSource(chosen[0], context, index2);
192547
- context.report.add(DIAGNOSTIC_CODES.importChanged, START, `The files hold ${chosen.length} programs and states (${chosen.slice(0, LISTED9).map((source) => source.path).join(", ")}), so each is drawn as a diagram of its own.`, `Set options.root to draw one of them, such as "${chosen[0]?.path ?? ""}".`);
193706
+ context.report.add(DIAGNOSTIC_CODES.importChanged, START, `The files hold ${chosen.length} programs and states (${chosen.slice(0, LISTED10).map((source) => source.path).join(", ")}), so each is drawn as a diagram of its own.`, `Set options.root to draw one of them, such as "${chosen[0]?.path ?? ""}".`);
192548
193707
  const drafts2 = [];
192549
193708
  for (const source of chosen) {
192550
193709
  const draft = drawSource(source, { ...context, title: `${context.title ?? "Cloud infrastructure"}: ${source.path}` }, index2);
@@ -192581,7 +193740,7 @@ function drawSource(source, context, index2) {
192581
193740
  layered: true
192582
193741
  });
192583
193742
  }
192584
- var LISTED9, stackOf, pulumiImporter;
193743
+ var LISTED10, stackOf, pulumiImporter;
192585
193744
  var init_pulumi = __esm({
192586
193745
  "../../packages/diagrams/src/importers/pulumi/index.ts"() {
192587
193746
  "use strict";
@@ -192594,7 +193753,7 @@ var init_pulumi = __esm({
192594
193753
  init_item();
192595
193754
  init_program();
192596
193755
  init_state10();
192597
- LISTED9 = 5;
193756
+ LISTED10 = 5;
192598
193757
  stackOf = (path) => /(?:^|[\\/])Pulumi\.([^.\\/]+)\.ya?ml$/i.exec(path)?.[1] ?? null;
192599
193758
  pulumiImporter = { format: "pulumi", kinds: ["flow"], joins: "\n", extensions: [".yaml", ".yml", ".json"], read: readPulumi };
192600
193759
  }
@@ -194003,15 +195162,19 @@ var init_registry4 = __esm({
194003
195162
  "../../packages/diagrams/src/importers/registry.ts"() {
194004
195163
  "use strict";
194005
195164
  init_arm();
195165
+ init_backstage();
194006
195166
  init_bicep();
195167
+ init_cdk();
194007
195168
  init_cloudformation();
194008
195169
  init_compose();
194009
195170
  init_csv();
194010
195171
  init_drizzle();
194011
195172
  init_git_log();
195173
+ init_helm();
194012
195174
  init_github_actions();
194013
195175
  init_json();
194014
195176
  init_kubernetes();
195177
+ init_kustomize();
194015
195178
  init_openapi();
194016
195179
  init_prisma();
194017
195180
  init_pulumi();
@@ -194030,8 +195193,12 @@ var init_registry4 = __esm({
194030
195193
  arm: armImporter,
194031
195194
  pulumi: pulumiImporter,
194032
195195
  cloudformation: cloudformationImporter,
195196
+ cdk: cdkImporter,
194033
195197
  compose: composeImporter,
194034
195198
  kubernetes: kubernetesImporter,
195199
+ helm: helmImporter,
195200
+ kustomize: kustomizeImporter,
195201
+ backstage: backstageImporter,
194035
195202
  "github-actions": githubActionsImporter,
194036
195203
  csv: csvImporter,
194037
195204
  json: jsonImporter,
@@ -194402,10 +195569,10 @@ var init_definitions = __esm({
194402
195569
  getDiagramSyntaxTool = defineTool({
194403
195570
  name: "get_diagram_syntax",
194404
195571
  title: "Get diagram syntax",
194405
- description: "Read the diagram language as data. Call it once with no kind before your first diagram: the overview, the tables every kind shares (node types, arrows including dotted ..> for asynchronous calls, animated and numbered lines, icon sets, size limits, how to edit an existing SVG), and one line about each kind. For one kind in full, call it again with kind set to it: its statements, limits, and a tested example. kind flow adds the group types (type=), overlays, stack= marks, and a complete cloud architecture example; build the group tree top-down with type=, one node per service, and icons from map_icons or search_icons. For a common architecture, find_pattern gives a checked diagram to adapt.",
195572
+ description: "Read the diagram language as data. Call it once with no kind before your first diagram: the overview, the tables every kind shares (node types, arrows including dotted ..> for asynchronous calls, animated and numbered lines, icon sets, size limits, how to edit an existing SVG), and one line about each kind. For one kind in full, call it again with kind set to it: its statements, limits, and a tested example. kind flow adds the group types (type=), overlays, stack= marks, and a complete cloud architecture example; build the group tree top-down with type=, one node per service, and icons from map_icons or search_icons. For a common architecture, find_pattern gives a checked diagram to adapt. topic import-formats lists what import_source reads.",
194406
195573
  input: SyntaxInputSchema,
194407
195574
  output: SyntaxResponseSchema,
194408
- execute: async (input2) => ok(buildSyntax(input2.kind))
195575
+ execute: async (input2) => ok(buildSyntax(input2.kind, input2.topic))
194409
195576
  });
194410
195577
  readDiagramTool = defineTool({
194411
195578
  name: "read_diagram",
@@ -195503,7 +196670,7 @@ var init_import_source3 = __esm({
195503
196670
  importSourceWorkflow = defineWorkflow({
195504
196671
  name: "import_source",
195505
196672
  title: "Import source",
195506
- description: "Workflow. Turns a file into a finished diagram in one call: SQL DDL, Prisma, or Drizzle into a schema; an OpenAPI document into an endpoint access matrix, a map of its endpoints by tag, or one operation as a sequence; Terraform, Bicep, ARM, Pulumi, CloudFormation, Compose, Kubernetes, or a GitHub Actions workflow into a flow with icons and frames; CSV or JSON data into the chart its shape suits; tree output or paths into a tree; a git log into a git graph or timeline. Prefer it to writing the diagram by hand whenever the person has such a file: send its text, never a path, or for a stack, migrations, or manifests send every file of the folder in files. Returns the source, diagnostics pointing into the file for anything left out, coverage of the file, parts (every diagram of a split file), the validation, an outline, viewUrl, and saveCommand. It runs no code and reads no file or URL. Replaces writing the source by hand, validate_diagram and outline_diagram.",
196673
+ description: "Workflow. Turns a file into a finished diagram in one call: a database schema, an API spec, infrastructure as code, manifests, a software catalog, a CI workflow, CSV or JSON data, a folder listing, or a git log. get_diagram_syntax with topic import-formats says what each format reads, draws, and takes. Prefer it to drawing by hand whenever the person has such a file: send its text, never a path, or every file of a folder in files. Returns the source, diagnostics pointing into the file for anything left out, coverage, parts (every diagram of a split file), the validation, an outline, viewUrl, and saveCommand. It runs no code and reads no file or URL. Replaces validate_diagram and outline_diagram.",
195507
196674
  input: ImportSourceInputSchema,
195508
196675
  output: ImportSourceResponseSchema,
195509
196676
  steps: [{ call: "importSource" }, { call: "validate", optional: true }, { call: "outline", optional: true }],
@@ -197733,7 +198900,7 @@ var init_view = __esm({
197733
198900
  });
197734
198901
 
197735
198902
  // ../../packages/diagrams/src/tools/view-link.ts
197736
- function isRecord5(value) {
198903
+ function isRecord6(value) {
197737
198904
  return typeof value === "object" && value !== null && !Array.isArray(value);
197738
198905
  }
197739
198906
  function pictureFileName(source) {
@@ -197757,7 +198924,7 @@ function savingSentence(hasCommand, release = CLI_RELEASE) {
197757
198924
  async function attachViewUrl(tool, args, value, base, release = CLI_RELEASE) {
197758
198925
  if (tool === "sync_markdown") return rebaseFileLinks(value, base);
197759
198926
  const pick3 = SOURCE_OF[tool];
197760
- if (pick3 === void 0 || !isRecord5(value) || !isRecord5(args)) return value;
198927
+ if (pick3 === void 0 || !isRecord6(value) || !isRecord6(args)) return value;
197761
198928
  const picked = pick3(args, value);
197762
198929
  if (typeof picked !== "string" || picked.length === 0) return value;
197763
198930
  const settings = SettingsSchema.safeParse(args["settings"]);
@@ -197780,20 +198947,20 @@ async function attachViewUrl(tool, args, value, base, release = CLI_RELEASE) {
197780
198947
  return { ...value, next: `${next}There is no viewUrl: the link would be ${link2.length} characters, over the ${VIEW_LINKS.maxLength} a link may have. ${savingSentence(false, release)}` };
197781
198948
  }
197782
198949
  function rebaseFileLinks(value, base) {
197783
- const files = isRecord5(value) ? value["files"] : void 0;
198950
+ const files = isRecord6(value) ? value["files"] : void 0;
197784
198951
  const engine2 = viewBaseFor();
197785
- if (!isRecord5(value) || !Array.isArray(files) || base === engine2) return value;
197786
- const moved3 = files.map((file2) => isRecord5(file2) && typeof file2["viewUrl"] === "string" && file2["viewUrl"].startsWith(`${engine2}#`) ? { ...file2, viewUrl: `${base}${file2["viewUrl"].slice(engine2.length)}` } : file2);
198952
+ if (!isRecord6(value) || !Array.isArray(files) || base === engine2) return value;
198953
+ const moved3 = files.map((file2) => isRecord6(file2) && typeof file2["viewUrl"] === "string" && file2["viewUrl"].startsWith(`${engine2}#`) ? { ...file2, viewUrl: `${base}${file2["viewUrl"].slice(engine2.length)}` } : file2);
197787
198954
  return { ...value, files: moved3 };
197788
198955
  }
197789
198956
  function stripViewLinks(value) {
197790
- if (!isRecord5(value)) return value;
198957
+ if (!isRecord6(value)) return value;
197791
198958
  const kept2 = { ...value };
197792
198959
  for (const field of VIEW_LINK_FIELDS) delete kept2[field];
197793
198960
  const files = kept2["files"];
197794
198961
  if (Array.isArray(files)) {
197795
198962
  kept2["files"] = files.map((file2) => {
197796
- if (!isRecord5(file2) || !("viewUrl" in file2)) return file2;
198963
+ if (!isRecord6(file2) || !("viewUrl" in file2)) return file2;
197797
198964
  const { viewUrl: _viewUrl, ...rest } = file2;
197798
198965
  return rest;
197799
198966
  });
@@ -198212,7 +199379,7 @@ var init_server_info = __esm({
198212
199379
  `Draws ${joinWords(kindNouns(), "and")} diagrams from a small line-oriented text language.`,
198213
199380
  "Prefer the workflow tools: each does the work of several single tools in one call.",
198214
199381
  "When you are unsure which kind of diagram fits, call suggest_diagram with the request in plain words and any data: it ranks the kinds and gives a starter that parses; for a whole document, plan_document_diagrams returns the sections a picture would help, each with a starter.",
198215
- "When the diagram should come from a file the person has (SQL, Prisma, or Drizzle models, an OpenAPI document, Terraform, Bicep, ARM, Pulumi, or CloudFormation, docker-compose, Kubernetes manifests, a GitHub Actions workflow, CSV or JSON data, a folder listing, or a git log), call import_source with its text and format: it builds the whole diagram from the file in one call, and its diagnostics say what was left out.",
199382
+ "When the diagram should come from a file the person has (SQL, Prisma, or Drizzle models, an OpenAPI document, Terraform, Bicep, ARM, Pulumi, CloudFormation, or the CDK, docker-compose, Kubernetes manifests, Helm, Kustomize, a Backstage catalog, a GitHub Actions workflow, CSV or JSON data, a folder listing, or a git log), call import_source with its text and format: it builds the whole diagram from the file in one call, and its diagnostics say what was left out get_diagram_syntax with topic import-formats says what each format reads.",
198216
199383
  "For a request to draw a common architecture, such as a three-tier network, a serverless API, a container platform, or a queue with workers, call find_pattern first with the request in plain words; when a pattern fits, call draw_from_pattern with its id, a provider flavour (neutral, aws, azure, or gcp), and slot values that rename its parts, or get_pattern to read and edit its source, and when nothing fits, write the diagram from scratch with draw_diagram.",
198217
199384
  "Call get_diagram_syntax once with no kind to read the shared language, and again with kind set to the kind you draw for its statements, limits, and example.",
198218
199385
  'Write the source and call draw_diagram: it returns every error with line, column, and hint and draws nothing until the source is valid, so fix every error and call again; once valid it returns the warnings, an outline to check the meaning, and the SVG for light and dark. The pictures are most of the answer and you cannot look at them, so pass svg "never" to check a diagram, and draw only when you will save, show, or publish the pictures; when a drawing is too big to read in one answer the SVG is left out, picturesOmitted says so, and svg "always" forces it.',