@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.
- package/LICENSE +203 -0
- package/README.md +266 -2
- package/build/ts/kcmd/cli.js +19578 -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 +16 -0
- package/build/ts/kcmd/gcp/biglake.js +50 -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 +63 -0
- package/build/ts/kcmd/gcp/spanner.js +123 -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 +344 -0
- package/build/ts/kcmd/layouts/semantic-model.d.ts +61 -0
- package/build/ts/kcmd/layouts/semantic-model.js +542 -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 +104 -0
- package/build/ts/kcmd/okf/concept_to_entry.d.ts +8 -0
- package/build/ts/kcmd/okf/concept_to_entry.js +254 -0
- package/build/ts/kcmd/okf/init_okf_workspace.d.ts +15 -0
- package/build/ts/kcmd/okf/init_okf_workspace.js +115 -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 +82 -0
- package/build/ts/kcmd/semantic/bigquery.d.ts +22 -0
- package/build/ts/kcmd/semantic/bigquery.js +525 -0
- package/build/ts/kcmd/semantic/binding.d.ts +3 -0
- package/build/ts/kcmd/semantic/binding.js +97 -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 +129 -0
- package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +1396 -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 +34 -0
- package/build/ts/kcmd/semantic/graph_ddl.js +563 -0
- package/build/ts/kcmd/semantic/import_model.d.ts +50 -0
- package/build/ts/kcmd/semantic/import_model.js +1275 -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 +31 -0
- package/build/ts/kcmd/semantic/kc_converter.js +824 -0
- package/build/ts/kcmd/semantic/kc_custom_types.d.ts +70 -0
- package/build/ts/kcmd/semantic/kc_custom_types.js +828 -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 +103 -0
- package/build/ts/kcmd/semantic/kc_ids.js +191 -0
- package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +32 -0
- package/build/ts/kcmd/semantic/knowledge_catalog.js +1043 -0
- package/build/ts/kcmd/semantic/loader.d.ts +262 -0
- package/build/ts/kcmd/semantic/loader.js +3440 -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 +911 -0
- package/build/ts/kcmd/semantic/parameters.d.ts +18 -0
- package/build/ts/kcmd/semantic/parameters.js +152 -0
- package/build/ts/kcmd/semantic/pull_kc.d.ts +14 -0
- package/build/ts/kcmd/semantic/pull_kc.js +346 -0
- package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +54 -0
- package/build/ts/kcmd/semantic/resolve_inheritance.js +330 -0
- package/build/ts/kcmd/semantic/resolve_profiles.d.ts +250 -0
- package/build/ts/kcmd/semantic/resolve_profiles.js +1822 -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 +94 -0
- package/build/ts/kcmd/semantic/runtime/runtime.js +318 -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 +10 -0
- package/build/ts/kcmd/semantic/source_dialect.js +261 -0
- package/build/ts/kcmd/semantic/source_entries.d.ts +12 -0
- package/build/ts/kcmd/semantic/source_entries.js +282 -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 +17 -0
- package/build/ts/kcmd/semantic/sql_expr_utils.js +221 -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 +44 -0
- package/build/ts/kcmd/semantic/sql_parser.js +378 -0
- package/build/ts/kcmd/semantic/transpile.d.ts +74 -0
- package/build/ts/kcmd/semantic/transpile.js +563 -0
- package/build/ts/kcmd/semantic/validate.d.ts +35 -0
- package/build/ts/kcmd/semantic/validate.js +1899 -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 +33 -0
- package/build/ts/kcmd/snapshot.js +379 -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 +697 -0
- package/package.json +62 -4
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Implements the Open Knowledge Format (OKF) layout (`bundle/<id>.md`).
|
|
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.OkfLayout = exports.BUNDLE_DIR = void 0;
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const dataplex = __importStar(require("../gcp/dataplex"));
|
|
41
|
+
const concept_1 = require("../okf/concept");
|
|
42
|
+
const concept_to_entry_1 = require("../okf/concept_to_entry");
|
|
43
|
+
const links_1 = require("../okf/links");
|
|
44
|
+
exports.BUNDLE_DIR = 'bundle';
|
|
45
|
+
const FALLBACK_CONTAINER = 'projects/_/locations/_/entryGroups/_';
|
|
46
|
+
function mapping(value) {
|
|
47
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
48
|
+
? value
|
|
49
|
+
: {};
|
|
50
|
+
}
|
|
51
|
+
function text(value) {
|
|
52
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
53
|
+
}
|
|
54
|
+
function conceptFiles(dir) {
|
|
55
|
+
return fs
|
|
56
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
57
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
58
|
+
.flatMap((e) => {
|
|
59
|
+
const p = path.join(dir, e.name);
|
|
60
|
+
if (e.isDirectory())
|
|
61
|
+
return conceptFiles(p);
|
|
62
|
+
return e.name.endsWith('.md') ? [p] : [];
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function toPosixId(root, file) {
|
|
66
|
+
return path.relative(root, file).split(path.sep).join('/').replace(/\.md$/, '');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolves the catalog entry ID a concept file maps to, or `undefined` when the
|
|
70
|
+
* file should be skipped (`<group>_entry`, append-only `log.md`, or a generated
|
|
71
|
+
* `index.md` shadowed by an explicit `agents.md` / `<dir>.md`).
|
|
72
|
+
*/
|
|
73
|
+
function resolvedEntryName(id, frontmatter, bundlePath) {
|
|
74
|
+
const residue = mapping(frontmatter[concept_1.RESIDUE_KEY]);
|
|
75
|
+
const extra = mapping(frontmatter[concept_1.EXTRA_KEY]);
|
|
76
|
+
if (text(residue['entryType'])?.endsWith('/entrygroup') ||
|
|
77
|
+
text(extra['type'])?.endsWith('/entrygroup')) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const hasExplicitMeta = frontmatter[concept_1.RESIDUE_KEY] !== undefined ||
|
|
81
|
+
frontmatter[concept_1.EXTRA_KEY] !== undefined ||
|
|
82
|
+
frontmatter['type'] !== undefined;
|
|
83
|
+
// Skip append-only operation logs (`log.md`) that carry no concept frontmatter.
|
|
84
|
+
if (id === 'log' && !hasExplicitMeta) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
// When `agents.md` or `<dir>.md` already exists alongside a directory-listing
|
|
88
|
+
// `index.md` that has no explicit concept metadata, prefer the explicit file.
|
|
89
|
+
if (id === 'index' &&
|
|
90
|
+
!hasExplicitMeta &&
|
|
91
|
+
fs.existsSync(path.join(bundlePath, 'agents.md'))) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
if (id.endsWith('/index') &&
|
|
95
|
+
!hasExplicitMeta &&
|
|
96
|
+
fs.existsSync(path.join(bundlePath, `${id.slice(0, -'/index'.length)}.md`))) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const explicitId = text(residue['entryId']) ?? text(extra['entry_id']);
|
|
100
|
+
if (explicitId)
|
|
101
|
+
return explicitId;
|
|
102
|
+
const hasExplicitType = text(residue['entryType']) !== undefined ||
|
|
103
|
+
text(extra['type']) !== undefined;
|
|
104
|
+
if (!hasExplicitType) {
|
|
105
|
+
if (id === 'index')
|
|
106
|
+
return 'agents';
|
|
107
|
+
if (id.endsWith('/index'))
|
|
108
|
+
return id.slice(0, -'/index'.length);
|
|
109
|
+
}
|
|
110
|
+
return id;
|
|
111
|
+
}
|
|
112
|
+
function toLocalFromService(entry, localName) {
|
|
113
|
+
const aspects = {};
|
|
114
|
+
if (entry.aspects) {
|
|
115
|
+
for (const [key, val] of Object.entries(entry.aspects)) {
|
|
116
|
+
aspects[key] = val.data ?? {};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const src = entry.entrySource ?? {};
|
|
120
|
+
const resource = {};
|
|
121
|
+
if (src.resource !== undefined)
|
|
122
|
+
resource.name = src.resource;
|
|
123
|
+
if (src.system !== undefined)
|
|
124
|
+
resource.system = src.system;
|
|
125
|
+
if (src.platform !== undefined)
|
|
126
|
+
resource.platform = src.platform;
|
|
127
|
+
if (src.displayName !== undefined)
|
|
128
|
+
resource.displayName = src.displayName;
|
|
129
|
+
if (src.description !== undefined)
|
|
130
|
+
resource.description = src.description;
|
|
131
|
+
if (src.labels !== undefined)
|
|
132
|
+
resource.labels = src.labels;
|
|
133
|
+
if (src.location !== undefined)
|
|
134
|
+
resource.location = src.location;
|
|
135
|
+
if (src.ancestors !== undefined)
|
|
136
|
+
resource.ancestors = src.ancestors;
|
|
137
|
+
if (src.createTime !== undefined)
|
|
138
|
+
resource.createTime = src.createTime;
|
|
139
|
+
if (src.updateTime !== undefined)
|
|
140
|
+
resource.updateTime = src.updateTime;
|
|
141
|
+
if (entry.parentEntry) {
|
|
142
|
+
const at = entry.parentEntry.indexOf('/entries/');
|
|
143
|
+
resource.parent =
|
|
144
|
+
at !== -1
|
|
145
|
+
? entry.parentEntry.slice(at + '/entries/'.length)
|
|
146
|
+
: entry.parentEntry;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
name: localName,
|
|
150
|
+
type: dataplex._nameToTypeRef(entry.entryType),
|
|
151
|
+
resource,
|
|
152
|
+
aspects: Object.keys(aspects).length ? aspects : undefined,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function toServiceFromLocal(entry, container) {
|
|
156
|
+
const entryType = entry.type.includes('/')
|
|
157
|
+
? entry.type
|
|
158
|
+
: dataplex._typeRefToName(entry.type, 'entry');
|
|
159
|
+
const res = entry.resource ?? {};
|
|
160
|
+
let parentEntry;
|
|
161
|
+
if (res.parent) {
|
|
162
|
+
parentEntry = res.parent.startsWith('projects/')
|
|
163
|
+
? res.parent
|
|
164
|
+
: `${container}/entries/${res.parent}`;
|
|
165
|
+
}
|
|
166
|
+
const source = {};
|
|
167
|
+
if (res.name !== undefined)
|
|
168
|
+
source.resource = res.name;
|
|
169
|
+
if (res.system !== undefined)
|
|
170
|
+
source.system = res.system;
|
|
171
|
+
if (res.platform !== undefined)
|
|
172
|
+
source.platform = res.platform;
|
|
173
|
+
if (res.displayName !== undefined)
|
|
174
|
+
source.displayName = res.displayName;
|
|
175
|
+
if (res.description !== undefined)
|
|
176
|
+
source.description = res.description;
|
|
177
|
+
if (res.labels !== undefined)
|
|
178
|
+
source.labels = res.labels;
|
|
179
|
+
if (res.location !== undefined)
|
|
180
|
+
source.location = res.location;
|
|
181
|
+
if (res.ancestors !== undefined)
|
|
182
|
+
source.ancestors = res.ancestors;
|
|
183
|
+
if (res.createTime !== undefined)
|
|
184
|
+
source.createTime = res.createTime;
|
|
185
|
+
if (res.updateTime !== undefined)
|
|
186
|
+
source.updateTime = res.updateTime;
|
|
187
|
+
const aspects = {};
|
|
188
|
+
for (const [key, data] of Object.entries(entry.aspects ?? {})) {
|
|
189
|
+
aspects[key] = { data };
|
|
190
|
+
}
|
|
191
|
+
const out = {
|
|
192
|
+
name: `${container}/entries/${entry.name}`,
|
|
193
|
+
entryType,
|
|
194
|
+
};
|
|
195
|
+
if (parentEntry)
|
|
196
|
+
out.parentEntry = parentEntry;
|
|
197
|
+
if (Object.keys(source).length)
|
|
198
|
+
out.entrySource = source;
|
|
199
|
+
if (Object.keys(aspects).length)
|
|
200
|
+
out.aspects = aspects;
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
class OkfLayout {
|
|
204
|
+
_bundlePath;
|
|
205
|
+
_container;
|
|
206
|
+
_index = new Map();
|
|
207
|
+
constructor(bundlePath, entryGroupContainer) {
|
|
208
|
+
this._bundlePath = bundlePath;
|
|
209
|
+
this._container =
|
|
210
|
+
entryGroupContainer && entryGroupContainer.startsWith('projects/')
|
|
211
|
+
? entryGroupContainer
|
|
212
|
+
: FALLBACK_CONTAINER;
|
|
213
|
+
}
|
|
214
|
+
get bundlePath() {
|
|
215
|
+
return this._bundlePath;
|
|
216
|
+
}
|
|
217
|
+
bundleExists() {
|
|
218
|
+
return fs.existsSync(this._bundlePath);
|
|
219
|
+
}
|
|
220
|
+
/** Resolves `<bundle>/<id>.md`, rejecting any ID that escapes `bundle/`. */
|
|
221
|
+
resolveConceptPath(id) {
|
|
222
|
+
if (!id ||
|
|
223
|
+
id.includes('\0') ||
|
|
224
|
+
path.posix.isAbsolute(id) ||
|
|
225
|
+
path.win32.isAbsolute(id)) {
|
|
226
|
+
throw new Error(`entry id '${id}' escapes ${exports.BUNDLE_DIR}/`);
|
|
227
|
+
}
|
|
228
|
+
const root = path.resolve(this._bundlePath);
|
|
229
|
+
const file = path.resolve(root, `${id}.md`);
|
|
230
|
+
if (!file.startsWith(root + path.sep)) {
|
|
231
|
+
throw new Error(`entry id '${id}' escapes ${exports.BUNDLE_DIR}/`);
|
|
232
|
+
}
|
|
233
|
+
return file;
|
|
234
|
+
}
|
|
235
|
+
/** Display path relative to workspace root (e.g. `bundle/tables/trips.md`). */
|
|
236
|
+
relativeBundlePath(name) {
|
|
237
|
+
const existing = this._index.get(name);
|
|
238
|
+
if (existing) {
|
|
239
|
+
const rel = path
|
|
240
|
+
.relative(this._bundlePath, existing)
|
|
241
|
+
.split(path.sep)
|
|
242
|
+
.join('/');
|
|
243
|
+
return `${exports.BUNDLE_DIR}/${rel}`;
|
|
244
|
+
}
|
|
245
|
+
return `${exports.BUNDLE_DIR}/${name}.md`;
|
|
246
|
+
}
|
|
247
|
+
async init() {
|
|
248
|
+
this._index.clear();
|
|
249
|
+
if (!fs.existsSync(this._bundlePath))
|
|
250
|
+
return;
|
|
251
|
+
for (const file of conceptFiles(this._bundlePath)) {
|
|
252
|
+
const id = toPosixId(this._bundlePath, file);
|
|
253
|
+
let entryName = id;
|
|
254
|
+
try {
|
|
255
|
+
const concept = (0, concept_1.fileContentToConcept)(fs.readFileSync(file, 'utf8'), id);
|
|
256
|
+
entryName = resolvedEntryName(id, concept.frontmatter, this._bundlePath);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// Defer frontmatter parse errors to loadEntry so pull --force-remove
|
|
260
|
+
// can still overwrite or prune a corrupted local concept file.
|
|
261
|
+
}
|
|
262
|
+
if (!entryName)
|
|
263
|
+
continue;
|
|
264
|
+
this._index.set(entryName, file);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
entryExists(name) {
|
|
268
|
+
const p = this._index.get(name);
|
|
269
|
+
return !!p && fs.existsSync(p);
|
|
270
|
+
}
|
|
271
|
+
listEntries() {
|
|
272
|
+
return [...this._index.keys()].sort((a, b) => {
|
|
273
|
+
const depthDiff = a.split('/').length - b.split('/').length;
|
|
274
|
+
return depthDiff !== 0 ? depthDiff : a.localeCompare(b);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async loadEntry(name) {
|
|
278
|
+
const file = this._index.get(name);
|
|
279
|
+
if (!file || !fs.existsSync(file)) {
|
|
280
|
+
throw new Error(`Entry not found: ${name}`);
|
|
281
|
+
}
|
|
282
|
+
const id = toPosixId(this._bundlePath, file);
|
|
283
|
+
const textContent = await fs.promises.readFile(file, 'utf8');
|
|
284
|
+
const concept = (0, concept_1.fileContentToConcept)(textContent, id);
|
|
285
|
+
const gcpEntry = (0, concept_to_entry_1.conceptToEntry)(concept, this._container);
|
|
286
|
+
return toLocalFromService(gcpEntry, name);
|
|
287
|
+
}
|
|
288
|
+
/** Validates and builds the `Concept` `saveEntry` would write, without I/O. */
|
|
289
|
+
previewConcept(name, entry) {
|
|
290
|
+
this.resolveConceptPath(name);
|
|
291
|
+
const gcpEntry = toServiceFromLocal({ ...entry, name }, this._container);
|
|
292
|
+
return (0, concept_to_entry_1.entryToConcept)(gcpEntry, name);
|
|
293
|
+
}
|
|
294
|
+
async saveEntry(name, entry) {
|
|
295
|
+
const file = this.resolveConceptPath(name);
|
|
296
|
+
const concept = this.previewConcept(name, entry);
|
|
297
|
+
await fs.promises.mkdir(path.dirname(file), { recursive: true });
|
|
298
|
+
await fs.promises.writeFile(file, (0, concept_1.conceptToFileContent)(concept), 'utf8');
|
|
299
|
+
this._index.set(name, file);
|
|
300
|
+
}
|
|
301
|
+
async deleteEntry(name) {
|
|
302
|
+
const indexed = this._index.get(name);
|
|
303
|
+
const root = path.resolve(this._bundlePath);
|
|
304
|
+
const file = indexed ? path.resolve(indexed) : this.resolveConceptPath(name);
|
|
305
|
+
if (!file.startsWith(root + path.sep) || !fs.existsSync(file)) {
|
|
306
|
+
throw new Error(`Entry not found: ${name}`);
|
|
307
|
+
}
|
|
308
|
+
await fs.promises.unlink(file);
|
|
309
|
+
this._index.delete(name);
|
|
310
|
+
this._pruneEmptyDirs(path.dirname(file), root);
|
|
311
|
+
}
|
|
312
|
+
/** Reads `bundle/_links.yaml` plus any frontmatter `links` on indexed concepts. */
|
|
313
|
+
readLinks() {
|
|
314
|
+
const concepts = [];
|
|
315
|
+
for (const name of this.listEntries()) {
|
|
316
|
+
const file = this._index.get(name);
|
|
317
|
+
if (!file || !fs.existsSync(file))
|
|
318
|
+
continue;
|
|
319
|
+
concepts.push((0, concept_1.fileContentToConcept)(fs.readFileSync(file, 'utf8'), name));
|
|
320
|
+
}
|
|
321
|
+
return (0, links_1.readBundleLinks)(this._bundlePath, this._container, concepts);
|
|
322
|
+
}
|
|
323
|
+
/** Writes or removes `bundle/_links.yaml` to match pulled EntryLinks. */
|
|
324
|
+
async writeLinks(links) {
|
|
325
|
+
const linksPath = path.join(this._bundlePath, links_1.LINKS_FILE);
|
|
326
|
+
if (links.length > 0) {
|
|
327
|
+
await fs.promises.mkdir(this._bundlePath, { recursive: true });
|
|
328
|
+
await fs.promises.writeFile(linksPath, (0, links_1.formatLinksYaml)(links, this._container), 'utf8');
|
|
329
|
+
}
|
|
330
|
+
else if (fs.existsSync(linksPath)) {
|
|
331
|
+
await fs.promises.unlink(linksPath);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
_pruneEmptyDirs(dir, stopAt) {
|
|
335
|
+
let cur = path.resolve(dir);
|
|
336
|
+
while (cur.startsWith(stopAt + path.sep) && cur !== stopAt) {
|
|
337
|
+
if (!fs.existsSync(cur) || fs.readdirSync(cur).length > 0)
|
|
338
|
+
break;
|
|
339
|
+
fs.rmdirSync(cur);
|
|
340
|
+
cur = path.dirname(cur);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
exports.OkfLayout = OkfLayout;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { CatalogLayout } from '../layout';
|
|
2
|
+
import * as md from '../metadata';
|
|
3
|
+
export declare function profileFilesBeside(dir: string, model: string): {
|
|
4
|
+
name: string;
|
|
5
|
+
path: string;
|
|
6
|
+
}[];
|
|
7
|
+
export interface OverviewSidecar {
|
|
8
|
+
content?: string;
|
|
9
|
+
contentType?: string;
|
|
10
|
+
links?: Array<{
|
|
11
|
+
url: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
export declare class SemanticModelLayout implements CatalogLayout {
|
|
16
|
+
private readonly _catalogPath;
|
|
17
|
+
private readonly _entryGroup?;
|
|
18
|
+
private readonly _index;
|
|
19
|
+
constructor(catalogPath: string, entryGroup?: string);
|
|
20
|
+
init(): Promise<void>;
|
|
21
|
+
entryExists(_name: string): boolean;
|
|
22
|
+
listEntries(): string[];
|
|
23
|
+
modelDocuments(): {
|
|
24
|
+
name: string;
|
|
25
|
+
text: string;
|
|
26
|
+
}[];
|
|
27
|
+
localProfileFiles(model: string): {
|
|
28
|
+
path: string;
|
|
29
|
+
profileName?: string;
|
|
30
|
+
}[];
|
|
31
|
+
overviewSidecars(): Map<string, OverviewSidecar>;
|
|
32
|
+
overviewSidecarPaths(): {
|
|
33
|
+
entryId: string;
|
|
34
|
+
path: string;
|
|
35
|
+
}[];
|
|
36
|
+
profileDocuments(model: string): {
|
|
37
|
+
name: string;
|
|
38
|
+
text: string;
|
|
39
|
+
path: string;
|
|
40
|
+
}[];
|
|
41
|
+
profileDocument(model: string, profileName: string): string | undefined;
|
|
42
|
+
profilePath(model: string, profileName: string): string;
|
|
43
|
+
writeProfileDocument(model: string, profileName: string, text: string): void;
|
|
44
|
+
removeProfileDocument(model: string, profileName: string): void;
|
|
45
|
+
orphanProfilePaths(): string[];
|
|
46
|
+
legacyProfileDirs(): string[];
|
|
47
|
+
private _groupDir;
|
|
48
|
+
private _siblingProfilePaths;
|
|
49
|
+
private _legacyProfilePaths;
|
|
50
|
+
private _modelHeader;
|
|
51
|
+
hasModel(name: string): boolean;
|
|
52
|
+
modelPath(name: string): string;
|
|
53
|
+
writeModelDocument(name: string, text: string): void;
|
|
54
|
+
removeModelDocument(name: string): void;
|
|
55
|
+
loadEntry(_name: string): Promise<md.Entry>;
|
|
56
|
+
saveEntry(_name: string, _entry: md.Entry): Promise<void>;
|
|
57
|
+
deleteEntry(_name: string): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
export declare function fileNameOf(layout: SemanticModelLayout, model: string, localPath: string): string;
|
|
60
|
+
export declare function parseOverviewSidecar(text: string, file: string): OverviewSidecar;
|
|
61
|
+
export declare function formatOverviewSidecar({ content, ...rest }: OverviewSidecar): string;
|