@google-cloud/kcmd 0.0.0-stage → 0.1.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 +266 -2
  3. package/build/ts/kcmd/cli.js +19578 -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 +16 -0
  9. package/build/ts/kcmd/gcp/biglake.js +50 -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 +63 -0
  21. package/build/ts/kcmd/gcp/spanner.js +123 -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 +344 -0
  30. package/build/ts/kcmd/layouts/semantic-model.d.ts +61 -0
  31. package/build/ts/kcmd/layouts/semantic-model.js +542 -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 +104 -0
  42. package/build/ts/kcmd/okf/concept_to_entry.d.ts +8 -0
  43. package/build/ts/kcmd/okf/concept_to_entry.js +254 -0
  44. package/build/ts/kcmd/okf/init_okf_workspace.d.ts +15 -0
  45. package/build/ts/kcmd/okf/init_okf_workspace.js +115 -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 +82 -0
  50. package/build/ts/kcmd/semantic/bigquery.d.ts +22 -0
  51. package/build/ts/kcmd/semantic/bigquery.js +525 -0
  52. package/build/ts/kcmd/semantic/binding.d.ts +3 -0
  53. package/build/ts/kcmd/semantic/binding.js +97 -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 +129 -0
  67. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +1396 -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 +34 -0
  73. package/build/ts/kcmd/semantic/graph_ddl.js +563 -0
  74. package/build/ts/kcmd/semantic/import_model.d.ts +50 -0
  75. package/build/ts/kcmd/semantic/import_model.js +1275 -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 +31 -0
  83. package/build/ts/kcmd/semantic/kc_converter.js +824 -0
  84. package/build/ts/kcmd/semantic/kc_custom_types.d.ts +70 -0
  85. package/build/ts/kcmd/semantic/kc_custom_types.js +828 -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 +103 -0
  89. package/build/ts/kcmd/semantic/kc_ids.js +191 -0
  90. package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +32 -0
  91. package/build/ts/kcmd/semantic/knowledge_catalog.js +1043 -0
  92. package/build/ts/kcmd/semantic/loader.d.ts +262 -0
  93. package/build/ts/kcmd/semantic/loader.js +3440 -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 +911 -0
  98. package/build/ts/kcmd/semantic/parameters.d.ts +18 -0
  99. package/build/ts/kcmd/semantic/parameters.js +152 -0
  100. package/build/ts/kcmd/semantic/pull_kc.d.ts +14 -0
  101. package/build/ts/kcmd/semantic/pull_kc.js +346 -0
  102. package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +54 -0
  103. package/build/ts/kcmd/semantic/resolve_inheritance.js +330 -0
  104. package/build/ts/kcmd/semantic/resolve_profiles.d.ts +250 -0
  105. package/build/ts/kcmd/semantic/resolve_profiles.js +1822 -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 +94 -0
  111. package/build/ts/kcmd/semantic/runtime/runtime.js +318 -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 +10 -0
  117. package/build/ts/kcmd/semantic/source_dialect.js +261 -0
  118. package/build/ts/kcmd/semantic/source_entries.d.ts +12 -0
  119. package/build/ts/kcmd/semantic/source_entries.js +282 -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 +17 -0
  123. package/build/ts/kcmd/semantic/sql_expr_utils.js +221 -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 +44 -0
  127. package/build/ts/kcmd/semantic/sql_parser.js +378 -0
  128. package/build/ts/kcmd/semantic/transpile.d.ts +74 -0
  129. package/build/ts/kcmd/semantic/transpile.js +563 -0
  130. package/build/ts/kcmd/semantic/validate.d.ts +35 -0
  131. package/build/ts/kcmd/semantic/validate.js +1899 -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 +33 -0
  137. package/build/ts/kcmd/snapshot.js +379 -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 +697 -0
  150. package/package.json +62 -4
