@topogram/cli 0.3.79 → 0.3.81

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.
@@ -0,0 +1,249 @@
1
+ // @ts-check
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import { stableStringify } from "./format.js";
7
+ import {
8
+ optionalStringArray,
9
+ optionalStringRecord,
10
+ packageAllowedByPolicy,
11
+ packageScopeFromName as sharedPackageScopeFromName
12
+ } from "./package-adapters/index.js";
13
+
14
+ export const EXTRACTOR_POLICY_FILE = "topogram.extractor-policy.json";
15
+
16
+ /**
17
+ * @typedef {Object} ExtractorPolicy
18
+ * @property {string} version
19
+ * @property {string[]} allowedPackageScopes
20
+ * @property {string[]} allowedPackages
21
+ * @property {Record<string, string>} pinnedVersions
22
+ * @property {string[]} enabledPackages
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} PackageExtractorBinding
27
+ * @property {string} packageName
28
+ * @property {string} version
29
+ */
30
+
31
+ /**
32
+ * @typedef {Object} ExtractorPolicyInfo
33
+ * @property {string} path
34
+ * @property {ExtractorPolicy|null} policy
35
+ * @property {boolean} exists
36
+ * @property {ExtractorPolicyDiagnostic[]} diagnostics
37
+ */
38
+
39
+ /**
40
+ * @typedef {Object} ExtractorPolicyDiagnostic
41
+ * @property {string} code
42
+ * @property {"error"|"warning"} severity
43
+ * @property {string} message
44
+ * @property {string|null} path
45
+ * @property {string|null} suggestedFix
46
+ * @property {string|null} step
47
+ * @property {string|null} [packageName]
48
+ * @property {string|null} [version]
49
+ */
50
+
51
+ /**
52
+ * @param {Record<string, any>} input
53
+ * @returns {ExtractorPolicyDiagnostic}
54
+ */
55
+ function extractorPolicyDiagnostic(input) {
56
+ return {
57
+ code: String(input.code || "extractor_policy_failed"),
58
+ severity: input.severity === "warning" ? "warning" : "error",
59
+ message: String(input.message || "Extractor policy check failed."),
60
+ path: typeof input.path === "string" ? input.path : null,
61
+ suggestedFix: typeof input.suggestedFix === "string" ? input.suggestedFix : null,
62
+ step: typeof input.step === "string" ? input.step : null,
63
+ packageName: typeof input.packageName === "string" ? input.packageName : null,
64
+ version: typeof input.version === "string" ? input.version : null
65
+ };
66
+ }
67
+
68
+ /**
69
+ * @returns {ExtractorPolicy}
70
+ */
71
+ export function defaultExtractorPolicy() {
72
+ return {
73
+ version: "0.1",
74
+ allowedPackageScopes: [],
75
+ allowedPackages: [],
76
+ pinnedVersions: {},
77
+ enabledPackages: []
78
+ };
79
+ }
80
+
81
+ /**
82
+ * @param {unknown} value
83
+ * @param {string} policyPath
84
+ * @returns {ExtractorPolicy}
85
+ */
86
+ export function validateExtractorPolicy(value, policyPath) {
87
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
88
+ throw new Error(`${EXTRACTOR_POLICY_FILE} must contain a JSON object.`);
89
+ }
90
+ const raw = /** @type {Record<string, unknown>} */ (value);
91
+ const defaults = defaultExtractorPolicy();
92
+ return {
93
+ version: typeof raw.version === "string" && raw.version ? raw.version : defaults.version,
94
+ allowedPackageScopes: raw.allowedPackageScopes == null
95
+ ? defaults.allowedPackageScopes
96
+ : optionalStringArray(raw.allowedPackageScopes, "allowedPackageScopes", policyPath),
97
+ allowedPackages: optionalStringArray(raw.allowedPackages, "allowedPackages", policyPath),
98
+ pinnedVersions: optionalStringRecord(raw.pinnedVersions, policyPath, "package-or-extractor ids"),
99
+ enabledPackages: optionalStringArray(raw.enabledPackages, "enabledPackages", policyPath)
100
+ };
101
+ }
102
+
103
+ /**
104
+ * @param {string} packageName
105
+ * @returns {string|null}
106
+ */
107
+ export function packageScopeFromName(packageName) {
108
+ return sharedPackageScopeFromName(packageName);
109
+ }
110
+
111
+ /**
112
+ * @param {ExtractorPolicy} policy
113
+ * @param {string} packageName
114
+ * @returns {boolean}
115
+ */
116
+ export function extractorPackageAllowed(policy, packageName) {
117
+ return packageName.startsWith("@topogram/extractor-") || packageAllowedByPolicy(policy, packageName);
118
+ }
119
+
120
+ /**
121
+ * @param {string} projectRoot
122
+ * @param {string|null|undefined} policyPath
123
+ * @returns {string}
124
+ */
125
+ function resolvePolicyPath(projectRoot, policyPath) {
126
+ if (policyPath) {
127
+ const resolved = path.resolve(projectRoot, policyPath);
128
+ if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
129
+ return path.join(resolved, EXTRACTOR_POLICY_FILE);
130
+ }
131
+ return resolved;
132
+ }
133
+ return path.join(projectRoot, EXTRACTOR_POLICY_FILE);
134
+ }
135
+
136
+ /**
137
+ * @param {string} projectRoot
138
+ * @param {string|null|undefined} [policyPath]
139
+ * @returns {ExtractorPolicyInfo}
140
+ */
141
+ export function loadExtractorPolicy(projectRoot, policyPath = null) {
142
+ const resolvedPolicyPath = resolvePolicyPath(projectRoot, policyPath);
143
+ if (!fs.existsSync(resolvedPolicyPath)) {
144
+ return {
145
+ path: resolvedPolicyPath,
146
+ policy: null,
147
+ exists: false,
148
+ diagnostics: []
149
+ };
150
+ }
151
+ try {
152
+ return {
153
+ path: resolvedPolicyPath,
154
+ policy: validateExtractorPolicy(JSON.parse(fs.readFileSync(resolvedPolicyPath, "utf8")), resolvedPolicyPath),
155
+ exists: true,
156
+ diagnostics: []
157
+ };
158
+ } catch (error) {
159
+ return {
160
+ path: resolvedPolicyPath,
161
+ policy: null,
162
+ exists: true,
163
+ diagnostics: [extractorPolicyDiagnostic({
164
+ code: "extractor_policy_invalid",
165
+ message: error instanceof Error ? error.message : String(error),
166
+ path: resolvedPolicyPath,
167
+ suggestedFix: `Fix ${EXTRACTOR_POLICY_FILE} or regenerate it with \`topogram extractor policy init\`.`,
168
+ step: "extractor-policy"
169
+ })]
170
+ };
171
+ }
172
+ }
173
+
174
+ /**
175
+ * @param {ExtractorPolicyInfo} policyInfo
176
+ * @returns {ExtractorPolicy}
177
+ */
178
+ export function effectiveExtractorPolicy(policyInfo) {
179
+ return policyInfo.policy || defaultExtractorPolicy();
180
+ }
181
+
182
+ /**
183
+ * @param {string} projectRoot
184
+ * @param {ExtractorPolicy} policy
185
+ * @param {string|null|undefined} [policyPath]
186
+ * @returns {ExtractorPolicy}
187
+ */
188
+ export function writeExtractorPolicy(projectRoot, policy, policyPath = null) {
189
+ const resolvedPolicyPath = resolvePolicyPath(projectRoot, policyPath);
190
+ fs.writeFileSync(resolvedPolicyPath, `${stableStringify(policy)}\n`, "utf8");
191
+ return policy;
192
+ }
193
+
194
+ /**
195
+ * @param {ExtractorPolicyInfo} policyInfo
196
+ * @param {PackageExtractorBinding[]} bindings
197
+ * @param {string} [step]
198
+ * @returns {ExtractorPolicyDiagnostic[]}
199
+ */
200
+ export function extractorPolicyDiagnosticsForPackages(policyInfo, bindings, step = "extractor-policy") {
201
+ if (policyInfo.diagnostics.length > 0) {
202
+ return policyInfo.diagnostics;
203
+ }
204
+ const policy = effectiveExtractorPolicy(policyInfo);
205
+ /** @type {ExtractorPolicyDiagnostic[]} */
206
+ const diagnostics = [];
207
+ for (const binding of bindings) {
208
+ if (!extractorPackageAllowed(policy, binding.packageName)) {
209
+ const scope = packageScopeFromName(binding.packageName);
210
+ diagnostics.push(extractorPolicyDiagnostic({
211
+ code: "extractor_package_denied",
212
+ message: `Extractor package '${binding.packageName}' is not allowed by ${EXTRACTOR_POLICY_FILE}.`,
213
+ path: policyInfo.path,
214
+ suggestedFix: `Review '${binding.packageName}', then run \`topogram extractor policy pin ${binding.packageName}@${binding.version}\` or add '${scope || binding.packageName}' to ${EXTRACTOR_POLICY_FILE}.`,
215
+ step,
216
+ packageName: binding.packageName,
217
+ version: binding.version
218
+ }));
219
+ }
220
+ const pinnedVersion = policy.pinnedVersions[binding.packageName] || null;
221
+ if (pinnedVersion && pinnedVersion !== binding.version) {
222
+ diagnostics.push(extractorPolicyDiagnostic({
223
+ code: "extractor_version_mismatch",
224
+ message: `Extractor package '${binding.packageName}' uses version '${binding.version}', but ${EXTRACTOR_POLICY_FILE} pins it to '${pinnedVersion}'.`,
225
+ path: policyInfo.path,
226
+ suggestedFix: `Use extractor version '${pinnedVersion}', or run \`topogram extractor policy pin ${binding.packageName}@${binding.version}\` after review.`,
227
+ step,
228
+ packageName: binding.packageName,
229
+ version: binding.version
230
+ }));
231
+ }
232
+ }
233
+ return diagnostics;
234
+ }
235
+
236
+ /**
237
+ * @param {string} spec
238
+ * @returns {{ packageName: string, version: string }}
239
+ */
240
+ export function parseExtractorPolicyPin(spec) {
241
+ const separator = spec.lastIndexOf("@");
242
+ if (separator <= 0 || separator === spec.length - 1) {
243
+ throw new Error("Extractor policy pin requires a package name and extractor version, for example @topogram/extractor-node-cli@1.");
244
+ }
245
+ return {
246
+ packageName: spec.slice(0, separator),
247
+ version: spec.slice(separator + 1)
248
+ };
249
+ }
@@ -2,12 +2,18 @@
2
2
 
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
- import { createRequire } from "node:module";
6
5
 
