@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.
- package/LICENSE +203 -0
- package/README.md +264 -2
- package/build/ts/kcmd/cli.js +20252 -0
- package/build/ts/kcmd/gcp/alloydb.d.ts +12 -0
- package/build/ts/kcmd/gcp/alloydb.js +70 -0
- package/build/ts/kcmd/gcp/api.d.ts +29 -0
- package/build/ts/kcmd/gcp/api.js +162 -0
- package/build/ts/kcmd/gcp/biglake.d.ts +18 -0
- package/build/ts/kcmd/gcp/biglake.js +59 -0
- package/build/ts/kcmd/gcp/bigquery.d.ts +57 -0
- package/build/ts/kcmd/gcp/bigquery.js +134 -0
- package/build/ts/kcmd/gcp/context.d.ts +10 -0
- package/build/ts/kcmd/gcp/context.js +74 -0
- package/build/ts/kcmd/gcp/crm.d.ts +13 -0
- package/build/ts/kcmd/gcp/crm.js +91 -0
- package/build/ts/kcmd/gcp/dataplex.d.ts +147 -0
- package/build/ts/kcmd/gcp/dataplex.js +406 -0
- package/build/ts/kcmd/gcp/index.d.ts +3 -0
- package/build/ts/kcmd/gcp/index.js +21 -0
- package/build/ts/kcmd/gcp/spanner.d.ts +73 -0
- package/build/ts/kcmd/gcp/spanner.js +126 -0
- package/build/ts/kcmd/index.d.ts +7 -0
- package/build/ts/kcmd/index.js +46 -0
- package/build/ts/kcmd/layout.d.ts +16 -0
- package/build/ts/kcmd/layout.js +31 -0
- package/build/ts/kcmd/layouts/documents.d.ts +24 -0
- package/build/ts/kcmd/layouts/documents.js +264 -0
- package/build/ts/kcmd/layouts/okf.d.ts +31 -0
- package/build/ts/kcmd/layouts/okf.js +386 -0
- package/build/ts/kcmd/layouts/semantic-model.d.ts +74 -0
- package/build/ts/kcmd/layouts/semantic-model.js +621 -0
- package/build/ts/kcmd/layouts/standard.d.ts +13 -0
- package/build/ts/kcmd/layouts/standard.js +108 -0
- package/build/ts/kcmd/manifest.d.ts +34 -0
- package/build/ts/kcmd/manifest.js +262 -0
- package/build/ts/kcmd/metadata.d.ts +25 -0
- package/build/ts/kcmd/metadata.js +8 -0
- package/build/ts/kcmd/okf/catalog_residue.d.ts +14 -0
- package/build/ts/kcmd/okf/catalog_residue.js +124 -0
- package/build/ts/kcmd/okf/concept.d.ts +14 -0
- package/build/ts/kcmd/okf/concept.js +110 -0
- package/build/ts/kcmd/okf/concept_to_entry.d.ts +10 -0
- package/build/ts/kcmd/okf/concept_to_entry.js +281 -0
- package/build/ts/kcmd/okf/init_okf_workspace.d.ts +15 -0
- package/build/ts/kcmd/okf/init_okf_workspace.js +113 -0
- package/build/ts/kcmd/okf/links.d.ts +23 -0
- package/build/ts/kcmd/okf/links.js +468 -0
- package/build/ts/kcmd/okf/okf_metadata.d.ts +12 -0
- package/build/ts/kcmd/okf/okf_metadata.js +99 -0
- package/build/ts/kcmd/semantic/bigquery.d.ts +22 -0
- package/build/ts/kcmd/semantic/bigquery.js +469 -0
- package/build/ts/kcmd/semantic/binding.d.ts +3 -0
- package/build/ts/kcmd/semantic/binding.js +92 -0
- package/build/ts/kcmd/semantic/catalog_entry.d.ts +24 -0
- package/build/ts/kcmd/semantic/catalog_entry.js +35 -0
- package/build/ts/kcmd/semantic/converters/owl/convert.d.ts +26 -0
- package/build/ts/kcmd/semantic/converters/owl/convert.js +41 -0
- package/build/ts/kcmd/semantic/converters/owl/model.d.ts +98 -0
- package/build/ts/kcmd/semantic/converters/owl/model.js +30 -0
- package/build/ts/kcmd/semantic/converters/owl/parse.d.ts +11 -0
- package/build/ts/kcmd/semantic/converters/owl/parse.js +634 -0
- package/build/ts/kcmd/semantic/converters/owl/to_ir.d.ts +19 -0
- package/build/ts/kcmd/semantic/converters/owl/to_ir.js +390 -0
- package/build/ts/kcmd/semantic/deploy_bigquery.d.ts +20 -0
- package/build/ts/kcmd/semantic/deploy_bigquery.js +226 -0
- package/build/ts/kcmd/semantic/deploy_knowledge_catalog.d.ts +104 -0
- package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +1367 -0
- package/build/ts/kcmd/semantic/deploy_spanner.d.ts +15 -0
- package/build/ts/kcmd/semantic/deploy_spanner.js +150 -0
- package/build/ts/kcmd/semantic/deployment_target.d.ts +93 -0
- package/build/ts/kcmd/semantic/deployment_target.js +379 -0
- package/build/ts/kcmd/semantic/graph_ddl.d.ts +44 -0
- package/build/ts/kcmd/semantic/graph_ddl.js +656 -0
- package/build/ts/kcmd/semantic/import_model.d.ts +72 -0
- package/build/ts/kcmd/semantic/import_model.js +1698 -0
- package/build/ts/kcmd/semantic/ir.d.ts +589 -0
- package/build/ts/kcmd/semantic/ir.js +247 -0
- package/build/ts/kcmd/semantic/kc_actions.d.ts +45 -0
- package/build/ts/kcmd/semantic/kc_actions.js +531 -0
- package/build/ts/kcmd/semantic/kc_constraints.d.ts +44 -0
- package/build/ts/kcmd/semantic/kc_constraints.js +279 -0
- package/build/ts/kcmd/semantic/kc_converter.d.ts +37 -0
- package/build/ts/kcmd/semantic/kc_converter.js +839 -0
- package/build/ts/kcmd/semantic/kc_custom_types.d.ts +63 -0
- package/build/ts/kcmd/semantic/kc_custom_types.js +751 -0
- package/build/ts/kcmd/semantic/kc_entries.d.ts +155 -0
- package/build/ts/kcmd/semantic/kc_entries.js +284 -0
- package/build/ts/kcmd/semantic/kc_ids.d.ts +102 -0
- package/build/ts/kcmd/semantic/kc_ids.js +188 -0
- package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +29 -0
- package/build/ts/kcmd/semantic/knowledge_catalog.js +960 -0
- package/build/ts/kcmd/semantic/loader.d.ts +258 -0
- package/build/ts/kcmd/semantic/loader.js +3576 -0
- package/build/ts/kcmd/semantic/names.d.ts +10 -0
- package/build/ts/kcmd/semantic/names.js +85 -0
- package/build/ts/kcmd/semantic/osi_converter.d.ts +60 -0
- package/build/ts/kcmd/semantic/osi_converter.js +910 -0
- package/build/ts/kcmd/semantic/parameters.d.ts +19 -0
- package/build/ts/kcmd/semantic/parameters.js +171 -0
- package/build/ts/kcmd/semantic/pull_kc.d.ts +10 -0
- package/build/ts/kcmd/semantic/pull_kc.js +272 -0
- package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +59 -0
- package/build/ts/kcmd/semantic/resolve_inheritance.js +346 -0
- package/build/ts/kcmd/semantic/resolve_profiles.d.ts +291 -0
- package/build/ts/kcmd/semantic/resolve_profiles.js +2003 -0
- package/build/ts/kcmd/semantic/runtime/agent_tools.d.ts +93 -0
- package/build/ts/kcmd/semantic/runtime/agent_tools.js +243 -0
- package/build/ts/kcmd/semantic/runtime/dialect.d.ts +13 -0
- package/build/ts/kcmd/semantic/runtime/dialect.js +39 -0
- package/build/ts/kcmd/semantic/runtime/runtime.d.ts +88 -0
- package/build/ts/kcmd/semantic/runtime/runtime.js +301 -0
- package/build/ts/kcmd/semantic/runtime/store.d.ts +112 -0
- package/build/ts/kcmd/semantic/runtime/store.js +344 -0
- package/build/ts/kcmd/semantic/skills.d.ts +110 -0
- package/build/ts/kcmd/semantic/skills.js +697 -0
- package/build/ts/kcmd/semantic/source_dialect.d.ts +17 -0
- package/build/ts/kcmd/semantic/source_dialect.js +311 -0
- package/build/ts/kcmd/semantic/source_entries.d.ts +12 -0
- package/build/ts/kcmd/semantic/source_entries.js +341 -0
- package/build/ts/kcmd/semantic/spanner.d.ts +18 -0
- package/build/ts/kcmd/semantic/spanner.js +60 -0
- package/build/ts/kcmd/semantic/sql_expr_utils.d.ts +23 -0
- package/build/ts/kcmd/semantic/sql_expr_utils.js +394 -0
- package/build/ts/kcmd/semantic/sql_identifiers.d.ts +6 -0
- package/build/ts/kcmd/semantic/sql_identifiers.js +267 -0
- package/build/ts/kcmd/semantic/sql_parser.d.ts +47 -0
- package/build/ts/kcmd/semantic/sql_parser.js +400 -0
- package/build/ts/kcmd/semantic/transpile.d.ts +76 -0
- package/build/ts/kcmd/semantic/transpile.js +581 -0
- package/build/ts/kcmd/semantic/validate.d.ts +70 -0
- package/build/ts/kcmd/semantic/validate.js +2266 -0
- package/build/ts/kcmd/semantic/yaml_options.d.ts +6 -0
- package/build/ts/kcmd/semantic/yaml_options.js +58 -0
- package/build/ts/kcmd/semantic/yaml_update.d.ts +26 -0
- package/build/ts/kcmd/semantic/yaml_update.js +782 -0
- package/build/ts/kcmd/snapshot.d.ts +37 -0
- package/build/ts/kcmd/snapshot.js +386 -0
- package/build/ts/kcmd/source.d.ts +31 -0
- package/build/ts/kcmd/source.js +119 -0
- package/build/ts/kcmd/sources/bq-dataset.d.ts +17 -0
- package/build/ts/kcmd/sources/bq-dataset.js +131 -0
- package/build/ts/kcmd/sources/entrygroup.d.ts +21 -0
- package/build/ts/kcmd/sources/entrygroup.js +83 -0
- package/build/ts/kcmd/sources/kb.d.ts +21 -0
- package/build/ts/kcmd/sources/kb.js +82 -0
- package/build/ts/kcmd/sources/semantic-model.d.ts +18 -0
- package/build/ts/kcmd/sources/semantic-model.js +48 -0
- package/build/ts/kcmd/sync.d.ts +35 -0
- package/build/ts/kcmd/sync.js +734 -0
- package/package.json +62 -4
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Implements the standard layout (yaml files in directory)
|
|
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.StandardLayout = void 0;
|
|
39
|
+
const fs = __importStar(require("node:fs"));
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
const glob = __importStar(require("glob"));
|
|
42
|
+
const yaml = __importStar(require("yaml"));
|
|
43
|
+
class StandardLayout {
|
|
44
|
+
_catalogPath;
|
|
45
|
+
_index = new Map();
|
|
46
|
+
constructor(catalogPath) {
|
|
47
|
+
this._catalogPath = catalogPath;
|
|
48
|
+
}
|
|
49
|
+
async init() {
|
|
50
|
+
this._index.clear();
|
|
51
|
+
if (!fs.existsSync(this._catalogPath)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const matches = await glob.glob('**/*.yaml', {
|
|
55
|
+
cwd: this._catalogPath,
|
|
56
|
+
absolute: true,
|
|
57
|
+
nodir: true,
|
|
58
|
+
});
|
|
59
|
+
for (const localPath of matches) {
|
|
60
|
+
try {
|
|
61
|
+
const content = await fs.promises.readFile(localPath, 'utf8');
|
|
62
|
+
const metadata = yaml.parse(content);
|
|
63
|
+
if (metadata && metadata.name) {
|
|
64
|
+
this._index.set(metadata.name, localPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
// A file that fails to read or parse is left out of the index, so it is
|
|
69
|
+
// never listed and never pushed. Warn rather than drop it silently:
|
|
70
|
+
// push otherwise makes the destination match the directory, and a file
|
|
71
|
+
// vanishing from that set is easy to miss.
|
|
72
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
73
|
+
const relPath = path.relative(this._catalogPath, localPath);
|
|
74
|
+
console.warn(`Skipping unreadable catalog file '${relPath}': ${reason}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
entryExists(name) {
|
|
79
|
+
const entryPath = this._index.get(name);
|
|
80
|
+
return !!entryPath && fs.existsSync(entryPath);
|
|
81
|
+
}
|
|
82
|
+
listEntries() {
|
|
83
|
+
return Array.from(this._index.keys());
|
|
84
|
+
}
|
|
85
|
+
async loadEntry(name) {
|
|
86
|
+
const entryPath = this._index.get(name);
|
|
87
|
+
if (!entryPath || !fs.existsSync(entryPath)) {
|
|
88
|
+
throw new Error(`Entry not found: ${name}`);
|
|
89
|
+
}
|
|
90
|
+
const content = await fs.promises.readFile(entryPath, 'utf8');
|
|
91
|
+
return yaml.parse(content);
|
|
92
|
+
}
|
|
93
|
+
async saveEntry(name, entry) {
|
|
94
|
+
const entryPath = path.join(this._catalogPath, `${name}.yaml`);
|
|
95
|
+
await fs.promises.mkdir(path.dirname(entryPath), { recursive: true });
|
|
96
|
+
await fs.promises.writeFile(entryPath, yaml.stringify(entry), 'utf8');
|
|
97
|
+
this._index.set(name, entryPath);
|
|
98
|
+
}
|
|
99
|
+
async deleteEntry(name) {
|
|
100
|
+
const entryPath = this._index.get(name);
|
|
101
|
+
if (!entryPath || !fs.existsSync(entryPath)) {
|
|
102
|
+
throw new Error(`Entry not found: ${name}`);
|
|
103
|
+
}
|
|
104
|
+
await fs.promises.unlink(entryPath);
|
|
105
|
+
this._index.delete(name);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.StandardLayout = StandardLayout;
|
|
@@ -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", "superseded_by", "stale_after", "runtime", "parameters", "computation", "executor", "attester", "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;
|