@google-cloud/kcmd 0.0.0-stage → 0.2.0

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.
Files changed (150) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +264 -2
  3. package/build/ts/kcmd/cli.js +20252 -0
  4. package/build/ts/kcmd/gcp/alloydb.d.ts +12 -0
  5. package/build/ts/kcmd/gcp/alloydb.js +70 -0
  6. package/build/ts/kcmd/gcp/api.d.ts +29 -0
  7. package/build/ts/kcmd/gcp/api.js +162 -0
  8. package/build/ts/kcmd/gcp/biglake.d.ts +18 -0
  9. package/build/ts/kcmd/gcp/biglake.js +59 -0
  10. package/build/ts/kcmd/gcp/bigquery.d.ts +57 -0
  11. package/build/ts/kcmd/gcp/bigquery.js +134 -0
  12. package/build/ts/kcmd/gcp/context.d.ts +10 -0
  13. package/build/ts/kcmd/gcp/context.js +74 -0
  14. package/build/ts/kcmd/gcp/crm.d.ts +13 -0
  15. package/build/ts/kcmd/gcp/crm.js +91 -0
  16. package/build/ts/kcmd/gcp/dataplex.d.ts +147 -0
  17. package/build/ts/kcmd/gcp/dataplex.js +406 -0
  18. package/build/ts/kcmd/gcp/index.d.ts +3 -0
  19. package/build/ts/kcmd/gcp/index.js +21 -0
  20. package/build/ts/kcmd/gcp/spanner.d.ts +73 -0
  21. package/build/ts/kcmd/gcp/spanner.js +126 -0
  22. package/build/ts/kcmd/index.d.ts +7 -0
  23. package/build/ts/kcmd/index.js +46 -0
  24. package/build/ts/kcmd/layout.d.ts +16 -0
  25. package/build/ts/kcmd/layout.js +31 -0
  26. package/build/ts/kcmd/layouts/documents.d.ts +24 -0
  27. package/build/ts/kcmd/layouts/documents.js +264 -0
  28. package/build/ts/kcmd/layouts/okf.d.ts +31 -0
  29. package/build/ts/kcmd/layouts/okf.js +386 -0
  30. package/build/ts/kcmd/layouts/semantic-model.d.ts +74 -0
  31. package/build/ts/kcmd/layouts/semantic-model.js +621 -0
  32. package/build/ts/kcmd/layouts/standard.d.ts +13 -0
  33. package/build/ts/kcmd/layouts/standard.js +108 -0
  34. package/build/ts/kcmd/manifest.d.ts +34 -0
  35. package/build/ts/kcmd/manifest.js +262 -0
  36. package/build/ts/kcmd/metadata.d.ts +25 -0
  37. package/build/ts/kcmd/metadata.js +8 -0
  38. package/build/ts/kcmd/okf/catalog_residue.d.ts +14 -0
  39. package/build/ts/kcmd/okf/catalog_residue.js +124 -0
  40. package/build/ts/kcmd/okf/concept.d.ts +14 -0
  41. package/build/ts/kcmd/okf/concept.js +110 -0
  42. package/build/ts/kcmd/okf/concept_to_entry.d.ts +10 -0
  43. package/build/ts/kcmd/okf/concept_to_entry.js +281 -0
  44. package/build/ts/kcmd/okf/init_okf_workspace.d.ts +15 -0
  45. package/build/ts/kcmd/okf/init_okf_workspace.js +113 -0
  46. package/build/ts/kcmd/okf/links.d.ts +23 -0
  47. package/build/ts/kcmd/okf/links.js +468 -0
  48. package/build/ts/kcmd/okf/okf_metadata.d.ts +12 -0
  49. package/build/ts/kcmd/okf/okf_metadata.js +99 -0
  50. package/build/ts/kcmd/semantic/bigquery.d.ts +22 -0
  51. package/build/ts/kcmd/semantic/bigquery.js +469 -0
  52. package/build/ts/kcmd/semantic/binding.d.ts +3 -0
  53. package/build/ts/kcmd/semantic/binding.js +92 -0
  54. package/build/ts/kcmd/semantic/catalog_entry.d.ts +24 -0
  55. package/build/ts/kcmd/semantic/catalog_entry.js +35 -0
  56. package/build/ts/kcmd/semantic/converters/owl/convert.d.ts +26 -0
  57. package/build/ts/kcmd/semantic/converters/owl/convert.js +41 -0
  58. package/build/ts/kcmd/semantic/converters/owl/model.d.ts +98 -0
  59. package/build/ts/kcmd/semantic/converters/owl/model.js +30 -0
  60. package/build/ts/kcmd/semantic/converters/owl/parse.d.ts +11 -0
  61. package/build/ts/kcmd/semantic/converters/owl/parse.js +634 -0
  62. package/build/ts/kcmd/semantic/converters/owl/to_ir.d.ts +19 -0
  63. package/build/ts/kcmd/semantic/converters/owl/to_ir.js +390 -0
  64. package/build/ts/kcmd/semantic/deploy_bigquery.d.ts +20 -0
  65. package/build/ts/kcmd/semantic/deploy_bigquery.js +226 -0
  66. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.d.ts +104 -0
  67. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +1367 -0
  68. package/build/ts/kcmd/semantic/deploy_spanner.d.ts +15 -0
  69. package/build/ts/kcmd/semantic/deploy_spanner.js +150 -0
  70. package/build/ts/kcmd/semantic/deployment_target.d.ts +93 -0
  71. package/build/ts/kcmd/semantic/deployment_target.js +379 -0
  72. package/build/ts/kcmd/semantic/graph_ddl.d.ts +44 -0
  73. package/build/ts/kcmd/semantic/graph_ddl.js +656 -0
  74. package/build/ts/kcmd/semantic/import_model.d.ts +72 -0
  75. package/build/ts/kcmd/semantic/import_model.js +1698 -0
  76. package/build/ts/kcmd/semantic/ir.d.ts +589 -0
  77. package/build/ts/kcmd/semantic/ir.js +247 -0
  78. package/build/ts/kcmd/semantic/kc_actions.d.ts +45 -0
  79. package/build/ts/kcmd/semantic/kc_actions.js +531 -0
  80. package/build/ts/kcmd/semantic/kc_constraints.d.ts +44 -0
  81. package/build/ts/kcmd/semantic/kc_constraints.js +279 -0
  82. package/build/ts/kcmd/semantic/kc_converter.d.ts +37 -0
  83. package/build/ts/kcmd/semantic/kc_converter.js +839 -0
  84. package/build/ts/kcmd/semantic/kc_custom_types.d.ts +63 -0
  85. package/build/ts/kcmd/semantic/kc_custom_types.js +751 -0
  86. package/build/ts/kcmd/semantic/kc_entries.d.ts +155 -0
  87. package/build/ts/kcmd/semantic/kc_entries.js +284 -0
  88. package/build/ts/kcmd/semantic/kc_ids.d.ts +102 -0
  89. package/build/ts/kcmd/semantic/kc_ids.js +188 -0
  90. package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +29 -0
  91. package/build/ts/kcmd/semantic/knowledge_catalog.js +960 -0
  92. package/build/ts/kcmd/semantic/loader.d.ts +258 -0
  93. package/build/ts/kcmd/semantic/loader.js +3576 -0
  94. package/build/ts/kcmd/semantic/names.d.ts +10 -0
  95. package/build/ts/kcmd/semantic/names.js +85 -0
  96. package/build/ts/kcmd/semantic/osi_converter.d.ts +60 -0
  97. package/build/ts/kcmd/semantic/osi_converter.js +910 -0
  98. package/build/ts/kcmd/semantic/parameters.d.ts +19 -0
  99. package/build/ts/kcmd/semantic/parameters.js +171 -0
  100. package/build/ts/kcmd/semantic/pull_kc.d.ts +10 -0
  101. package/build/ts/kcmd/semantic/pull_kc.js +272 -0
  102. package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +59 -0
  103. package/build/ts/kcmd/semantic/resolve_inheritance.js +346 -0
  104. package/build/ts/kcmd/semantic/resolve_profiles.d.ts +291 -0
  105. package/build/ts/kcmd/semantic/resolve_profiles.js +2003 -0
  106. package/build/ts/kcmd/semantic/runtime/agent_tools.d.ts +93 -0
  107. package/build/ts/kcmd/semantic/runtime/agent_tools.js +243 -0
  108. package/build/ts/kcmd/semantic/runtime/dialect.d.ts +13 -0
  109. package/build/ts/kcmd/semantic/runtime/dialect.js +39 -0
  110. package/build/ts/kcmd/semantic/runtime/runtime.d.ts +88 -0
  111. package/build/ts/kcmd/semantic/runtime/runtime.js +301 -0
  112. package/build/ts/kcmd/semantic/runtime/store.d.ts +112 -0
  113. package/build/ts/kcmd/semantic/runtime/store.js +344 -0
  114. package/build/ts/kcmd/semantic/skills.d.ts +110 -0
  115. package/build/ts/kcmd/semantic/skills.js +697 -0
  116. package/build/ts/kcmd/semantic/source_dialect.d.ts +17 -0
  117. package/build/ts/kcmd/semantic/source_dialect.js +311 -0
  118. package/build/ts/kcmd/semantic/source_entries.d.ts +12 -0
  119. package/build/ts/kcmd/semantic/source_entries.js +341 -0
  120. package/build/ts/kcmd/semantic/spanner.d.ts +18 -0
  121. package/build/ts/kcmd/semantic/spanner.js +60 -0
  122. package/build/ts/kcmd/semantic/sql_expr_utils.d.ts +23 -0
  123. package/build/ts/kcmd/semantic/sql_expr_utils.js +394 -0
  124. package/build/ts/kcmd/semantic/sql_identifiers.d.ts +6 -0
  125. package/build/ts/kcmd/semantic/sql_identifiers.js +267 -0
  126. package/build/ts/kcmd/semantic/sql_parser.d.ts +47 -0
  127. package/build/ts/kcmd/semantic/sql_parser.js +400 -0
  128. package/build/ts/kcmd/semantic/transpile.d.ts +76 -0
  129. package/build/ts/kcmd/semantic/transpile.js +581 -0
  130. package/build/ts/kcmd/semantic/validate.d.ts +70 -0
  131. package/build/ts/kcmd/semantic/validate.js +2266 -0
  132. package/build/ts/kcmd/semantic/yaml_options.d.ts +6 -0
  133. package/build/ts/kcmd/semantic/yaml_options.js +58 -0
  134. package/build/ts/kcmd/semantic/yaml_update.d.ts +26 -0
  135. package/build/ts/kcmd/semantic/yaml_update.js +782 -0
  136. package/build/ts/kcmd/snapshot.d.ts +37 -0
  137. package/build/ts/kcmd/snapshot.js +386 -0
  138. package/build/ts/kcmd/source.d.ts +31 -0
  139. package/build/ts/kcmd/source.js +119 -0
  140. package/build/ts/kcmd/sources/bq-dataset.d.ts +17 -0
  141. package/build/ts/kcmd/sources/bq-dataset.js +131 -0
  142. package/build/ts/kcmd/sources/entrygroup.d.ts +21 -0
  143. package/build/ts/kcmd/sources/entrygroup.js +83 -0
  144. package/build/ts/kcmd/sources/kb.d.ts +21 -0
  145. package/build/ts/kcmd/sources/kb.js +82 -0
  146. package/build/ts/kcmd/sources/semantic-model.d.ts +18 -0
  147. package/build/ts/kcmd/sources/semantic-model.js +48 -0
  148. package/build/ts/kcmd/sync.d.ts +35 -0
  149. package/build/ts/kcmd/sync.js +734 -0
  150. package/package.json +62 -4