7
6
  import {
8
7
  loadPackageGeneratorManifest,
9
8
  validateGeneratorManifest
10
9
  } from "./registry.js";
10
+ import {
11
+ isPathSpec,
12
+ loadInstalledPackageAdapter,
13
+ loadLocalPackageAdapter,
14
+ packageNameFromSpec,
15
+ validateRelativeStringFileMap
16
+ } from "../package-adapters/index.js";
11
17
 
12
18
  /**
13
19
  * @typedef {import("./registry.js").GeneratorManifest} GeneratorManifest
@@ -28,82 +34,6 @@ import {
28
34
  * @property {boolean} executesPackageCode
29
35
  */
30
36
 
31
- /**
32
- * @param {string} spec
33
- * @param {string} cwd
34
- * @returns {boolean}
35
- */
36
- function isPathSpec(spec, cwd) {
37
- return spec.startsWith(".") || spec.startsWith("/") || fs.existsSync(path.resolve(cwd, spec));
38
- }
39
-
40
- /**
41
- * @param {string} spec
42
- * @returns {string}
43
- */
44
- function packageNameFromSpec(spec) {
45
- if (spec.startsWith("@")) {
46
- const versionIndex = spec.indexOf("@", 1);
47
- return versionIndex > 0 ? spec.slice(0, versionIndex) : spec;
48
- }
49
- const versionIndex = spec.indexOf("@");
50
- return versionIndex > 0 ? spec.slice(0, versionIndex) : spec;
51
- }
52
-
53
- /**
54
- * @param {any} moduleValue
55
- * @param {string|null|undefined} exportName
56
- * @returns {any}
57
- */
58
- function selectPackageExport(moduleValue, exportName) {
59
- if (exportName) {
60
- return moduleValue?.[exportName] || moduleValue?.default?.[exportName] || null;
61
- }
62
- return moduleValue?.default || moduleValue;
63
- }
64
-
65
- /**
66
- * @param {string} root
67
- * @param {GeneratorManifest} manifest
68
- * @returns {{ adapter: any|null, error: string|null }}
69
- */
70
- function loadLocalAdapter(root, manifest) {
71
- try {
72
- const packageJsonPath = path.join(root, "package.json");
73
- const requireFromPackage = createRequire(packageJsonPath);
74
- return {
75
- adapter: selectPackageExport(requireFromPackage(root), manifest.export),
76
- error: null
77
- };
78
- } catch (error) {
79
- return {
80
- adapter: null,
81
- error: `Generator package export could not be loaded from '${root}': ${error instanceof Error ? error.message : String(error)}`
82
- };
83
- }
84
- }
85
-
86
- /**
87
- * @param {string} packageName
88
- * @param {string} rootDir
89
- * @param {GeneratorManifest} manifest
90
- * @returns {{ adapter: any|null, error: string|null }}
91
- */
92
- function loadInstalledAdapter(packageName, rootDir, manifest) {
93
- try {
94
- const requireFromRoot = createRequire(path.join(rootDir, "package.json"));
95
- return {
96
- adapter: selectPackageExport(requireFromRoot(packageName), manifest.export),
97
- error: null
98
- };
99
- } catch (error) {
100
- return {
101
- adapter: null,
102
- error: `Generator package '${packageName}' export could not be loaded from '${rootDir}': ${error instanceof Error ? error.message : String(error)}`
103
- };
104
- }
105
- }
106
-
107
37
  /**
108
38
  * @param {GeneratorManifest} manifest
109
39
  * @returns {Record<string, any>}
@@ -208,14 +138,12 @@ function validateSmokeResult(result) {
208
138
  if (!result.files || typeof result.files !== "object" || Array.isArray(result.files)) {
209
139
  return { ok: false, message: "generate(context) result must include a files object", smoke: null };
210
140
  }
211
- for (const [filePath, content] of Object.entries(result.files)) {
212
- const normalizedPath = typeof filePath === "string" ? path.normalize(filePath) : "";
213
- if (typeof filePath !== "string" || filePath.length === 0 || path.isAbsolute(filePath) || normalizedPath === ".." || normalizedPath.startsWith(`..${path.sep}`)) {
214
- return { ok: false, message: "generated file paths must be non-empty relative paths", smoke: null };
215
- }
216
- if (typeof content !== "string") {
217
- return { ok: false, message: `generated file '${filePath}' content must be a string`, smoke: null };
218
- }
141
+ const fileMapValidation = validateRelativeStringFileMap(result.files, {
142
+ filePathMessage: "generated file paths must be non-empty relative paths",
143
+ contentMessage: (filePath) => `generated file '${filePath}' content must be a string`
144
+ });
145
+ if (!fileMapValidation.ok) {
146
+ return { ok: false, message: fileMapValidation.message, smoke: null };
219
147
  }
220
148
  return {
221
149
  ok: true,
@@ -304,7 +232,11 @@ export function checkGeneratorPack(sourceSpec, options = {}) {
304
232
  }
305
233
 
306
234
  if (payload.source === "path") {
307
- const loaded = loadLocalAdapter(payload.packageRoot || cwd, payload.manifest);
235
+ const loaded = loadLocalPackageAdapter({
236
+ packageRoot: payload.packageRoot || cwd,
237
+ exportName: payload.manifest.export,
238
+ packageLabel: "Generator package"
239
+ });
308
240
  adapter = loaded.adapter;
309
241
  if (loaded.error) {
310
242
  payload.errors.push(loaded.error);
@@ -312,7 +244,12 @@ export function checkGeneratorPack(sourceSpec, options = {}) {
312
244
  return payload;
313
245
  }
314
246
  } else if (payload.packageName) {
315
- const loaded = loadInstalledAdapter(payload.packageName, cwd, payload.manifest);
247
+ const loaded = loadInstalledPackageAdapter({
248
+ packageName: payload.packageName,
249
+ rootDir: cwd,
250
+ exportName: payload.manifest.export,
251
+ packageLabel: "Generator package"
252
+ });
316
253
  adapter = loaded.adapter;
317
254
  if (loaded.error) {
318
255
  payload.errors.push(loaded.error);
@@ -1,8 +1,11 @@
1
1
  // @ts-check
2
2
 
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import { createRequire } from "node:module";
3
+ import {
4
+ loadPackageManifest,
5
+ packageInstallCommand,
6
+ packageInstallHint,
7
+ resolvePackageManifestPath
8
+ } from "../../package-adapters/index.js";
6
9
  import { UI_GENERATOR_RENDERED_COMPONENT_PATTERNS } from "../../ui/taxonomy.js";
7
10
 
8
11
  /**
@@ -208,20 +211,12 @@ export function getGeneratorManifest(generatorId) {
208
211
  return GENERATOR_BY_ID.get(generatorId) || null;
209
212
  }
210
213
 
211
- /**
212
- * @param {string|null|undefined} rootDir
213
- * @returns {string}
214
- */
215
- function packageResolutionBase(rootDir) {
216
- return path.join(rootDir || process.cwd(), "package.json");
217
- }
218
-
219
214
  /**
220
215
  * @param {string|null|undefined} packageName
221
216
  * @returns {string|null}
222
217
  */