@@ -0,0 +1,34 @@
1
+ import * as gcp from './gcp';
2
+ import { CatalogSource, Sources } from './source';
3
+ export declare const WORKSPACE_LAYOUTS: readonly ["okf"];
4
+ export type WorkspaceLayout = (typeof WORKSPACE_LAYOUTS)[number];
5
+ export interface SnapshotConfig {
6
+ entries?: string[];
7
+ aspects?: string[];
8
+ }
9
+ export interface PublishingConfig {
10
+ entries?: string[];
11
+ aspects?: string[];
12
+ }
13
+ export interface Scope {
14
+ type: string;
15
+ name: string;
16
+ }
17
+ export declare function layoutOf(manifest: any): WorkspaceLayout | undefined;
18
+ export declare function readWorkspaceLayout(path: string): WorkspaceLayout | undefined;
19
+ export declare function writeWorkspaceLayout(path: string, layout: WorkspaceLayout): void;
20
+ export declare class CatalogManifest {
21
+ readonly source: CatalogSource;
22
+ readonly snapshotConfig?: SnapshotConfig;
23
+ readonly publishingConfig?: PublishingConfig;
24
+ readonly layout?: WorkspaceLayout;
25
+ private constructor();
26
+ private static forNewSource;
27
+ static forPendingEntryGroup(type: Sources.ENTRYGROUP | Sources.KB, name: string, layout?: WorkspaceLayout): CatalogManifest;
28
+ static initWithEntryGroup(name: string, ctx: gcp.ApiContext): Promise<CatalogManifest>;
29
+ static initWithBigQuery(dataset: string, ctx: gcp.ApiContext): Promise<CatalogManifest>;
30
+ static initWithKnowledgeBase(name: string, ctx: gcp.ApiContext): Promise<CatalogManifest>;
31
+ static initWithSemanticModel(name: string, ctx: gcp.ApiContext): Promise<CatalogManifest>;
32
+ static load(path: string, ctx: gcp.ApiContext): Promise<CatalogManifest>;
33
+ save(path: string): void;
34
+ }
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ // Implements support for creating and loading catalog manifests.
3
+ //
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.CatalogManifest = exports.WORKSPACE_LAYOUTS = void 0;
39
+ exports.layoutOf = layoutOf;
40
+ exports.readWorkspaceLayout = readWorkspaceLayout;
41
+ exports.writeWorkspaceLayout = writeWorkspaceLayout;
42
+ const fs = __importStar(require("node:fs"));
43
+ const yaml = __importStar(require("yaml"));
44
+ const z = __importStar(require("zod"));
45
+ const dataplex = __importStar(require("./gcp/dataplex"));
46
+ const source_1 = require("./source");
47
+ const entrygroup_1 = require("./sources/entrygroup");
48
+ const kb_1 = require("./sources/kb");
49
+ // How a workspace stores its entries on disk, when it is not the layout its
50
+ // scope implies (see CatalogSource.layout). `kcmd init --format <layout>`
51
+ // records it, and `pull` and `push` read it back, so the choice is made once
52
+ // per workspace. Only `okf` exists: the entries as an OKF bundle under
53
+ // `bundle/`. A later layout, such as a split workspace that also keeps a
54
+ // semantic model under `catalog/`, is a new value here.
55
+ exports.WORKSPACE_LAYOUTS = ['okf'];
56
+ // The scope types a workspace layout may be recorded for.
57
+ const LAYOUT_SCOPES = [source_1.Sources.ENTRYGROUP, source_1.Sources.KB];
58
+ const manifestSchema = z.object({
59
+ scope: z.union([z.string(), z.array(z.string())]),
60
+ // The workspace layout, when it is not the scope's own (see above).
61
+ layout: z.string().optional(),
62
+ snapshot: z.object({
63
+ entries: z.array(z.string()).optional(),
64
+ aspects: z.array(z.string()).optional()
65
+ }).optional(),
66
+ publishing: z.object({
67
+ entries: z.array(z.string()).optional(),
68
+ aspects: z.array(z.string()).optional()
69
+ }).optional(),
70
+ });
71
+ // The workspace layout a parsed catalog.yaml records, or undefined when it
72
+ // records none. Throws naming the problem when the layout is not one kcmd
73
+ // supports, or when the scope's type does not take a layout.
74
+ function layoutOf(manifest) {
75
+ const layout = manifest?.layout;
76
+ if (layout === undefined || layout === null)
77
+ return undefined;
78
+ if (!exports.WORKSPACE_LAYOUTS.includes(layout)) {
79
+ throw new Error(`Manifest error: layout '${layout}' is not supported; the supported ` +
80
+ `layouts are ${exports.WORKSPACE_LAYOUTS.map(l => `'${l}'`).join(', ')}.`);
81
+ }
82
+ const scope = manifest?.scope;
83
+ const type = typeof scope === 'string' ? scope.split('.')[0] : undefined;
84
+ if (type === undefined || !LAYOUT_SCOPES.includes(type)) {
85
+ throw new Error(`Manifest error: layout '${layout}' applies only to ` +
86
+ `${LAYOUT_SCOPES.map(s => `'${s}'`).join(' and ')} scopes.`);
87
+ }
88
+ return layout;
89
+ }
90
+ // The workspace layout the catalog.yaml at `path` records, read without
91
+ // resolving the scope, so that a command can choose its path before any call
92
+ // to Knowledge Catalog (the entry group may not exist yet). Undefined when
93
+ // there is no catalog.yaml or it records no layout.
94
+ function readWorkspaceLayout(path) {
95
+ if (!fs.existsSync(path))
96
+ return undefined;
97
+ return layoutOf(yaml.parse(fs.readFileSync(path, 'utf8')));
98
+ }
99
+ // Records `layout` in the catalog.yaml at `path`, keeping every other key and
100
+ // comment as written.
101
+ function writeWorkspaceLayout(path, layout) {
102
+ const doc = yaml.parseDocument(fs.readFileSync(path, 'utf8'));
103
+ doc.set('layout', layout);
104
+ fs.writeFileSync(path, doc.toString(), 'utf8');
105
+ }
106
+ class OfflineEntryGroupSource extends entrygroup_1.EntryGroupSource {
107
+ async *entries(_ctx) { }
108
+ }
109
+ class OfflineKnowledgeBaseSource extends kb_1.KnowledgeBaseSource {
110
+ async *entries(_ctx) { }
111
+ }
112
+ class CatalogManifest {
113
+ source;
114
+ snapshotConfig;
115
+ publishingConfig;
116
+ // The workspace layout, when it is not the scope's own (see the schema).
117
+ layout;
118
+ constructor(source, snapshotConfig, publishingConfig, layout) {
119
+ this.source = source;
120
+ this.snapshotConfig = snapshotConfig;
121
+ this.publishingConfig = publishingConfig;
122
+ this.layout = layout;
123
+ }
124
+ // A manifest for a scope `kcmd init` just named, listing the scope's default
125
+ // aspects under snapshot.aspects.
126
+ static forNewSource(source) {
127
+ const aspects = [...source.defaultAspects];
128
+ return new CatalogManifest(source, aspects.length ? { aspects } : undefined);
129
+ }
130
+ // Constructs a manifest for an EntryGroup or KB that has not been created in
131
+ // Knowledge Catalog yet (used by `kcmd init --format okf` and `kcmd push --dry-run`).
132
+ static forPendingEntryGroup(type, name, layout) {
133
+ const [project, location, entryGroup] = name.split('.');
134
+ if (!project || !location || !entryGroup || name.split('.').length !== 3) {
135
+ throw new Error('EntryGroup must be in format <projectId>.<locationId>.<entryGroupId>');
136
+ }
137
+ const eg = {
138
+ name: dataplex.catalogContainer(project, location, entryGroup),
139
+ };
140
+ const source = type === source_1.Sources.KB
141
+ ? new OfflineKnowledgeBaseSource(type, name, eg)
142
+ : new OfflineEntryGroupSource(type, name, eg);
143
+ const aspects = [...source.defaultAspects];
144
+ return new CatalogManifest(source, aspects.length ? { aspects } : undefined, undefined, layout);
145
+ }
146
+ static async initWithEntryGroup(name, ctx) {
147
+ const source = await (0, source_1.createSource)(source_1.Sources.ENTRYGROUP, name, ctx);
148
+ return CatalogManifest.forNewSource(source);
149
+ }
150
+ static async initWithBigQuery(dataset, ctx) {
151
+ const source = await (0, source_1.createSource)(source_1.Sources.BIGQUERY_DATASET, dataset, ctx);
152
+ return CatalogManifest.forNewSource(source);
153
+ }
154
+ static async initWithKnowledgeBase(name, ctx) {
155
+ const source = await (0, source_1.createSource)(source_1.Sources.KB, name, ctx);
156
+ return CatalogManifest.forNewSource(source);
157
+ }
158
+ static async initWithSemanticModel(name, ctx) {
159
+ const source = await (0, source_1.createSource)(source_1.Sources.SEMANTIC_MODEL, name, ctx);
160
+ return CatalogManifest.forNewSource(source);
161
+ }
162
+ static async load(path, ctx) {
163
+ const content = fs.readFileSync(path, 'utf8');
164
+ const parsed = yaml.parse(content);
165
+ const result = manifestSchema.safeParse(parsed);
166
+ if (!result.success) {
167
+ throw new Error(`Manifest error: ${result.error.message}`);
168
+ }
169
+ const scope = result.data.scope;
170
+ const layout = layoutOf(result.data);
171
+ let source;
172
+ if (Array.isArray(scope)) {
173
+ if (scope.length === 0) {
174
+ throw new Error('Manifest error: scope array cannot be empty.');
175
+ }
176
+ const datasets = [];
177
+ for (const s of scope) {
178
+ const dotIndex = s.indexOf('.');
179
+ if (dotIndex === -1) {
180
+ throw new Error(`Manifest error: scope '${s}' is invalid.`);
181
+ }
182
+ const type = s.substring(0, dotIndex);
183
+ const name = s.substring(dotIndex + 1);
184
+ if (type !== source_1.Sources.BIGQUERY_DATASET) {
185
+ throw new Error(`Manifest error: Unsupported scope type in multiple scopes: '${type}'.`);
186
+ }
187
+ datasets.push(name);
188
+ }
189
+ source = await (0, source_1.createSource)(source_1.Sources.BIGQUERY_DATASET, datasets.join(','), ctx);
190
+ }
191
+ else {
192
+ const dotIndex = scope.indexOf('.');
193
+ if (dotIndex === -1) {
194
+ throw new Error(`Manifest error: scope '${scope}' is invalid.`);
195
+ }
196
+ source = await (0, source_1.createSource)(scope.substring(0, dotIndex), scope.substring(dotIndex + 1), ctx);
197
+ }
198
+ const snapshot = result.data.snapshot;
199
+ if (snapshot) {
200
+ if (snapshot.entries) {
201
+ for (const entryType of snapshot.entries) {
202
+ const parts = entryType.split('.');
203
+ if (parts.length !== 3) {
204
+ throw new Error(`Manifest error: Invalid Entry Type '${entryType}'`);
205
+ }
206
+ }
207
+ }
208
+ if (snapshot.aspects) {
209
+ for (const aspectType of snapshot.aspects) {
210
+ const parts = aspectType.split('.');
211
+ if (parts.length !== 3) {
212
+ throw new Error(`Manifest error: Invalid Aspect Type '${aspectType}'`);
213
+ }
214
+ }
215
+ }
216
+ }
217
+ const publishing = result.data.publishing;
218
+ if (publishing) {
219
+ if (publishing.entries) {
220
+ for (const entryType of publishing.entries) {
221
+ const parts = entryType.split('.');
222
+ if (parts.length !== 3) {
223
+ throw new Error(`Manifest error: Invalid Entry Type '${entryType}'`);
224
+ }
225
+ if (!snapshot?.entries?.includes(entryType)) {
226
+ throw new Error(`Manifest error: Publishing entry type '${entryType}' is not listed in snapshot entries.`);
227
+ }
228
+ }
229
+ }
230
+ if (publishing.aspects) {
231
+ for (const aspectType of publishing.aspects) {
232
+ const parts = aspectType.split('.');
233
+ if (parts.length !== 3) {
234
+ throw new Error(`Manifest error: Invalid Aspect Type '${aspectType}'`);
235
+ }
236
+ if (!snapshot?.aspects?.includes(aspectType)) {
237
+ throw new Error(`Manifest error: Publishing aspect type '${aspectType}' is not listed in snapshot aspects.`);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ return new CatalogManifest(source, snapshot, publishing, layout);
243
+ }
244
+ save(path) {
245
+ let scope;
246
+ const names = this.source.name.split(',');
247
+ if (names.length > 1) {
248
+ scope = names.map(n => `${this.source.type}.${n}`);
249
+ }
250
+ else {
251
+ scope = `${this.source.type}.${this.source.name}`;
252
+ }
253
+ const data = {
254
+ scope: scope,
255
+ layout: this.layout ?? undefined,
256
+ snapshot: this.snapshotConfig ?? undefined,
257
+ publishing: this.publishingConfig ?? undefined
258
+ };
259
+ fs.writeFileSync(path, yaml.stringify(data), 'utf8');
260
+ }
261
+ }
262
+ exports.CatalogManifest = CatalogManifest;
@@ -0,0 +1,25 @@
1
+ export declare const OVERVIEW_ASPECT = "dataplex-types.global.overview";
2
+ export interface Aspect {
3
+ [key: string]: any;
4
+ }
5
+ export interface Entry {
6
+ name: string;
7
+ type: string;
8
+ resource: {
9
+ name?: string;
10
+ system?: string;
11
+ platform?: string;
12
+ displayName?: string;
13
+ description?: string;
14
+ labels?: Record<string, string>;
15
+ location?: string;
16
+ parent?: string;
17
+ ancestors?: {
18
+ name: string;
19
+ type: string;
20
+ }[];
21
+ createTime?: string;
22
+ updateTime?: string;
23
+ };
24
+ aspects?: Record<string, Aspect>;
25
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ // Defines metadata objects provided by the catalog snapshot
3
+ //
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.OVERVIEW_ASPECT = void 0;
6
+ // The aspect that holds an entry's overview text, which the documents layout
7
+ // writes as a document's body.
8
+ exports.OVERVIEW_ASPECT = 'dataplex-types.global.overview';
@@ -0,0 +1,14 @@
1
+ import * as dataplex from '../gcp/dataplex';
2
+ export declare const RESIDUE_VERSION = 1;
3
+ export declare const OVERVIEW_ASPECT_KEY = "dataplex-types.global.overview";
4
+ export declare const DOCUMENT_ASPECT_KEY = "dataplex-types.global.document";
5
+ export declare const QUERY_TEMPLATE_ASPECT_KEY = "dataplex-types.global.query-template";
6
+ export declare const DEFAULT_DOCUMENT_ENTRY_TYPE = "projects/dataplex-types/locations/global/entryTypes/document";
7
+ export declare const QUERY_TEMPLATE_ENTRY_TYPE = "projects/dataplex-types/locations/global/entryTypes/query-template";
8
+ export declare class ResidueError extends Error {
9
+ }
10
+ /** The aspect whose `type` field becomes the concept's OKF `type`. */
11
+ export declare function typeFacetKey(entryType?: string): string | undefined;
12
+ /** Collects what OKF cannot express. Undefined when there is nothing. */
13
+ export declare function buildResidue(entry: dataplex.Entry, overviewAspectKey: string, body?: string): Record<string, unknown> | undefined;
14
+ export declare function assertResidueOnly(frontmatter: Record<string, unknown>): void;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ // Catalog fields OKF has no key for stored in the `x-kcmd` frontmatter block
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ResidueError = exports.QUERY_TEMPLATE_ENTRY_TYPE = exports.DEFAULT_DOCUMENT_ENTRY_TYPE = exports.QUERY_TEMPLATE_ASPECT_KEY = exports.DOCUMENT_ASPECT_KEY = exports.OVERVIEW_ASPECT_KEY = exports.RESIDUE_VERSION = void 0;
5
+ exports.typeFacetKey = typeFacetKey;
6
+ exports.buildResidue = buildResidue;
7
+ exports.assertResidueOnly = assertResidueOnly;
8
+ const concept_1 = require("./concept");
9
+ exports.RESIDUE_VERSION = 1;
10
+ exports.OVERVIEW_ASPECT_KEY = 'dataplex-types.global.overview';
11
+ exports.DOCUMENT_ASPECT_KEY = 'dataplex-types.global.document';
12
+ exports.QUERY_TEMPLATE_ASPECT_KEY = 'dataplex-types.global.query-template';
13
+ exports.DEFAULT_DOCUMENT_ENTRY_TYPE = 'projects/dataplex-types/locations/global/entryTypes/document';
14
+ exports.QUERY_TEMPLATE_ENTRY_TYPE = 'projects/dataplex-types/locations/global/entryTypes/query-template';
15
+ class ResidueError extends Error {
16
+ }
17
+ exports.ResidueError = ResidueError;
18
+ const NATIVE_PATHS = [
19
+ ['entrySource.displayName', 'title'],
20
+ ['entrySource.description', 'description'],
21
+ ['entrySource.resource', 'resource'],
22
+ ];
23
+ /** The aspect whose `type` field becomes the concept's OKF `type`. */
24
+ function typeFacetKey(entryType) {
25
+ const short = entryType?.split('/').pop();
26
+ return short ? `dataplex-types.global.${short}` : undefined;
27
+ }
28
+ function liftedType(data) {
29
+ return typeof data?.['type'] === 'string' && data['type'] !== '';
30
+ }
31
+ /** Collects what OKF cannot express. Undefined when there is nothing. */
32
+ function buildResidue(entry, overviewAspectKey, body) {
33
+ const residue = {};
34
+ if (entry.entryType)
35
+ residue['entryType'] = entry.entryType;
36
+ if (entry.parentEntry)
37
+ residue['parentEntry'] = entry.parentEntry;
38
+ const source = entry.entrySource ?? {};
39
+ const carried = {};
40
+ if (source.system)
41
+ carried['system'] = source.system;
42
+ if (source.platform)
43
+ carried['platform'] = source.platform;
44
+ if (source.location)
45
+ carried['location'] = source.location;
46
+ if (source.ancestors?.length)
47
+ carried['ancestors'] = source.ancestors;
48
+ if (source.labels && Object.keys(source.labels).length) {
49
+ carried['labels'] = source.labels;
50
+ }
51
+ if (Object.keys(carried).length)
52
+ residue['entrySource'] = carried;
53
+ // An OKF file always ends in a newline, so a body that does not must say so.
54
+ // This is the body as written to disk, after any trailing section is split
55
+ // off, since that is what push has to reconstruct.
56
+ const content = body ?? entry.aspects?.[overviewAspectKey]?.data?.['content'];
57
+ if (typeof content === 'string' && content && !content.endsWith('\n')) {
58
+ residue['trailingNewline'] = false;
59
+ }
60
+ const facetKey = typeFacetKey(entry.entryType);
61
+ const aspects = {};
62
+ for (const [key, aspect] of Object.entries(entry.aspects ?? {})) {
63
+ if (key === overviewAspectKey)
64
+ continue;
65
+ if (key === facetKey && liftedType(aspect.data)) {
66
+ const { type, ...rest } = aspect.data;
67
+ aspects[key] = rest;
68
+ continue;
69
+ }
70
+ aspects[key] = aspect.data;
71
+ }
72
+ if (Object.keys(aspects).length)
73
+ residue['aspects'] = aspects;
74
+ if (!Object.keys(residue).length)
75
+ return undefined;
76
+ return { v: exports.RESIDUE_VERSION, ...residue };
77
+ }
78
+ function at(obj, dotted) {
79
+ let cur = obj;
80
+ for (const part of dotted.split('.')) {
81
+ if (cur === null || typeof cur !== 'object')
82
+ return undefined;
83
+ cur = cur[part];
84
+ }
85
+ return cur;
86
+ }
87
+ function assertResidueOnly(frontmatter) {
88
+ const rawExtra = frontmatter[concept_1.EXTRA_KEY];
89
+ if (rawExtra !== undefined &&
90
+ rawExtra !== null &&
91
+ (typeof rawExtra !== 'object' || Array.isArray(rawExtra))) {
92
+ throw new ResidueError(`${concept_1.EXTRA_KEY} must be a mapping`);
93
+ }
94
+ const raw = frontmatter[concept_1.RESIDUE_KEY];
95
+ if (raw === undefined || raw === null)
96
+ return;
97
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
98
+ throw new ResidueError(`${concept_1.RESIDUE_KEY} must be a mapping`);
99
+ }
100
+ const residue = raw;
101
+ const found = [];
102
+ for (const key of concept_1.KEY_ORDER) {
103
+ if (residue[key] !== undefined)
104
+ found.push(`${concept_1.RESIDUE_KEY}.${key} (${key})`);
105
+ }
106
+ for (const [dotted, native] of NATIVE_PATHS) {
107
+ if (at(residue, dotted) !== undefined) {
108
+ found.push(`${concept_1.RESIDUE_KEY}.${dotted} (${native})`);
109
+ }
110
+ }
111
+ const entryType = residue['entryType'];
112
+ const facetKey = typeFacetKey(typeof entryType === 'string' ? entryType : undefined);
113
+ const facet = facetKey
114
+ ? at(residue, 'aspects')?.[facetKey]
115
+ : undefined;
116
+ if (facet &&
117
+ typeof facet === 'object' &&
118
+ facet['type'] !== undefined) {
119
+ found.push(`${concept_1.RESIDUE_KEY}.aspects.${facetKey}.type (type)`);
120
+ }
121
+ if (found.length) {
122
+ throw new ResidueError(`residue duplicates native keys: ${found.join(', ')}`);
123
+ }
124
+ }
@@ -0,0 +1,14 @@
1
+ /** One concept. The `id` is its path in the bundle, without `.md`. */
2
+ export interface Concept {
3
+ id: string;
4
+ frontmatter: Record<string, unknown>;
5
+ body: string;
6
+ }
7
+ export declare const RESIDUE_KEY = "x-kcmd";
8
+ export declare const EXTRA_KEY = "extra";
9
+ export declare const ENTRY_SOURCE_KEYS: readonly ["title", "description", "resource"];
10
+ export declare const OKF_METADATA_KEYS: readonly ["tags", "status", "stale_after", "generated", "verified", "sources", "usage_window"];
11
+ export declare const KEY_ORDER: readonly string[];
12
+ export declare function fileContentToConcept(text: string, id: string): Concept;
13
+ /** Renders a concept back into file content. */
14
+ export declare function conceptToFileContent(concept: Concept): string;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ // An OKF concept: one markdown file with a YAML frontmatter block
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.KEY_ORDER = exports.OKF_METADATA_KEYS = exports.ENTRY_SOURCE_KEYS = exports.EXTRA_KEY = exports.RESIDUE_KEY = void 0;
38
+ exports.fileContentToConcept = fileContentToConcept;
39
+ exports.conceptToFileContent = conceptToFileContent;
40
+ const yaml = __importStar(require("yaml"));
41
+ exports.RESIDUE_KEY = 'x-kcmd';
42
+ exports.EXTRA_KEY = 'extra';
43
+ exports.ENTRY_SOURCE_KEYS = ['title', 'description', 'resource'];
44
+ exports.OKF_METADATA_KEYS = [
45
+ 'tags',
46
+ 'status',
47
+ 'stale_after',
48
+ 'generated',
49
+ 'verified',
50
+ 'sources',
51
+ 'usage_window',
52
+ ];
53
+ // Anything not listed keeps its insertion order and follows (SPEC §4.1).
54
+ exports.KEY_ORDER = [
55
+ 'type',
56
+ ...exports.ENTRY_SOURCE_KEYS,
57
+ ...exports.OKF_METADATA_KEYS,
58
+ ];
59
+ function fileContentToConcept(text, id) {
60
+ const lines = text.split(/\r?\n/);
61
+ const end = lines[0] === '---' ? lines.indexOf('---', 1) : -1;
62
+ if (end === -1) {
63
+ return { id, frontmatter: {}, body: text };
64
+ }
65
+ let parsed;
66
+ try {
67
+ parsed = yaml.parse(lines.slice(1, end).join('\n'));
68
+ }
69
+ catch (e) {
70
+ throw new Error(`${id}.md: invalid frontmatter: ${e instanceof Error ? e.message : e}`);
71
+ }
72
+ const frontmatter = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
73
+ ? parsed
74
+ : {};
75
+ return { id, frontmatter, body: lines.slice(end + 1).join('\n') };
76
+ }
77
+ /** Renders a concept back into file content. */
78
+ function conceptToFileContent(concept) {
79
+ const { frontmatter, body } = concept;
80
+ const ordered = {};
81
+ for (const key of exports.KEY_ORDER) {
82
+ if (key in frontmatter && frontmatter[key] !== undefined) {
83
+ ordered[key] = frontmatter[key];
84
+ }
85
+ }
86
+ for (const [key, value] of Object.entries(frontmatter)) {
87
+ if (key === exports.RESIDUE_KEY ||
88
+ key === exports.EXTRA_KEY ||
89
+ key in ordered ||
90
+ value === undefined) {
91
+ continue;
92
+ }
93
+ ordered[key] = value;
94
+ }
95
+ if (frontmatter[exports.EXTRA_KEY] !== undefined) {
96
+ ordered[exports.EXTRA_KEY] = frontmatter[exports.EXTRA_KEY];
97
+ }
98
+ if (frontmatter[exports.RESIDUE_KEY] !== undefined) {
99
+ ordered[exports.RESIDUE_KEY] = frontmatter[exports.RESIDUE_KEY];
100
+ }
101
+ const block = yaml.stringify(ordered).trimEnd();
102
+ const text = body.endsWith('\n') || body === '' ? body : `${body}\n`;
103
+ return `---\n${block}\n---\n${text}`;
104
+ }
@@ -0,0 +1,8 @@
1
+ import * as dataplex from '../gcp/dataplex';
2
+ import { Concept } from './concept';
3
+ export declare class ConceptError extends Error {
4
+ }
5
+ /** Maps OKF human-readable `type` labels to Dataplex `document.type` slugs. */
6
+ export declare function normalizeOkfDocType(rawType: string): string;
7
+ export declare function entryToConcept(entry: dataplex.Entry, id: string): Concept;
8
+ export declare function conceptToEntry(concept: Concept, entryGroup: string): dataplex.Entry;