@topogram/cli 0.3.80 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topogram/cli",
3
- "version": "0.3.80",
3
+ "version": "0.3.81",
4
4
  "description": "Topogram CLI for checking Topogram workspaces and generating app bundles.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -5,6 +5,7 @@ import path from "node:path";
5
5
 
6
6
  import { stableStringify } from "../../format.js";
7
7
  import { checkExtractorPack } from "../../extractor/check.js";
8
+ import { FIRST_PARTY_EXTRACTOR_PACKAGES, firstPartyExtractorInfo } from "../../extractor/first-party.js";
8
9
  import {
9
10
  EXTRACTOR_MANIFESTS,
10
11
  getExtractorManifest,
@@ -23,6 +24,8 @@ import {
23
24
  writeExtractorPolicy
24
25
  } from "../../extractor-policy.js";
25
26
 
27
+ const EXTRACTOR_TRACK_ORDER = ["db", "api", "ui", "cli", "workflows", "verification", "unknown"];
28
+
26
29
  export function printExtractorHelp() {
27
30
  console.log("Usage: topogram extractor list [--json]");
28
31
  console.log(" or: topogram extractor show <id-or-package> [--json]");
@@ -43,9 +46,11 @@ export function printExtractorHelp() {
43
46
  console.log("Examples:");
44
47
  console.log(" topogram extractor list");
45
48
  console.log(" topogram extractor show topogram/api-extractors");
49
+ console.log(" topogram extractor show @topogram/extractor-prisma-db");
46
50
  console.log(" topogram extractor check ./extractor-package");
47
51
  console.log(" topogram extractor policy init");
48
52
  console.log(" topogram extractor policy pin @topogram/extractor-node-cli@1");
53
+ console.log(" topogram extract ./express-api --out ./imported-topogram --from api --extractor @topogram/extractor-express-api");
49
54
  }
50
55
 
51
56
  /**
@@ -78,27 +83,105 @@ function declaredExtractorPackages(cwd) {
78
83
  return [...packages].sort();
79
84
  }
80
85
 
86
+ /**
87
+ * @param {string|null|undefined} packageName
88
+ * @param {string|null|undefined} version
89
+ * @returns {string|null}
90
+ */
91
+ function extractorPolicyPinCommand(packageName, version) {
92
+ return packageName ? `topogram extractor policy pin ${packageName}@${version || "1"}` : null;
93
+ }
94
+
95
+ /**
96
+ * @param {string|null|undefined} packageName
97
+ * @param {string[]} tracks
98
+ * @param {string|null|undefined} exampleSource
99
+ * @returns {string|null}
100
+ */
101
+ function extractorRunCommand(packageName, tracks, exampleSource) {
102
+ if (!packageName) {
103
+ return null;
104
+ }
105
+ const trackList = tracks.length > 0 ? tracks.join(",") : "db,api,ui,cli";
106
+ return `topogram extract ${exampleSource || "./existing-app"} --out ./imported-topogram --from ${trackList} --extractor ${packageName}`;
107
+ }
108
+
109
+ /**
110
+ * @param {Record<string, any>} extractor
111
+ * @returns {{ id: string|null, package: string|null, label: string|null, source: string, installed: boolean, knownFirstParty: boolean, useWhen: string|null, extractCommand: string|null }}
112
+ */
113
+ function groupExtractorEntry(extractor) {
114
+ return {
115
+ id: extractor.id || null,
116
+ package: extractor.package || null,
117
+ label: extractor.label || null,
118
+ source: extractor.source,
119
+ installed: Boolean(extractor.installed),
120
+ knownFirstParty: Boolean(extractor.knownFirstParty),
121
+ useWhen: extractor.useWhen || null,
122
+ extractCommand: extractor.extractCommand || null
123
+ };
124
+ }
125
+
126
+ /**
127
+ * @param {Record<string, any>[]} extractors
128
+ * @returns {Record<string, ReturnType<typeof groupExtractorEntry>[]>}
129
+ */
130
+ function groupExtractorsByTrack(extractors) {
131
+ /** @type {Record<string, ReturnType<typeof groupExtractorEntry>[]>} */
132
+ const groups = {};
133
+ for (const track of EXTRACTOR_TRACK_ORDER) {
134
+ groups[track] = [];
135
+ }
136
+ for (const extractor of extractors) {
137
+ const tracks = Array.isArray(extractor.tracks) && extractor.tracks.length > 0 ? extractor.tracks : ["unknown"];
138
+ for (const track of tracks) {
139
+ if (!groups[track]) {
140
+ groups[track] = [];
141
+ }
142
+ groups[track].push(groupExtractorEntry(extractor));
143
+ }
144
+ }
145
+ for (const entries of Object.values(groups)) {
146
+ entries.sort((left, right) => String(left.label || left.id || left.package || "").localeCompare(String(right.label || right.id || right.package || "")));
147
+ }
148
+ return Object.fromEntries(Object.entries(groups).filter(([, entries]) => entries.length > 0));
149
+ }
150
+
81
151
  /**
82
152
  * @param {any} manifest
83
153
  * @param {{ installed?: boolean, manifestPath?: string|null, packageRoot?: string|null, errors?: string[] }} [metadata]
84
154
  * @returns {Record<string, any>}
85
155
  */
86
156
  function extractorManifestSummary(manifest, metadata = {}) {
87
- const installCommand = manifest.package ? packageExtractorInstallCommand(manifest.package) : null;
157
+ const firstParty = firstPartyExtractorInfo(manifest.package || manifest.id);
158
+ const packageName = manifest.package || null;
159
+ const tracks = manifest.tracks || [];
160
+ const version = manifest.version || firstParty?.version || "1";
161
+ const installCommand = packageName ? packageExtractorInstallCommand(packageName) : null;
162
+ const policyPinCommand = extractorPolicyPinCommand(packageName, version);
163
+ const extractCommand = extractorRunCommand(packageName, tracks, firstParty?.exampleSource);
88
164
  return {
89
165
  id: manifest.id,
90
- version: manifest.version,
91
- tracks: manifest.tracks || [],
166
+ version,
167
+ label: firstParty?.label || null,
168
+ tracks,
92
169
  extractors: manifest.extractors || [],
93
170
  stack: manifest.stack || {},
94
171
  capabilities: manifest.capabilities || {},
95
172
  candidateKinds: manifest.candidateKinds || [],
96
173
  evidenceTypes: manifest.evidenceTypes || [],
174
+ useWhen: firstParty?.useWhen || null,
175
+ extracts: firstParty?.extracts || [],
176
+ knownFirstParty: Boolean(firstParty),
97
177
  source: manifest.source,
98
178
  loadsAdapter: false,
99
179
  executesPackageCode: false,
100
- ...(manifest.package ? { package: manifest.package } : {}),
180
+ ...(packageName ? { package: packageName } : {}),
101
181
  ...(installCommand ? { installCommand } : {}),
182
+ ...(policyPinCommand ? { policyPinCommand } : {}),
183
+ ...(extractCommand ? { extractCommand } : {}),
184
+ ...(packageName ? { showCommand: `topogram extractor show ${packageName}` } : {}),
102
185
  installed: metadata.installed !== false,
103
186
  manifestPath: metadata.manifestPath || null,
104
187
  packageRoot: metadata.packageRoot || null,
@@ -106,25 +189,64 @@ function extractorManifestSummary(manifest, metadata = {}) {
106
189
  };
107
190
  }
108
191
 
192
+ /**
193
+ * @param {typeof FIRST_PARTY_EXTRACTOR_PACKAGES[number]} info
194
+ * @returns {Record<string, any>}
195
+ */
196
+ function firstPartyExtractorPlaceholder(info) {
197
+ return {
198
+ id: info.id,
199
+ version: info.version,
200
+ label: info.label,
201
+ tracks: info.tracks,
202
+ extractors: info.extractors,
203
+ stack: info.stack,
204
+ capabilities: info.capabilities,
205
+ candidateKinds: info.candidateKinds,
206
+ evidenceTypes: info.evidenceTypes,
207
+ useWhen: info.useWhen,
208
+ extracts: info.extracts,
209
+ knownFirstParty: true,
210
+ source: "package",
211
+ loadsAdapter: false,
212
+ executesPackageCode: false,
213
+ package: info.package,
214
+ installCommand: packageExtractorInstallCommand(info.package),
215
+ policyPinCommand: extractorPolicyPinCommand(info.package, info.version),
216
+ extractCommand: extractorRunCommand(info.package, info.tracks, info.exampleSource),
217
+ showCommand: `topogram extractor show ${info.package}`,
218
+ installed: false,
219
+ manifestPath: null,
220
+ packageRoot: null,
221
+ errors: []
222
+ };
223
+ }
224
+
109
225
  /**
110
226
  * @param {string} cwd
111
- * @returns {{ ok: boolean, cwd: string, extractors: Record<string, any>[], summary: Record<string, number> }}
227
+ * @returns {{ ok: boolean, cwd: string, extractors: Record<string, any>[], groups: Record<string, ReturnType<typeof groupExtractorEntry>[]>, summary: Record<string, number> }}
112
228
  */
113
229
  export function buildExtractorListPayload(cwd) {
114
230
  const extractors = EXTRACTOR_MANIFESTS
115
231
  .map((manifest) => extractorManifestSummary(manifest))
116
232
  .sort((left, right) => left.id.localeCompare(right.id));
233
+ const seenPackages = new Set(extractors.map((extractor) => extractor.package).filter(Boolean));
234
+ const seenIds = new Set(extractors.map((extractor) => extractor.id).filter(Boolean));
117
235
  for (const packageName of declaredExtractorPackages(cwd)) {
118
236
  const loaded = loadPackageExtractorManifest(packageName, cwd);
119
237
  if (loaded.manifest) {
120
- extractors.push(extractorManifestSummary(loaded.manifest, {
238
+ const summary = extractorManifestSummary(loaded.manifest, {
121
239
  installed: true,
122
240
  manifestPath: loaded.manifestPath,
123
241
  packageRoot: loaded.packageRoot,
124
242
  errors: loaded.errors
125
- }));
243
+ });
244
+ extractors.push(summary);
245
+ if (summary.package) seenPackages.add(summary.package);
246
+ if (summary.id) seenIds.add(summary.id);
126
247
  } else {
127
- extractors.push({
248
+ const firstParty = firstPartyExtractorInfo(packageName);
249
+ const fallback = firstParty ? firstPartyExtractorPlaceholder(firstParty) : {
128
250
  id: null,
129
251
  version: null,
130
252
  tracks: [],
@@ -133,26 +255,44 @@ export function buildExtractorListPayload(cwd) {
133
255
  capabilities: {},
134
256
  candidateKinds: [],
135
257
  evidenceTypes: [],
258
+ useWhen: null,
259
+ extracts: [],
260
+ knownFirstParty: false,
136
261
  source: "package",
137
262
  package: packageName,
138
263
  installCommand: packageExtractorInstallCommand(packageName),
264
+ policyPinCommand: null,
265
+ extractCommand: null,
266
+ showCommand: `topogram extractor show ${packageName}`,
139
267
  installed: false,
140
268
  manifestPath: loaded.manifestPath,
141
269
  packageRoot: loaded.packageRoot,
142
270
  errors: loaded.errors
143
- });
271
+ };
272
+ extractors.push({ ...fallback, errors: loaded.errors });
273
+ seenPackages.add(packageName);
274
+ if (fallback.id) seenIds.add(fallback.id);
275
+ }
276
+ }
277
+ for (const firstParty of FIRST_PARTY_EXTRACTOR_PACKAGES) {
278
+ if (!seenPackages.has(firstParty.package) && !seenIds.has(firstParty.id)) {
279
+ extractors.push(firstPartyExtractorPlaceholder(firstParty));
144
280
  }
145
281
  }
146
282
  extractors.sort((left, right) => String(left.id || left.package || "").localeCompare(String(right.id || right.package || "")));
283
+ const groups = groupExtractorsByTrack(extractors);
147
284
  return {
148
285
  ok: extractors.every((extractor) => extractor.errors.length === 0),
149
286
  cwd,
150
287
  extractors,
288
+ groups,
151
289
  summary: {
152
290
  total: extractors.length,
153
291
  bundled: extractors.filter((extractor) => extractor.source === "bundled").length,
154
292
  package: extractors.filter((extractor) => extractor.source === "package").length,
155
- installed: extractors.filter((extractor) => extractor.installed).length
293
+ installed: extractors.filter((extractor) => extractor.installed).length,
294
+ knownFirstParty: extractors.filter((extractor) => extractor.knownFirstParty).length,
295
+ missingFirstParty: extractors.filter((extractor) => extractor.knownFirstParty && !extractor.installed).length
156
296
  }
157
297
  };
158
298
  }
@@ -184,6 +324,10 @@ export function buildExtractorShowPayload(spec, cwd) {
184
324
  errors: []
185
325
  };
186
326
  }
327
+ const firstParty = firstPartyExtractorInfo(spec);
328
+ if (firstParty) {
329
+ return { ok: true, sourceSpec: spec, extractor: firstPartyExtractorPlaceholder(firstParty), errors: [] };
330
+ }
187
331
  return { ok: false, sourceSpec: spec, extractor: null, errors: loaded.errors };
188
332
  }
189
333
 
@@ -193,23 +337,36 @@ export function buildExtractorShowPayload(spec, cwd) {
193
337
  */
194
338
  export function printExtractorList(payload) {
195
339
  console.log("Topogram extractors");
196
- console.log(`Bundled: ${payload.summary.bundled}; package-backed: ${payload.summary.package}; installed: ${payload.summary.installed}`);
340
+ console.log(`Bundled: ${payload.summary.bundled}; package-backed: ${payload.summary.package}; installed: ${payload.summary.installed}; first-party missing: ${payload.summary.missingFirstParty || 0}`);
341
+ console.log("Package-backed extractors are listed for discovery even before they are installed.");
197
342
  console.log("");
198
- for (const extractor of payload.extractors) {
199
- const id = extractor.id || extractor.package || "unknown";
200
- const status = extractor.errors.length > 0
201
- ? "invalid"
202
- : extractor.source === "package"
203
- ? (extractor.installed ? "package installed" : "package missing")
204
- : "bundled";
205
- console.log(`- ${id}${extractor.version ? `@${extractor.version}` : ""} (${extractor.tracks.join(", ") || "unknown"}, ${status})`);
206
- console.log(` Source: ${extractor.source}`);
207
- console.log(" Adapter loaded: no");
208
- console.log(" Executes package code: no");
209
- console.log(` Extractors: ${extractor.extractors.join(", ") || "none"}`);
210
- if (extractor.package) console.log(` Package: ${extractor.package}`);
211
- if (extractor.installCommand) console.log(` Install: ${extractor.installCommand}`);
212
- for (const error of extractor.errors || []) console.log(` Error: ${error}`);
343
+ for (const track of EXTRACTOR_TRACK_ORDER) {
344
+ const entries = (payload.groups || {})[track] || [];
345
+ if (entries.length === 0) {
346
+ continue;
347
+ }
348
+ console.log(`${track}:`);
349
+ for (const entry of entries) {
350
+ const extractor = /** @type {Record<string, any>} */ (payload.extractors.find((item) => (entry.package && item.package === entry.package) || (entry.id && item.id === entry.id)) || entry);
351
+ const id = extractor.id || extractor.package || "unknown";
352
+ const status = extractor.errors.length > 0
353
+ ? "invalid"
354
+ : extractor.source === "package"
355
+ ? (extractor.installed ? "package installed" : "package missing")
356
+ : "bundled";
357
+ console.log(`- ${extractor.label ? `${extractor.label} ` : ""}${id}${extractor.version ? `@${extractor.version}` : ""} (${status})`);
358
+ if (extractor.useWhen) console.log(` Use: ${extractor.useWhen}`);
359
+ console.log(` Source: ${extractor.source}`);
360
+ console.log(" Adapter loaded: no");
361
+ console.log(" Executes package code: no");
362
+ console.log(` Extractors: ${extractor.extractors.join(", ") || "none"}`);
363
+ if (extractor.package) console.log(` Package: ${extractor.package}`);
364
+ if (extractor.installCommand) console.log(` Install: ${extractor.installCommand}`);
365
+ if (extractor.policyPinCommand) console.log(` Policy: ${extractor.policyPinCommand}`);
366
+ if (extractor.extractCommand) console.log(` Extract: ${extractor.extractCommand}`);
367
+ for (const error of extractor.errors || []) console.log(` Error: ${error}`);
368
+ }
369
+ console.log("");
213
370
  }
214
371
  }
215
372
 
@@ -229,8 +386,14 @@ export function printExtractorShow(payload) {
229
386
  console.log(`Source: ${extractor.source}`);
230
387
  console.log("Adapter loaded: no");
231
388
  console.log("Executes package code: no");
389
+ if (extractor.label) console.log(`Label: ${extractor.label}`);
390
+ if (extractor.useWhen) console.log(`Use: ${extractor.useWhen}`);
391
+ if (extractor.extracts?.length) console.log(`Extracts: ${extractor.extracts.join(", ")}`);
392
+ console.log(`Installed: ${extractor.installed ? "yes" : "no"}`);
232
393
  if (extractor.package) console.log(`Package: ${extractor.package}`);
233
394
  if (extractor.installCommand) console.log(`Install: ${extractor.installCommand}`);
395
+ if (extractor.policyPinCommand) console.log(`Policy: ${extractor.policyPinCommand}`);
396
+ if (extractor.extractCommand) console.log(`Extract: ${extractor.extractCommand}`);
234
397
  if (extractor.manifestPath) console.log(`Manifest: ${extractor.manifestPath}`);
235
398
  console.log(`Extractors: ${extractor.extractors.join(", ") || "none"}`);
236
399
  console.log(`Candidate kinds: ${extractor.candidateKinds.join(", ") || "none"}`);
@@ -282,6 +445,9 @@ export function buildExtractorPolicyStatusPayload(projectPath) {
282
445
  version,
283
446
  allowed: extractorPackageAllowed(policy, packageName),
284
447
  installed: Boolean(loaded.manifest),
448
+ knownFirstParty: Boolean(firstPartyExtractorInfo(packageName)),
449
+ installCommand: packageExtractorInstallCommand(packageName),
450
+ showCommand: `topogram extractor show ${packageName}`,
285
451
  manifestPath: loaded.manifestPath,
286
452
  packageRoot: loaded.packageRoot,
287
453
  errors: [...loaded.errors, ...diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message)]
@@ -371,8 +537,12 @@ export function printExtractorPolicyStatus(payload) {
371
537
  console.log(`Policy file exists: ${payload.exists ? "yes" : "no"}`);
372
538
  console.log(`Default policy active: ${payload.defaulted ? "yes" : "no"}`);
373
539
  console.log(`Enabled packages: ${payload.summary.enabledPackages}`);
540
+ console.log("Default allowlist: bundled topogram/* extractors and first-party @topogram/extractor-* packages.");
541
+ console.log("Install behavior: Topogram does not install extractor packages automatically.");
374
542
  for (const item of payload.packages) {
375
543
  console.log(`- ${item.packageName}@${item.version}: ${item.installed ? "installed" : "missing"}, ${item.allowed ? "allowed" : "denied"}`);
544
+ if (!item.installed && item.installCommand) console.log(` Install: ${item.installCommand}`);
545
+ if (item.showCommand) console.log(` Show: ${item.showCommand}`);
376
546
  }
377
547
  for (const diagnostic of payload.diagnostics) {
378
548
  console.log(`[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
package/src/cli/help.js CHANGED
@@ -107,8 +107,10 @@ export function printUsage(options = {}) {
107
107
  console.log(" topogram generator check ./generator-package");
108
108
  console.log(" topogram generator policy check");
109
109
  console.log(" topogram extractor list");
110
+ console.log(" topogram extractor show @topogram/extractor-prisma-db");
110
111
  console.log(" topogram extractor check ./extractor-package");
111
112
  console.log(" topogram extractor policy check");
113
+ console.log(" topogram extract ./express-api --out ./extracted-topogram --from api --extractor @topogram/extractor-express-api");
112
114
  console.log(" topogram generate");
113
115
  console.log(" topogram extract ./existing-app --out ./extracted-topogram");
114
116
  console.log(" topogram extract diff ./extracted-topogram");
@@ -0,0 +1,93 @@
1
+ // @ts-check
2
+
3
+ export const FIRST_PARTY_EXTRACTOR_PACKAGES = [
4
+ {
5
+ package: "@topogram/extractor-prisma-db",
6
+ id: "@topogram/extractor-prisma-db",
7
+ version: "1",
8
+ label: "Prisma DB",
9
+ tracks: ["db"],
10
+ stack: { orm: "prisma", domain: "database" },
11
+ capabilities: { schema: true, migrations: true, maintainedSeams: true },
12
+ candidateKinds: ["entity", "enum", "relation", "index", "maintained_db_migration_seam"],
13
+ evidenceTypes: ["runtime_source", "parser_config"],
14
+ extractors: ["db.prisma"],
15
+ useWhen: "Use for Prisma schema.prisma models plus Prisma migration evidence.",
16
+ extracts: ["entities", "enums", "relations", "indexes", "maintained DB seam proposals"],
17
+ exampleSource: "./prisma-app"
18
+ },
19
+ {
20
+ package: "@topogram/extractor-express-api",
21
+ id: "@topogram/extractor-express-api",
22
+ version: "1",
23
+ label: "Express API",
24
+ tracks: ["api"],
25
+ stack: { runtime: "node", framework: "express" },
26
+ capabilities: { routes: true, parameters: true, authHints: true },
27
+ candidateKinds: ["capability", "route", "stack"],
28
+ evidenceTypes: ["runtime_source", "parser_config"],
29
+ extractors: ["api.express"],
30
+ useWhen: "Use for Express route files, routers, params, middleware, and auth hints.",
31
+ extracts: ["API route candidates", "capability suggestions", "parameter evidence", "auth hints", "stack evidence"],
32
+ exampleSource: "./express-api"
33
+ },
34
+ {
35
+ package: "@topogram/extractor-drizzle-db",
36
+ id: "@topogram/extractor-drizzle-db",
37
+ version: "1",
38
+ label: "Drizzle DB",
39
+ tracks: ["db"],
40
+ stack: { orm: "drizzle", domain: "database" },
41
+ capabilities: { schema: true, migrations: true, maintainedSeams: true },
42
+ candidateKinds: ["entity", "enum", "relation", "index", "maintained_db_migration_seam"],
43
+ evidenceTypes: ["runtime_source", "parser_config"],
44
+ extractors: ["db.drizzle"],
45
+ useWhen: "Use for Drizzle config, schema modules, table definitions, and migration output.",
46
+ extracts: ["entities", "relations", "indexes", "maintained DB seam proposals", "schema/migration evidence"],
47
+ exampleSource: "./drizzle-app"
48
+ },
49
+ {
50
+ package: "@topogram/extractor-node-cli",
51
+ id: "@topogram/extractor-node-cli",
52
+ version: "1",
53
+ label: "Node CLI",
54
+ tracks: ["cli"],
55
+ stack: { runtime: "node", domain: "cli" },
56
+ capabilities: { commands: true, options: true, effects: true },
57
+ candidateKinds: ["command", "capability", "cli_surface"],
58
+ evidenceTypes: ["runtime_source", "parser_config"],
59
+ extractors: ["cli.node-package"],
60
+ useWhen: "Use for Node package CLIs with bin entries, scripts, command modules, and help text.",
61
+ extracts: ["command candidates", "options", "effects", "CLI surface projections", "capability suggestions"],
62
+ exampleSource: "./existing-cli"
63
+ },
64
+ {
65
+ package: "@topogram/extractor-react-router",
66
+ id: "@topogram/extractor-react-router",
67
+ version: "1",
68
+ label: "React Router UI",
69
+ tracks: ["ui"],
70
+ stack: { framework: "react-router", domain: "ui" },
71
+ capabilities: { routes: true, screens: true, flows: true, widgets: true },
72
+ candidateKinds: ["screen", "route", "action", "flow", "widget", "shape", "stack"],
73
+ evidenceTypes: ["runtime_source", "parser_config"],
74
+ extractors: ["ui.react-router"],
75
+ useWhen: "Use for React Router route trees, route modules, screen hints, and non-resource UI flows.",
76
+ extracts: ["screens", "routes", "flow candidates", "widget evidence", "stack evidence"],
77
+ exampleSource: "./react-router-app"
78
+ }
79
+ ];
80
+
81
+ export const FIRST_PARTY_EXTRACTORS_BY_PACKAGE = new Map(FIRST_PARTY_EXTRACTOR_PACKAGES.map((item) => [item.package, item]));
82
+ export const FIRST_PARTY_EXTRACTORS_BY_ID = new Map(FIRST_PARTY_EXTRACTOR_PACKAGES.map((item) => [item.id, item]));
83
+
84
+ /**
85
+ * @param {string|null|undefined} value
86
+ * @returns {typeof FIRST_PARTY_EXTRACTOR_PACKAGES[number]|null}
87
+ */
88
+ export function firstPartyExtractorInfo(value) {
89
+ if (!value) {
90
+ return null;
91
+ }
92
+ return FIRST_PARTY_EXTRACTORS_BY_PACKAGE.get(value) || FIRST_PARTY_EXTRACTORS_BY_ID.get(value) || null;
93
+ }
@@ -16,8 +16,17 @@ export const DEFAULT_FIRST_PARTY_GENERATOR_REPOS = [
16
16
  "topogram-generator-vanilla-web"
17
17
  ];
18
18
 
19
+ export const DEFAULT_FIRST_PARTY_EXTRACTOR_REPOS = [
20
+ "topogram-extractor-node-cli",
21
+ "topogram-extractor-react-router",
22
+ "topogram-extractor-prisma-db",
23
+ "topogram-extractor-express-api",
24
+ "topogram-extractor-drizzle-db"
25
+ ];
26
+
19
27
  export const DEFAULT_RELEASE_CONSUMER_REPOS = [
20
28
  ...DEFAULT_FIRST_PARTY_GENERATOR_REPOS,
29
+ ...DEFAULT_FIRST_PARTY_EXTRACTOR_REPOS,
21
30
  "topogram-starters",
22
31
  "topogram-template-todo",
23
32
  "topogram-demo-todo",
@@ -34,6 +43,11 @@ export const DEFAULT_RELEASE_CONSUMER_WORKFLOWS = {
34
43
  "topogram-generator-sveltekit-web": "Generator Verification",
35
44
  "topogram-generator-swiftui-native": "Generator Verification",
36
45
  "topogram-generator-vanilla-web": "Generator Verification",
46
+ "topogram-extractor-node-cli": "Extractor Verification",
47
+ "topogram-extractor-react-router": "Extractor Verification",
48
+ "topogram-extractor-prisma-db": "Extractor Verification",
49
+ "topogram-extractor-express-api": "Extractor Verification",
50
+ "topogram-extractor-drizzle-db": "Extractor Verification",
37
51
  "topogram-starters": "Starter Verification",
38
52
  "topogram-template-todo": "Template Verification",
39
53
  "topogram-demo-todo": "Demo Verification",