223
218
  export function packageGeneratorInstallCommand(packageName) {
224
- return packageName ? `npm install -D ${packageName}` : null;
219
+ return packageInstallCommand(packageName);
225
220
  }
226
221
 
227
222
  /**
@@ -229,8 +224,7 @@ export function packageGeneratorInstallCommand(packageName) {
229
224
  * @returns {string|null}
230
225
  */
231
226
  export function packageGeneratorInstallHint(packageName) {
232
- const command = packageGeneratorInstallCommand(packageName);
233
- return command ? `Install it from the project root with: ${command}` : null;
227
+ return packageInstallHint(packageName);
234
228
  }
235
229
 
236
230
  /**
@@ -239,41 +233,7 @@ export function packageGeneratorInstallHint(packageName) {
239
233
  * @returns {{ manifestPath: string|null, packageRoot: string|null, error: string|null }}
240
234
  */
241
235
  export function resolvePackageGeneratorManifestPath(packageName, rootDir = process.cwd()) {
242
- const requireFromRoot = createRequire(packageResolutionBase(rootDir));
243
- try {
244
- const manifestPath = requireFromRoot.resolve(`${packageName}/topogram-generator.json`);
245
- return {
246
- manifestPath,
247
- packageRoot: path.dirname(manifestPath),
248
- error: null
249
- };
250
- } catch (manifestError) {
251
- try {
252
- const packageJsonPath = requireFromRoot.resolve(`${packageName}/package.json`);
253
- const packageRoot = path.dirname(packageJsonPath);
254
- const manifestPath = path.join(packageRoot, "topogram-generator.json");
255
- if (!fs.existsSync(manifestPath)) {
256
- return {
257
- manifestPath: null,
258
- packageRoot,
259
- error: `Generator package '${packageName}' is missing topogram-generator.json`
260
- };
261
- }
262
- return {
263
- manifestPath,
264
- packageRoot,
265
- error: null
266
- };
267
- } catch {
268
- const detail = manifestError instanceof Error ? manifestError.message : String(manifestError);
269
- const installHint = packageGeneratorInstallHint(packageName);
270
- return {
271
- manifestPath: null,
272
- packageRoot: null,
273
- error: `Generator package '${packageName}' could not be resolved from '${rootDir || process.cwd()}': ${detail}${installHint ? `. ${installHint}` : ""}`
274
- };
275
- }
276
- }
236
+ return resolvePackageManifestPath(packageName, "topogram-generator.json", rootDir, "Generator package");
277
237
  }
