@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,63 @@
1
+ import * as api from './api';
2
+ import * as context from './context';
3
+ export interface Operation {
4
+ name?: string;
5
+ done?: boolean;
6
+ error?: {
7
+ code?: number;
8
+ message?: string;
9
+ [key: string]: any;
10
+ };
11
+ response?: {
12
+ [key: string]: any;
13
+ };
14
+ metadata?: {
15
+ [key: string]: any;
16
+ };
17
+ [key: string]: any;
18
+ }
19
+ export interface Session {
20
+ name?: string;
21
+ [key: string]: any;
22
+ }
23
+ export interface ResultSet {
24
+ stats?: {
25
+ queryPlan?: {
26
+ planNodes?: Array<{
27
+ [key: string]: any;
28
+ }>;
29
+ };
30
+ };
31
+ [key: string]: any;
32
+ }
33
+ export interface SpannerType {
34
+ code: string;
35
+ }
36
+ export interface Instance {
37
+ name?: string;
38
+ config?: string;
39
+ [key: string]: any;
40
+ }
41
+ export interface InstanceConfig {
42
+ name?: string;
43
+ replicas?: Array<{
44
+ location?: string;
45
+ type?: string;
46
+ defaultLeaderLocation?: boolean;
47
+ }>;
48
+ [key: string]: any;
49
+ }
50
+ export declare class SpannerClient extends api.ApiClient {
51
+ constructor(ctx: context.ApiContext);
52
+ updateDatabaseDdl(project: string, instance: string, database: string, statements: string[]): Promise<api.ApiResult<Operation>>;
53
+ getDatabase(project: string, instance: string, database: string): Promise<api.ApiResult<{
54
+ name?: string;
55
+ [key: string]: any;
56
+ }>>;
57
+ getInstance(project: string, instance: string): Promise<api.ApiResult<Instance>>;
58
+ getInstanceConfig(configName: string): Promise<api.ApiResult<InstanceConfig>>;
59
+ getOperation(operationName: string): Promise<api.ApiResult<Operation>>;
60
+ createSession(project: string, instance: string, database: string): Promise<api.ApiResult<Session>>;
61
+ deleteSession(sessionName: string): Promise<api.ApiResult<{}>>;
62
+ planDml(sessionName: string, sql: string, params?: Record<string, null>, paramTypes?: Record<string, SpannerType>): Promise<api.ApiResult<ResultSet>>;
63
+ }
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ // API client for Cloud Spanner, covering three surfaces kcmd needs: Database
3
+ // Admin (updateDatabaseDdl, getDatabase and getOperation) to check the
4
+ // database and deploy Spanner Graph DDL; the data plane (sessions and
5
+ // executeSql) to PLAN an action's DML against the live database before
6
+ // publishing it; and Instance Admin (getInstance and getInstanceConfig) to
7
+ // find the region where Knowledge Catalog keeps a table's entry.
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.SpannerClient = void 0;
43
+ const api = __importStar(require("./api"));
44
+ class SpannerClient extends api.ApiClient {
45
+ constructor(ctx) {
46
+ super('https://spanner.googleapis.com', 'v1', ctx);
47
+ }
48
+ // Applies DDL statements to a database. This is asynchronous: the response is
49
+ // a long-running Operation whose `name` the caller polls with getOperation
50
+ // until `done`. Statements are applied in order. The REST binding for
51
+ // updateDatabaseDdl is PATCH on the `.../ddl` collection (a POST 404s --
52
+ // verified live), so this uses PATCH, not the more common POST-to-create.
53
+ async updateDatabaseDdl(project, instance, database, statements) {
54
+ const name = `projects/${project}/instances/${instance}/databases/${database}/ddl`;
55
+ return await this._patch(name, { statements });
56
+ }
57
+ // Fetches a database's metadata.
58
+ async getDatabase(project, instance, database) {
59
+ return await this._get(`projects/${project}/instances/${instance}/databases/${database}`);
60
+ }
61
+ // Fetches an instance's metadata, which names its instance config.
62
+ async getInstance(project, instance) {
63
+ return await this._get(`projects/${project}/instances/${instance}`);
64
+ }
65
+ // Fetches an instance config by its full name, as Instance.config gives it.
66
+ async getInstanceConfig(configName) {
67
+ return await this._get(configName);
68
+ }
69
+ // Fetches a long-running operation by its resource name (as returned in
70
+ // Operation.name, e.g.
71
+ // `projects/.../instances/.../databases/.../operations/...`).
72
+ async getOperation(operationName) {
73
+ return await this._get(operationName);
74
+ }
75
+ // Opens a data-plane session on a database. Every executeSql call runs in
76
+ // one; the caller is responsible for deleting it when done.
77
+ async createSession(project, instance, database) {
78
+ const parent = `projects/${project}/instances/${instance}/databases/${database}/sessions`;
79
+ return await this._post(parent, {});
80
+ }
81
+ // Closes a session by its full resource name. Deleting a session discards
82
+ // any transaction still open on it, which is how a PLAN-only caller disposes
83
+ // of the read-write transactions it began and never committed.
84
+ async deleteSession(sessionName) {
85
+ return await this._delete(sessionName);
86
+ }
87
+ // Plans a DML statement WITHOUT executing it, and without writing anything.
88
+ //
89
+ // Two parts of the request are load-bearing and were both established live
90
+ // against a real database:
91
+ //
92
+ // - `queryMode: PLAN` asks Spanner to parse, resolve every table and
93
+ // column, and type-check the statement, then return the query plan
94
+ // instead of running it. A `Table not found`, an `Unrecognized name`
95
+ // (with Spanner's own "Did you mean ...?"), a bad assignment type and a
96
+ // wrong-dialect construct all come back here as an error.
97
+ // - `transaction.begin.readWrite` is NOT optional. Spanner refuses DML
98
+ // outside a read-write transaction even in PLAN mode
99
+ // ("DML statements can only be performed in a read-write transaction"),
100
+ // so the request begins one. Because PLAN executes nothing, the
101
+ // transaction is never committed and the caller drops it by deleting the
102
+ // session.
103
+ //
104
+ // `params`/`paramTypes` are optional: Spanner infers a `@name` reference's
105
+ // type from its context when neither is supplied. Passing them turns the
106
+ // action's DECLARED parameter types into a claim checked against the real
107
+ // column types, which is the reason to bother. A parameter named in `params`
108
+ // must also appear in `paramTypes` -- a null with no declared type is the one
109
+ // combination Spanner cannot infer.
110
+ async planDml(sessionName, sql, params, paramTypes) {
111
+ const body = {
112
+ sql,
113
+ queryMode: 'PLAN',
114
+ transaction: { begin: { readWrite: {} } },
115
+ };
116
+ if (params && Object.keys(params).length) {
117
+ body.params = params;
118
+ body.paramTypes = paramTypes;
119
+ }
120
+ return await this._post(`${sessionName}:executeSql`, body);
121
+ }
122
+ }
123
+ exports.SpannerClient = SpannerClient;
@@ -0,0 +1,7 @@
1
+ export * from './layout';
2
+ export * from './manifest';
3
+ export * from './metadata';
4
+ export * from './snapshot';
5
+ export * from './sync';
6
+ export * as gcp from './gcp/context';
7
+ export * as dataplex from './gcp/dataplex';
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.dataplex = exports.gcp = void 0;
40
+ __exportStar(require("./layout"), exports);
41
+ __exportStar(require("./manifest"), exports);
42
+ __exportStar(require("./metadata"), exports);
43
+ __exportStar(require("./snapshot"), exports);
44
+ __exportStar(require("./sync"), exports);
45
+ exports.gcp = __importStar(require("./gcp/context"));
46
+ exports.dataplex = __importStar(require("./gcp/dataplex"));
@@ -0,0 +1,16 @@
1
+ import * as md from './metadata';
2
+ export declare enum Layouts {
3
+ STANDARD = "standard",
4
+ DOCUMENTS = "documents",
5
+ SEMANTIC_MODEL = "semantic-model",
6
+ OKF = "okf"
7
+ }
8
+ export interface CatalogLayout {
9
+ init(): Promise<void>;
10
+ entryExists(name: string): boolean;
11
+ listEntries(): string[];
12
+ loadEntry(name: string): Promise<md.Entry>;
13
+ saveEntry(name: string, entry: md.Entry): Promise<void>;
14
+ deleteEntry(name: string): Promise<void>;
15
+ }
16
+ export declare function createLayout(layout: Layouts, catalogPath: string, entryGroup?: string): CatalogLayout;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ // Defines the Catalog metadata layout abstraction.
3
+ //
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.Layouts = void 0;
6
+ exports.createLayout = createLayout;
7
+ const standard_1 = require("./layouts/standard");
8
+ const documents_1 = require("./layouts/documents");
9
+ const semantic_model_1 = require("./layouts/semantic-model");
10
+ const okf_1 = require("./layouts/okf");
11
+ var Layouts;
12
+ (function (Layouts) {
13
+ Layouts["STANDARD"] = "standard";
14
+ Layouts["DOCUMENTS"] = "documents";
15
+ Layouts["SEMANTIC_MODEL"] = "semantic-model";
16
+ Layouts["OKF"] = "okf";
17
+ })(Layouts || (exports.Layouts = Layouts = {}));
18
+ function createLayout(layout, catalogPath, entryGroup) {
19
+ switch (layout) {
20
+ case Layouts.STANDARD:
21
+ return new standard_1.StandardLayout(catalogPath);
22
+ case Layouts.DOCUMENTS:
23
+ return new documents_1.DocumentsLayout(catalogPath);
24
+ case Layouts.SEMANTIC_MODEL:
25
+ return new semantic_model_1.SemanticModelLayout(catalogPath, entryGroup);
26
+ case Layouts.OKF:
27
+ return new okf_1.OkfLayout(catalogPath, entryGroup);
28
+ default:
29
+ throw new Error(`Unknown layout type: ${layout}`);
30
+ }
31
+ }
@@ -0,0 +1,24 @@
1
+ import * as md from '../metadata';
2
+ import { CatalogLayout } from '../layout';
3
+ export declare class DocumentsLayout implements CatalogLayout {
4
+ private _catalogPath;
5
+ private readonly _index;
6
+ constructor(catalogPath: string);
7
+ init(): Promise<void>;
8
+ entryExists(name: string): boolean;
9
+ listEntries(): string[];
10
+ loadEntry(name: string): Promise<md.Entry>;
11
+ saveEntry(name: string, entry: md.Entry): Promise<void>;
12
+ deleteEntry(name: string): Promise<void>;
13
+ private _synthesizeIndexEntries;
14
+ }
15
+ export declare function splitFrontmatter(content: string): {
16
+ metadata?: any;
17
+ body: string;
18
+ };
19
+ export declare function joinFrontmatter(metadata: object, body: string): string;
20
+ export declare function parseMarkdown(content: string): {
21
+ entry: md.Entry | null;
22
+ body: string;
23
+ };
24
+ export declare function toMarkdown(entry: md.Entry, body: string): string;
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ // Implements the documents layout (markdown 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.DocumentsLayout = void 0;
39
+ exports.splitFrontmatter = splitFrontmatter;
40
+ exports.joinFrontmatter = joinFrontmatter;
41
+ exports.parseMarkdown = parseMarkdown;
42
+ exports.toMarkdown = toMarkdown;
43
+ const fs = __importStar(require("node:fs"));
44
+ const glob = __importStar(require("glob"));
45
+ const path = __importStar(require("node:path"));
46
+ const yaml = __importStar(require("yaml"));
47
+ const md = __importStar(require("../metadata"));
48
+ const DEFAULT_ENTRY_TYPE = 'dataplex-types.global.generic';
49
+ const INDEX_NAME = 'index';
50
+ class DocumentsLayout {
51
+ _catalogPath = '';
52
+ // Maps entry name -> local file path, or null for synthetic directory-index
53
+ // entries that don't have a backing file on disk.
54
+ _index = new Map();
55
+ constructor(catalogPath) {
56
+ this._catalogPath = catalogPath;
57
+ }
58
+ async init() {
59
+ this._index.clear();
60
+ if (!fs.existsSync(this._catalogPath)) {
61
+ return;
62
+ }
63
+ const matches = await glob.glob('**/*.md', {
64
+ cwd: this._catalogPath,
65
+ absolute: true,
66
+ nodir: true,
67
+ });
68
+ for (const localPath of matches) {
69
+ const name = deriveEntryNameFromPath(localPath, this._catalogPath);
70
+ this._index.set(name, localPath);
71
+ }
72
+ this._synthesizeIndexEntries();
73
+ }
74
+ entryExists(name) {
75
+ return this._index.has(name);
76
+ }
77
+ listEntries() {
78
+ return Array.from(this._index.keys());
79
+ }
80
+ async loadEntry(name) {
81
+ if (!this._index.has(name)) {
82
+ throw new Error(`Entry not found: ${name}`);
83
+ }
84
+ const entryPath = this._index.get(name) ?? null;
85
+ let parsed = null;
86
+ let body = '';
87
+ if (entryPath) {
88
+ const content = await fs.promises.readFile(entryPath, 'utf8');
89
+ const result = parseMarkdown(content);
90
+ parsed = result.entry;
91
+ body = result.body;
92
+ }
93
+ const entry = parsed ?? { type: DEFAULT_ENTRY_TYPE, resource: {} };
94
+ entry.name = name;
95
+ entry.resource = entry.resource ?? {};
96
+ const parentName = deriveParentLocalName(name);
97
+ if (parentName !== undefined) {
98
+ entry.resource.parent = parentName;
99
+ }
100
+ else {
101
+ delete entry.resource.parent;
102
+ }
103
+ // Ensure the entry's type aspect is present — Dataplex create requires it.
104
+ entry.aspects = entry.aspects ?? {};
105
+ entry.aspects[entry.type] = entry.aspects[entry.type] ?? {};
106
+ const bodyTrimmed = body.trim();
107
+ if (bodyTrimmed) {
108
+ if (!entry.aspects[md.OVERVIEW_ASPECT]) {
109
+ entry.aspects[md.OVERVIEW_ASPECT] = {};
110
+ }
111
+ entry.aspects[md.OVERVIEW_ASPECT].content = bodyTrimmed;
112
+ entry.aspects[md.OVERVIEW_ASPECT].contentType ??= 'MARKDOWN';
113
+ }
114
+ return entry;
115
+ }
116
+ async saveEntry(name, entry) {
117
+ const entryPath = path.join(this._catalogPath, `${name}.md`);
118
+ await fs.promises.mkdir(path.dirname(entryPath), { recursive: true });
119
+ // Clone to avoid mutating original entry aspects
120
+ const clonedEntry = JSON.parse(JSON.stringify(entry));
121
+ let body = '';
122
+ if (clonedEntry.aspects?.[md.OVERVIEW_ASPECT]) {
123
+ const aspect = clonedEntry.aspects[md.OVERVIEW_ASPECT];
124
+ if (aspect.content !== undefined) {
125
+ body = aspect.content;
126
+ delete aspect.content;
127
+ // Any other content type stays in the front matter, so a push
128
+ // keeps it.
129
+ if (aspect.contentType === 'MARKDOWN')
130
+ delete aspect.contentType;
131
+ }
132
+ }
133
+ const fileContent = toMarkdown(clonedEntry, body);
134
+ await fs.promises.writeFile(entryPath, fileContent, 'utf8');
135
+ this._index.set(name, entryPath);
136
+ this._synthesizeIndexEntries();
137
+ }
138
+ async deleteEntry(name) {
139
+ const entryPath = this._index.get(name);
140
+ if (!entryPath) {
141
+ throw new Error(`Entry not found: ${name}`);
142
+ }
143
+ if (!fs.existsSync(entryPath)) {
144
+ throw new Error(`Entry not found: ${name}`);
145
+ }
146
+ await fs.promises.unlink(entryPath);
147
+ this._index.delete(name);
148
+ this._synthesizeIndexEntries();
149
+ }
150
+ // Ensure every directory that contains entries has an index entry. Directories
151
+ // whose own index file is absent get a synthetic entry (no backing file).
152
+ // Synthetic entries left behind by a previous synthesis pass are dropped if
153
+ // their directory no longer has any other entries.
154
+ _synthesizeIndexEntries() {
155
+ for (const [name, entryPath] of this._index) {
156
+ if (entryPath === null) {
157
+ this._index.delete(name);
158
+ }
159
+ }
160
+ const dirs = new Set();
161
+ for (const name of this._index.keys()) {
162
+ const segments = name.split('/');
163
+ for (let i = 0; i < segments.length; i++) {
164
+ dirs.add(segments.slice(0, i).join('/'));
165
+ }
166
+ }
167
+ for (const dir of dirs) {
168
+ const indexName = dir ? `${dir}/${INDEX_NAME}` : INDEX_NAME;
169
+ if (!this._index.has(indexName)) {
170
+ this._index.set(indexName, null);
171
+ }
172
+ }
173
+ }
174
+ }
175
+ exports.DocumentsLayout = DocumentsLayout;
176
+ function deriveEntryNameFromPath(absolutePath, catalogPath) {
177
+ const rel = path.relative(catalogPath, absolutePath);
178
+ return rel.replace(/\.md$/, '');
179
+ }
180
+ function deriveParentLocalName(name) {
181
+ const segments = name.split('/');
182
+ const leaf = segments[segments.length - 1];
183
+ // The root index entry has no parent.
184
+ if (leaf === INDEX_NAME && segments.length === 1) {
185
+ return undefined;
186
+ }
187
+ const parentDir = leaf === INDEX_NAME
188
+ ? segments.slice(0, -2)
189
+ : segments.slice(0, -1);
190
+ return [...parentDir, INDEX_NAME].join('/');
191
+ }
192
+ // Splits markdown text into its YAML frontmatter, parsed, and its body.
193
+ // `metadata` is undefined when the text does not open with a `---` block.
194
+ function splitFrontmatter(content) {
195
+ const lines = content.split(/\r?\n/);
196
+ const endIndex = lines[0] === '---' ? lines.indexOf('---', 1) : -1;
197
+ if (endIndex === -1)
198
+ return { body: content };
199
+ return {
200
+ metadata: yaml.parse(lines.slice(1, endIndex).join('\n')),
201
+ body: lines.slice(endIndex + 1).join('\n'),
202
+ };
203
+ }
204
+ // Markdown text with `metadata` as its YAML frontmatter and `body` after it.
205
+ function joinFrontmatter(metadata, body) {
206
+ return `---\n${yaml.stringify(metadata).trim()}\n---\n${body}`;
207
+ }
208
+ function parseMarkdown(content) {
209
+ const { metadata, body } = splitFrontmatter(content);
210
+ if (metadata === undefined) {
211
+ return { entry: null, body };
212
+ }
213
+ const entry = (metadata.catalogEntry ?? {});
214
+ entry.type = (typeof metadata.type === 'string' && metadata.type.split('.').length === 3)
215
+ ? metadata.type
216
+ : DEFAULT_ENTRY_TYPE;
217
+ entry.resource = entry.resource ?? {};
218
+ entry.resource.displayName = metadata.title;
219
+ entry.resource.description = metadata.description;
220
+ if (metadata.tags) {
221
+ entry.resource.labels = entry.resource.labels ?? {};
222
+ for (const tag of metadata.tags) {
223
+ entry.resource.labels[tag] = 'true';
224
+ }
225
+ }
226
+ if (metadata.timeStamp) {
227
+ entry.resource.updateTime = metadata.timeStamp;
228
+ if (!entry.resource.createTime) {
229
+ entry.resource.createTime = metadata.timeStamp;
230
+ }
231
+ }
232
+ return { entry, body };
233
+ }
234
+ function toMarkdown(entry, body) {
235
+ // Clone to be able to make modifications
236
+ const entryClone = JSON.parse(JSON.stringify(entry));
237
+ const tags = [];
238
+ if (entry.resource.labels) {
239
+ for (const [k, v] of Object.entries(entryClone.resource.labels ?? {})) {
240
+ if (v == 'true') {
241
+ tags.push(k);
242
+ }
243
+ }
244
+ }
245
+ const metadata = {
246
+ type: entry.type,
247
+ title: entry.resource.displayName ?? entry.resource.name,
248
+ description: entry.resource.description ?? undefined,
249
+ tags: tags.length ? tags : undefined,
250
+ timeStamp: entry.resource.updateTime ?? entry.resource.createTime ?? undefined,
251
+ catalogEntry: entryClone
252
+ };
253
+ delete entryClone.name;
254
+ delete entryClone.resource.displayName;
255
+ delete entryClone.resource.description;
256
+ delete entryClone.resource.updateTime;
257
+ delete entryClone.resource.createTime;
258
+ delete entryClone.resource.parent;
259
+ delete entryClone.type;
260
+ for (const tag of tags) {
261
+ delete entryClone.resource.labels[tag];
262
+ }
263
+ return joinFrontmatter(metadata, body);
264
+ }
@@ -0,0 +1,31 @@
1
+ import * as dataplex from '../gcp/dataplex';
2
+ import { CatalogLayout } from '../layout';
3
+ import * as md from '../metadata';
4
+ import { Concept } from '../okf/concept';
5
+ import { BundleLinksResult } from '../okf/links';
6
+ export declare const BUNDLE_DIR = "bundle";
7
+ export declare class OkfLayout implements CatalogLayout {
8
+ private readonly _bundlePath;
9
+ private readonly _container;
10
+ private readonly _index;
11
+ constructor(bundlePath: string, entryGroupContainer?: string);
12
+ get bundlePath(): string;
13
+ bundleExists(): boolean;
14
+ /** Resolves `<bundle>/<id>.md`, rejecting any ID that escapes `bundle/`. */
15
+ resolveConceptPath(id: string): string;
16
+ /** Display path relative to workspace root (e.g. `bundle/tables/trips.md`). */
17
+ relativeBundlePath(name: string): string;
18
+ init(): Promise<void>;
19
+ entryExists(name: string): boolean;
20
+ listEntries(): string[];
21
+ loadEntry(name: string): Promise<md.Entry>;
22
+ /** Validates and builds the `Concept` `saveEntry` would write, without I/O. */
23
+ previewConcept(name: string, entry: md.Entry): Concept;
24
+ saveEntry(name: string, entry: md.Entry): Promise<void>;
25
+ deleteEntry(name: string): Promise<void>;
26
+ /** Reads `bundle/_links.yaml` plus any frontmatter `links` on indexed concepts. */
27
+ readLinks(): BundleLinksResult;
28
+ /** Writes or removes `bundle/_links.yaml` to match pulled EntryLinks. */
29
+ writeLinks(links: dataplex.EntryLink[]): Promise<void>;
30
+ private _pruneEmptyDirs;
31
+ }