@@ -0,0 +1,468 @@
1
+ "use strict";
2
+ // EntryLink serialization and parsing for OKF `bundle/_links.yaml` (SPEC §4).
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.LINKS_FILE = void 0;
38
+ exports.readBundleLinks = readBundleLinks;
39
+ exports.formatLinksYaml = formatLinksYaml;
40
+ exports.entryLinkId = entryLinkId;
41
+ exports.linkDedupKey = linkDedupKey;
42
+ exports.isStaleEntryLink = isStaleEntryLink;
43
+ exports.resolveEntryLinkType = resolveEntryLinkType;
44
+ exports.shortenLinkType = shortenLinkType;
45
+ exports.resolveEntryRefName = resolveEntryRefName;
46
+ exports.shortenEntryName = shortenEntryName;
47
+ const crypto = __importStar(require("node:crypto"));
48
+ const fs = __importStar(require("node:fs"));
49
+ const path = __importStar(require("node:path"));
50
+ const yaml = __importStar(require("yaml"));
51
+ const dataplex = __importStar(require("../gcp/dataplex"));
52
+ const concept_1 = require("./concept");
53
+ exports.LINKS_FILE = '_links.yaml';
54
+ const SYSTEM_TYPES_PREFIX = 'projects/dataplex-types/locations/global/entryLinkTypes/';
55
+ const UNDIRECTED_LINK_TYPES = new Set(['related', 'synonym', 'schema-join']);
56
+ // The system link types, which Knowledge Catalog returns under the project
57
+ // number of `dataplex-types` rather than its ID.
58
+ const SYSTEM_LINK_TYPE_NAMES = new Set([
59
+ 'related',
60
+ 'reference',
61
+ 'synonym',
62
+ 'definition',
63
+ 'schema-join',
64
+ ]);
65
+ const NUMBERED_SYSTEM_LINK_TYPE = /^projects\/\d+\/locations\/global\/entryLinkTypes\/([a-z0-9-]+)$/;
66
+ /** Reads `bundle/_links.yaml` and any frontmatter `links` from `concepts`. */
67
+ function readBundleLinks(root, entryGroup, concepts = []) {
68
+ const rawLinks = [];
69
+ let hasLinksConfig = false;
70
+ if (fs.existsSync(root)) {
71
+ for (const linksFile of findLinksFiles(root)) {
72
+ hasLinksConfig = true;
73
+ const text = fs.readFileSync(linksFile, 'utf8');
74
+ let parsed;
75
+ try {
76
+ parsed = yaml.parse(text);
77
+ }
78
+ catch (err) {
79
+ throw new Error(`invalid YAML in ${path.relative(root, linksFile)}: ${err.message || err}`);
80
+ }
81
+ if (parsed === null || parsed === undefined)
82
+ continue;
83
+ for (const item of extractRawLinks(parsed, path.relative(root, linksFile))) {
84
+ rawLinks.push({ raw: item });
85
+ }
86
+ }
87
+ }
88
+ for (const concept of concepts) {
89
+ const fm = concept.frontmatter;
90
+ const extra = asRecord(fm[concept_1.EXTRA_KEY]);
91
+ const residue = asRecord(fm[concept_1.RESIDUE_KEY]);
92
+ const conceptLinks = fm['links'] ?? extra?.['links'] ?? residue?.['links'];
93
+ if (conceptLinks === undefined)
94
+ continue;
95
+ hasLinksConfig = true;
96
+ for (const item of extractRawLinks(conceptLinks, `${concept.id}.md`)) {
97
+ rawLinks.push({ raw: item, sourceId: concept.id });
98
+ }
99
+ }
100
+ const byKey = new Map();
101
+ for (const { raw, sourceId } of rawLinks) {
102
+ const link = toEntryLink(raw, entryGroup, sourceId);
103
+ byKey.set(linkDedupKey(link), link);
104
+ }
105
+ return { hasLinksConfig, links: [...byKey.values()] };
106
+ }
107
+ /** Formats Dataplex `EntryLink`s into canonical `bundle/_links.yaml` content. */
108
+ function formatLinksYaml(links, entryGroup) {
109
+ const serialized = links.map((link) => {
110
+ const shortType = shortenLinkType(link.entryLinkType);
111
+ const refs = orderedReferences(link.entryReferences ?? [], entryGroup).map((ref) => {
112
+ const shortName = shortenEntryName(ref.name, entryGroup);
113
+ const hasPath = typeof ref.path === 'string' && ref.path.length > 0;
114
+ const hasRole = typeof ref.type === 'string' &&
115
+ ref.type.length > 0 &&
116
+ ref.type !== 'UNSPECIFIED';
117
+ if (!hasPath && !hasRole) {
118
+ return shortName;
119
+ }
120
+ const out = { name: shortName };
121
+ if (hasPath)
122
+ out.path = ref.path;
123
+ if (hasRole)
124
+ out.type = ref.type;
125
+ return out;
126
+ });
127
+ // The link's own ID, so that pushing a pulled bundle updates the same
128
+ // link instead of deleting it and creating one under a new ID.
129
+ const id = entryLinkId(link);
130
+ const item = {
131
+ ...(id ? { id } : {}),
132
+ type: shortType,
133
+ entries: refs,
134
+ };
135
+ if (link.aspects && Object.keys(link.aspects).length > 0) {
136
+ item.aspects = link.aspects;
137
+ }
138
+ return item;
139
+ });
140
+ return yaml.stringify({ links: serialized });
141
+ }
142
+ /** Extracts the bare `entryLinkId` from an `EntryLink.name`. */
143
+ function entryLinkId(link) {
144
+ return link.name?.split('/entryLinks/')[1] ?? link.name ?? '';
145
+ }
146
+ /** Deterministic deduplication key across link type and endpoints. */
147
+ function linkDedupKey(link) {
148
+ if (link.name)
149
+ return link.name;
150
+ const refs = (link.entryReferences ?? [])
151
+ .map((r) => `${r.type ?? 'UNSPECIFIED'}:${r.name}${r.path ? `@${r.path}` : ''}`)
152
+ .sort()
153
+ .join('|');
154
+ return `${link.entryLinkType}|${refs}`;
155
+ }
156
+ /** True when `existing` differs from `desired` in immutable type or references. */
157
+ function isStaleEntryLink(existing, desired) {
158
+ if (shortenLinkType(existing.entryLinkType) !==
159
+ shortenLinkType(desired.entryLinkType)) {
160
+ return true;
161
+ }
162
+ const a = canonicalRefs(existing.entryReferences ?? []);
163
+ const b = canonicalRefs(desired.entryReferences ?? []);
164
+ return a !== b;
165
+ }
166
+ function canonicalRefs(refs) {
167
+ return refs
168
+ .map((r) => `${r.type ?? 'UNSPECIFIED'}:${normalizeProjectSegment(r.name)}${r.path ? `@${r.path}` : ''}`)
169
+ .sort()
170
+ .join('|');
171
+ }
172
+ function normalizeProjectSegment(resourceName) {
173
+ const idx = resourceName.indexOf('/locations/');
174
+ return idx === -1 ? resourceName : resourceName.slice(idx);
175
+ }
176
+ function findLinksFiles(dir) {
177
+ const files = [];
178
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
179
+ const full = path.join(dir, entry.name);
180
+ if (entry.isDirectory()) {
181
+ files.push(...findLinksFiles(full));
182
+ }
183
+ else if (entry.name === exports.LINKS_FILE) {
184
+ files.push(full);
185
+ }
186
+ }
187
+ return files.sort();
188
+ }
189
+ function extractRawLinks(parsed, sourceLabel) {
190
+ if (Array.isArray(parsed)) {
191
+ return parsed.map((item, i) => {
192
+ const rec = asRecord(item);
193
+ if (!rec) {
194
+ throw new Error(`${sourceLabel}: link #${i + 1} must be an object`);
195
+ }
196
+ return rec;
197
+ });
198
+ }
199
+ const obj = asRecord(parsed);
200
+ if (!obj) {
201
+ throw new Error(`${sourceLabel}: expected a YAML mapping or sequence`);
202
+ }
203
+ const list = obj['links'] ?? obj['entryLinks'] ?? obj['entry_links'];
204
+ if (list !== undefined) {
205
+ if (!Array.isArray(list)) {
206
+ throw new Error(`${sourceLabel}: 'links' must be a list`);
207
+ }
208
+ return extractRawLinks(list, sourceLabel);
209
+ }
210
+ // Shorthand mapping keyed by link type: `{ related: [...], definition: [...] }`.
211
+ const out = [];
212
+ for (const [key, val] of Object.entries(obj)) {
213
+ if (!Array.isArray(val)) {
214
+ throw new Error(`${sourceLabel}: unexpected field '${key}' (expected 'links' list)`);
215
+ }
216
+ for (const item of extractRawLinks(val, `${sourceLabel}.${key}`)) {
217
+ out.push({ type: key, ...item });
218
+ }
219
+ }
220
+ return out;
221
+ }
222
+ function toEntryLink(raw, entryGroup, defaultSourceId) {
223
+ const rawType = asString(raw['type']) ??
224
+ asString(raw['entryLinkType']) ??
225
+ asString(raw['entry_link_type']) ??
226
+ asString(raw['linkType']) ??
227
+ asString(raw['link_type']);
228
+ if (!rawType) {
229
+ throw new Error(`${exports.LINKS_FILE}: link is missing 'type'`);
230
+ }
231
+ const entryLinkType = resolveEntryLinkType(rawType);
232
+ const shortType = shortenLinkType(entryLinkType);
233
+ const isUndirected = UNDIRECTED_LINK_TYPES.has(shortType);
234
+ const rawRefs = extractRawRefs(raw, defaultSourceId);
235
+ if (rawRefs.length < 2) {
236
+ throw new Error(`${exports.LINKS_FILE}: link of type '${rawType}' must reference at least 2 entries`);
237
+ }
238
+ const entryReferences = rawRefs.map((r) => toEntryReference(r, entryGroup, isUndirected));
239
+ // Directed 2-endpoint links (such as `definition`) default untyped endpoints
240
+ // to SOURCE -> TARGET in declaration order.
241
+ if (!isUndirected &&
242
+ entryReferences.length === 2 &&
243
+ entryReferences[0].type === 'UNSPECIFIED' &&
244
+ entryReferences[1].type === 'UNSPECIFIED') {
245
+ entryReferences[0].type = 'SOURCE';
246
+ entryReferences[1].type = 'TARGET';
247
+ }
248
+ const explicitId = asString(raw['id']) ??
249
+ asString(raw['entryLinkId']) ??
250
+ asString(raw['entry_link_id']) ??
251
+ asString(raw['name']);
252
+ const linkId = explicitId
253
+ ? normalizeLinkId(explicitId)
254
+ : computeDeterministicLinkId(entryLinkType, entryReferences);
255
+ const link = {
256
+ name: `${entryGroup}/entryLinks/${linkId}`,
257
+ entryLinkType,
258
+ entryReferences,
259
+ };
260
+ const rawAspects = asRecord(raw['aspects']);
261
+ if (rawAspects && Object.keys(rawAspects).length > 0) {
262
+ const aspects = {};
263
+ for (const [key, val] of Object.entries(rawAspects)) {
264
+ const valRec = asRecord(val);
265
+ if (!valRec)
266
+ continue;
267
+ const atIdx = key.indexOf('@');
268
+ const typeRef = atIdx === -1 ? key : key.slice(0, atIdx);
269
+ const keyPath = atIdx === -1 ? undefined : key.slice(atIdx + 1);
270
+ const aspectType = asString(valRec['aspectType']) ??
271
+ (typeRef.split('.').length === 3
272
+ ? dataplex._typeRefToName(typeRef, 'aspect')
273
+ : undefined);
274
+ const data = asRecord(valRec['data']) ?? valRec;
275
+ aspects[key] = {
276
+ ...(aspectType ? { aspectType } : {}),
277
+ ...(keyPath ? { path: keyPath } : {}),
278
+ data,
279
+ };
280
+ }
281
+ if (Object.keys(aspects).length > 0) {
282
+ link.aspects = aspects;
283
+ }
284
+ }
285
+ return link;
286
+ }
287
+ function extractRawRefs(raw, defaultSourceId) {
288
+ const list = raw['entries'] ??
289
+ raw['entryReferences'] ??
290
+ raw['entry_references'] ??
291
+ raw['references'];
292
+ if (Array.isArray(list)) {
293
+ if (list.length === 1 && defaultSourceId) {
294
+ return [{ name: defaultSourceId, type: 'SOURCE' }, list[0]];
295
+ }
296
+ return list;
297
+ }
298
+ const source = raw['source'] ?? raw['from'] ?? defaultSourceId;
299
+ const target = raw['target'] ?? raw['to'];
300
+ if (source !== undefined && target !== undefined) {
301
+ const srcObj = typeof source === 'string'
302
+ ? {
303
+ name: source,
304
+ path: raw['source_path'] ?? raw['sourcePath'],
305
+ type: 'SOURCE',
306
+ }
307
+ : { ...(asRecord(source) ?? {}), type: 'SOURCE' };
308
+ const tgtObj = typeof target === 'string'
309
+ ? {
310
+ name: target,
311
+ path: raw['target_path'] ?? raw['targetPath'] ?? raw['path'],
312
+ type: 'TARGET',
313
+ }
314
+ : { ...(asRecord(target) ?? {}), type: 'TARGET' };
315
+ return [srcObj, tgtObj];
316
+ }
317
+ throw new Error(`${exports.LINKS_FILE}: link is missing 'entries' list`);
318
+ }
319
+ function toEntryReference(raw, entryGroup, isUndirected) {
320
+ if (typeof raw === 'string') {
321
+ const [rawName, inlinePath] = splitInlinePath(raw);
322
+ const ref = {
323
+ name: resolveEntryRefName(rawName, entryGroup),
324
+ type: 'UNSPECIFIED',
325
+ };
326
+ if (inlinePath)
327
+ ref.path = inlinePath;
328
+ return ref;
329
+ }
330
+ const obj = asRecord(raw);
331
+ if (!obj) {
332
+ throw new Error(`${exports.LINKS_FILE}: entry reference must be a string or object`);
333
+ }
334
+ const rawName = asString(obj['name']) ??
335
+ asString(obj['entry']) ??
336
+ asString(obj['id']) ??
337
+ asString(obj['target']);
338
+ if (!rawName) {
339
+ throw new Error(`${exports.LINKS_FILE}: entry reference is missing 'name'`);
340
+ }
341
+ const [baseName, inlinePath] = splitInlinePath(rawName);
342
+ const pathVal = asString(obj['path']) ??
343
+ asString(obj['field']) ??
344
+ asString(obj['column']) ??
345
+ inlinePath;
346
+ const rawRole = (asString(obj['type']) ??
347
+ asString(obj['role']) ??
348
+ asString(obj['mode']) ??
349
+ 'UNSPECIFIED').toUpperCase();
350
+ let type = 'UNSPECIFIED';
351
+ if (!isUndirected) {
352
+ if (rawRole === 'SOURCE' || rawRole === 'FROM') {
353
+ type = 'SOURCE';
354
+ }
355
+ else if (rawRole === 'TARGET' || rawRole === 'TO') {
356
+ type = 'TARGET';
357
+ }
358
+ }
359
+ const ref = {
360
+ name: resolveEntryRefName(baseName, entryGroup),
361
+ type,
362
+ };
363
+ if (pathVal)
364
+ ref.path = pathVal;
365
+ return ref;
366
+ }
367
+ function splitInlinePath(raw) {
368
+ const hashIdx = raw.indexOf('#');
369
+ if (hashIdx === -1)
370
+ return [raw, undefined];
371
+ const base = raw.slice(0, hashIdx);
372
+ const col = raw.slice(hashIdx + 1);
373
+ if (!col)
374
+ return [base, undefined];
375
+ return [base, col.startsWith('Schema.') ? col : `Schema.${col}`];
376
+ }
377
+ function resolveEntryLinkType(rawType) {
378
+ const trimmed = rawType.trim();
379
+ if (trimmed.startsWith('projects/') && trimmed.includes('/entryLinkTypes/')) {
380
+ return trimmed;
381
+ }
382
+ if (trimmed.split('.').length === 3) {
383
+ return dataplex._typeRefToName(trimmed, 'entryLink');
384
+ }
385
+ return `${SYSTEM_TYPES_PREFIX}${trimmed}`;
386
+ }
387
+ function shortenLinkType(entryLinkType) {
388
+ if (entryLinkType.startsWith(SYSTEM_TYPES_PREFIX)) {
389
+ return entryLinkType.slice(SYSTEM_TYPES_PREFIX.length);
390
+ }
391
+ const numbered = NUMBERED_SYSTEM_LINK_TYPE.exec(entryLinkType)?.[1];
392
+ if (numbered && SYSTEM_LINK_TYPE_NAMES.has(numbered))
393
+ return numbered;
394
+ return entryLinkType;
395
+ }
396
+ function resolveEntryRefName(rawName, entryGroup) {
397
+ let cleaned = rawName.trim();
398
+ if (cleaned.startsWith('projects/') && cleaned.includes('/entries/')) {
399
+ return cleaned;
400
+ }
401
+ cleaned = cleaned
402
+ .replace(/^\.\//, '')
403
+ .replace(/^(bundle|catalog)\//, '')
404
+ .replace(/\.md$/, '');
405
+ return `${entryGroup}/entries/${cleaned}`;
406
+ }
407
+ function shortenEntryName(fullEntryName, entryGroup) {
408
+ const prefix = `${entryGroup}/entries/`;
409
+ if (fullEntryName.startsWith(prefix)) {
410
+ return fullEntryName.slice(prefix.length);
411
+ }
412
+ // Handle project-number vs project-id spelling of the same location + entryGroup.
413
+ const locIdx = entryGroup.indexOf('/locations/');
414
+ if (locIdx !== -1) {
415
+ const groupSuffix = `${entryGroup.slice(locIdx)}/entries/`;
416
+ const matchIdx = fullEntryName.indexOf(groupSuffix);
417
+ if (matchIdx !== -1 && fullEntryName.startsWith('projects/')) {
418
+ return fullEntryName.slice(matchIdx + groupSuffix.length);
419
+ }
420
+ }
421
+ return fullEntryName;
422
+ }
423
+ function orderedReferences(refs, entryGroup) {
424
+ const copy = [...refs];
425
+ const hasDirected = copy.some((r) => r.type === 'SOURCE' || r.type === 'TARGET');
426
+ if (hasDirected) {
427
+ const rank = (t) => t === 'SOURCE' ? 0 : t === 'TARGET' ? 1 : 2;
428
+ return copy.sort((a, b) => rank(a.type) - rank(b.type));
429
+ }
430
+ // For undirected links, list in-group entries before external entries.
431
+ const isLocal = (r) => shortenEntryName(r.name, entryGroup) !== r.name;
432
+ return copy.sort((a, b) => Number(isLocal(b)) - Number(isLocal(a)));
433
+ }
434
+ function normalizeLinkId(rawId) {
435
+ const tail = rawId.includes('/entryLinks/')
436
+ ? rawId.split('/entryLinks/')[1]
437
+ : rawId;
438
+ const slug = tail
439
+ .toLowerCase()
440
+ .replace(/[^a-z0-9-]+/g, '-')
441
+ .replace(/^-+|-+$/g, '');
442
+ if (!slug)
443
+ return 'link-default';
444
+ const prefixed = /^[a-z]/.test(slug) ? slug : `link-${slug}`;
445
+ return prefixed.slice(0, 63).replace(/-+$/, '');
446
+ }
447
+ function computeDeterministicLinkId(entryLinkType, refs) {
448
+ const canonical = `${entryLinkType}|${refs
449
+ .map((r) => `${r.type}:${r.name}${r.path ? `@${r.path}` : ''}`)
450
+ .sort()
451
+ .join('|')}`;
452
+ const digest = crypto
453
+ .createHash('sha1')
454
+ .update(canonical)
455
+ .digest('hex')
456
+ .slice(0, 10);
457
+ return `link-${digest}`;
458
+ }
459
+ function asRecord(val) {
460
+ return val && typeof val === 'object' && !Array.isArray(val)
461
+ ? val
462
+ : undefined;
463
+ }
464
+ function asString(val) {
465
+ return typeof val === 'string' && val.trim().length > 0
466
+ ? val.trim()
467
+ : undefined;
468
+ }
@@ -0,0 +1,12 @@
1
+ import { OKF_METADATA_KEYS } from './concept';
2
+ export { OKF_METADATA_KEYS };
3
+ /** Appends the section. The body is returned unchanged when there is none. */
4
+ export declare function appendOkfMetadata(body: string, frontmatter: Record<string, unknown>): string;
5
+ /**
6
+ * Splits a trailing section back off the overview body. Only a well-formed
7
+ * section at the very end counts, so a page that discusses one is left alone.
8
+ */
9
+ export declare function splitOkfMetadata(content: string): {
10
+ body: string;
11
+ metadata: Record<string, unknown>;
12
+ };
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ // OKF frontmatter keys the catalog has no field for, carried in a trailing
3
+ // section of the overview body so a push does not drop them.
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.OKF_METADATA_KEYS = void 0;
39
+ exports.appendOkfMetadata = appendOkfMetadata;
40
+ exports.splitOkfMetadata = splitOkfMetadata;
41
+ const yaml = __importStar(require("yaml"));
42
+ const concept_1 = require("./concept");
43
+ Object.defineProperty(exports, "OKF_METADATA_KEYS", { enumerable: true, get: function () { return concept_1.OKF_METADATA_KEYS; } });
44
+ const HEADING = '## OKF Metadata';
45
+ const OPEN = '```yaml';
46
+ const CLOSE = '```';
47
+ const MARKER = `\n\n${HEADING}\n\n${OPEN}\n`;
48
+ // Frontmatter keys stored natively on the catalog entry or in kcmd residue/links,
49
+ // so they are not duplicated into the trailing `## OKF Metadata` block.
50
+ const NON_CARRIED_KEYS = new Set([
51
+ 'type',
52
+ ...concept_1.ENTRY_SOURCE_KEYS,
53
+ concept_1.RESIDUE_KEY,
54
+ concept_1.EXTRA_KEY,
55
+ 'links',
56
+ ]);
57
+ /** Appends the section. The body is returned unchanged when there is none. */
58
+ function appendOkfMetadata(body, frontmatter) {
59
+ const carried = {};
60
+ for (const key of concept_1.OKF_METADATA_KEYS) {
61
+ if (frontmatter[key] !== undefined)
62
+ carried[key] = frontmatter[key];
63
+ }
64
+ // Preserve `x-*` producer extensions (SPEC §4.1) and any other unknown OKF
65
+ // frontmatter keys across push -> pull.
66
+ for (const [key, value] of Object.entries(frontmatter)) {
67
+ if (value === undefined || key in carried || NON_CARRIED_KEYS.has(key)) {
68
+ continue;
69
+ }
70
+ carried[key] = value;
71
+ }
72
+ if (!Object.keys(carried).length)
73
+ return body;
74
+ const block = yaml.stringify(carried);
75
+ return `${body.replace(/\n*$/, '')}${MARKER}${block}${CLOSE}\n`;
76
+ }
77
+ /**
78
+ * Splits a trailing section back off the overview body. Only a well-formed
79
+ * section at the very end counts, so a page that discusses one is left alone.
80
+ */
81
+ function splitOkfMetadata(content) {
82
+ const at = content.lastIndexOf(MARKER);
83
+ if (at === -1)
84
+ return { body: content, metadata: {} };
85
+ const inner = content.slice(at + MARKER.length);
86
+ if (!inner.endsWith(`${CLOSE}\n`))
87
+ return { body: content, metadata: {} };
88
+ let parsed;
89
+ try {
90
+ parsed = yaml.parse(inner.slice(0, -(CLOSE.length + 1)));
91
+ }
92
+ catch {
93
+ return { body: content, metadata: {} };
94
+ }
95
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
96
+ return { body: content, metadata: {} };
97
+ }
98
+ return { body: content.slice(0, at), metadata: parsed };
99
+ }
@@ -0,0 +1,22 @@
1
+ import { GenerateResult } from './graph_ddl';
2
+ import { AiContext, Field, SemanticModel } from './ir';
3
+ export interface GenerateOptions {
4
+ project?: string;
5
+ dataset?: string;
6
+ graphName?: string;
7
+ }
8
+ export type { GenerateResult };
9
+ /**
10
+ * Generates the BigQuery property-graph DDL for a semantic model.
11
+ *
12
+ * The generated statement references the entities' existing base tables; it
13
+ * does not create them. Returns the DDL plus any warnings collected while
14
+ * mapping the IR (e.g. metrics that could not be placed on a single table).
15
+ *
16
+ * Throws when the model yields no valid node table -- it declares no entities,
17
+ * or every entity lacks a KEY -- since a property graph with an empty NODE
18
+ * TABLES block is invalid DDL that BigQuery would reject.
19
+ */
20
+ export declare function generatePropertyGraph(model: SemanticModel, opts?: GenerateOptions): GenerateResult;
21
+ export declare function elementDescription(description?: string, ai?: AiContext): string | undefined;
22
+ export declare function fieldDescription(field: Field): string | undefined;