278
238
 
279
239
  /**
@@ -282,32 +242,13 @@ export function resolvePackageGeneratorManifestPath(packageName, rootDir = proce
282
242
  * @returns {{ manifest: GeneratorManifest|null, errors: string[], manifestPath: string|null, packageRoot: string|null }}
283
243
  */
284
244
  export function loadPackageGeneratorManifest(packageName, rootDir = process.cwd()) {
285
- const resolved = resolvePackageGeneratorManifestPath(packageName, rootDir);
286
- if (!resolved.manifestPath) {
287
- return {
288
- manifest: null,
289
- errors: [resolved.error || `Generator package '${packageName}' could not be resolved`],
290
- manifestPath: null,
291
- packageRoot: resolved.packageRoot
292
- };
293
- }
294
- try {
295
- const manifest = JSON.parse(fs.readFileSync(resolved.manifestPath, "utf8"));
296
- const validation = validateGeneratorManifest(manifest);
297
- return {
298
- manifest: validation.ok ? manifest : null,
299
- errors: validation.errors,
300
- manifestPath: resolved.manifestPath,
301
- packageRoot: resolved.packageRoot
302
- };
303
- } catch (error) {
304
- return {
305
- manifest: null,
306
- errors: [`Generator package '${packageName}' manifest could not be read: ${error instanceof Error ? error.message : String(error)}`],
307
- manifestPath: resolved.manifestPath,
308
- packageRoot: resolved.packageRoot
309
- };
310
- }
245
+ return loadPackageManifest({
246
+ packageName,
247
+ rootDir,
248
+ manifestFile: "topogram-generator.json",
249
+ packageLabel: "Generator package",
250
+ validateManifest: validateGeneratorManifest
251
+ });
311
252
  }
312
253
 
313
254
  /**
@@ -4,6 +4,12 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
 
6
6
  import { stableStringify } from "./format.js";
7
+ import {
8
+ optionalStringArray,
9
+ optionalStringRecord,
10
+ packageAllowedByPolicy,
11
+ packageScopeFromName as sharedPackageScopeFromName
12
+ } from "./package-adapters/index.js";
7
13
 
8
14
  export const GENERATOR_POLICY_FILE = "topogram.generator-policy.json";
9
15
 
@@ -66,47 +72,6 @@ function generatorPolicyDiagnostic(input) {
66
72
  };
67
73
  }
68
74
 
69
- /**
70
- * @param {unknown} value
71
- * @param {string} fieldName
72
- * @param {string} policyPath
73
- * @returns {string[]}
74
- */
75
- function optionalStringArray(value, fieldName, policyPath) {
76
- if (value == null) {
77
- return [];
78
- }
79
- if (!Array.isArray(value)) {
80
- throw new Error(`${policyPath} ${fieldName} must be an array of strings.`);
81
- }
82
- return value.map((item) => {
83
- if (typeof item !== "string" || item.length === 0) {
84
- throw new Error(`${policyPath} ${fieldName} must contain only non-empty strings.`);
85
- }
86
- return item;
87
- });
88
- }
89
-
90
- /**
91
- * @param {unknown} value
92
- * @param {string} policyPath
93
- * @returns {Record<string, string>}
94
- */
95
- function optionalStringRecord(value, policyPath) {
96
- if (value == null) {
97
- return {};
98
- }
99
- if (typeof value !== "object" || Array.isArray(value)) {
100
- throw new Error(`${policyPath} pinnedVersions must be an object of package-or-generator ids to versions.`);
101
- }
102
- return Object.fromEntries(Object.entries(value).map(([key, item]) => {
103
- if (typeof item !== "string" || item.length === 0) {
104
- throw new Error(`${policyPath} pinnedVersions['${key}'] must be a non-empty string.`);
105
- }
106
- return [key, item];
107
- }));
108
- }
109
-
110
75
  /**
111
76
  * @returns {GeneratorPolicy}
112
77
  */
@@ -136,7 +101,7 @@ export function validateGeneratorPolicy(value, policyPath) {
136
101
  ? defaults.allowedPackageScopes
137
102
  : optionalStringArray(raw.allowedPackageScopes, "allowedPackageScopes", policyPath),
138
103
  allowedPackages: optionalStringArray(raw.allowedPackages, "allowedPackages", policyPath),
139
- pinnedVersions: optionalStringRecord(raw.pinnedVersions, policyPath)
104
+ pinnedVersions: optionalStringRecord(raw.pinnedVersions, policyPath, "package-or-generator ids")
140
105
  };
141
106
  }
142
107
 
@@ -145,16 +110,7 @@ export function validateGeneratorPolicy(value, policyPath) {
145
110
  * @returns {string|null}
146
111
  */
147
112
  export function packageScopeFromName(packageName) {
148
- return packageName.startsWith("@") ? packageName.split("/")[0] || null : null;
149
- }
150
-
151
- /**
152
- * @param {string} allowed
153
- * @param {string|null} scope
154
- * @returns {boolean}
155
- */
156
- function packageScopeMatches(allowed, scope) {
157
- return Boolean(scope && (allowed === scope || allowed === `${scope}/*`));
113
+ return sharedPackageScopeFromName(packageName);
158
114
  }
159
115
 
160
116
  /**
@@ -163,11 +119,7 @@ function packageScopeMatches(allowed, scope) {
163
119
  * @returns {boolean}
164
120
  */
165
121
  export function generatorPackageAllowed(policy, packageName) {
166
- if (policy.allowedPackages.includes(packageName)) {
167
- return true;
168
- }
169
- const scope = packageScopeFromName(packageName);
170
- return policy.allowedPackageScopes.some((allowed) => packageScopeMatches(allowed, scope));
122
+ return packageAllowedByPolicy(policy, packageName);
171
123
  }
172
124
 
173
125
  /**
@@ -1,4 +1,7 @@
1
1
  export const extractorRegistry: Record<string, any[]>;
2
2
  export const enricherRegistry: Record<string, any[]>;
3
+ export const BUILTIN_EXTRACTOR_PACKS: any[];
3
4
  export function getExtractorsForTrack(...args: any[]): any[];
4
5
  export function getEnrichersForTrack(...args: any[]): any[];
6
+ export function getBundledExtractorPack(...args: any[]): any;
7
+ export function getBundledExtractorById(...args: any[]): any;