@memberjunction/cli 5.41.0 → 5.42.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/dist/commands/bundle/index.d.ts +1 -0
- package/dist/commands/bundle/index.d.ts.map +1 -1
- package/dist/commands/bundle/index.js +7 -1
- package/dist/commands/bundle/index.js.map +1 -1
- package/dist/commands/codegen/index.d.ts +6 -16
- package/dist/commands/codegen/index.d.ts.map +1 -1
- package/dist/commands/codegen/index.js +6 -90
- package/dist/commands/codegen/index.js.map +1 -1
- package/dist/commands/codegen/usage.d.ts +7 -0
- package/dist/commands/codegen/usage.d.ts.map +1 -0
- package/dist/commands/codegen/usage.js +10 -0
- package/dist/commands/codegen/usage.js.map +1 -0
- package/dist/commands/install/claude.d.ts +35 -0
- package/dist/commands/install/claude.d.ts.map +1 -0
- package/dist/commands/install/claude.js +108 -0
- package/dist/commands/install/claude.js.map +1 -0
- package/dist/commands/install/index.d.ts +1 -0
- package/dist/commands/install/index.d.ts.map +1 -1
- package/dist/commands/install/index.js +4 -0
- package/dist/commands/install/index.js.map +1 -1
- package/dist/commands/migrate/convert.d.ts +59 -12
- package/dist/commands/migrate/convert.d.ts.map +1 -1
- package/dist/commands/migrate/convert.js +294 -21
- package/dist/commands/migrate/convert.js.map +1 -1
- package/dist/commands/migrate/rebake.d.ts +41 -0
- package/dist/commands/migrate/rebake.d.ts.map +1 -0
- package/dist/commands/migrate/rebake.js +193 -0
- package/dist/commands/migrate/rebake.js.map +1 -0
- package/dist/commands/plugin/add.d.ts +18 -0
- package/dist/commands/plugin/add.d.ts.map +1 -0
- package/dist/commands/plugin/add.js +53 -0
- package/dist/commands/plugin/add.js.map +1 -0
- package/dist/commands/sync/pull.d.ts +6 -26
- package/dist/commands/sync/pull.d.ts.map +1 -1
- package/dist/commands/sync/pull.js +6 -281
- package/dist/commands/sync/pull.js.map +1 -1
- package/dist/commands/sync/push.d.ts +8 -19
- package/dist/commands/sync/push.d.ts.map +1 -1
- package/dist/commands/sync/push.js +8 -328
- package/dist/commands/sync/push.js.map +1 -1
- package/dist/commands/sync/usage.d.ts +7 -0
- package/dist/commands/sync/usage.d.ts.map +1 -0
- package/dist/commands/sync/usage.js +10 -0
- package/dist/commands/sync/usage.js.map +1 -0
- package/dist/commands/update/claude.d.ts +37 -0
- package/dist/commands/update/claude.d.ts.map +1 -0
- package/dist/commands/update/claude.js +90 -0
- package/dist/commands/update/claude.js.map +1 -0
- package/dist/commands/usage.d.ts +17 -0
- package/dist/commands/usage.d.ts.map +1 -0
- package/dist/commands/usage.js +29 -0
- package/dist/commands/usage.js.map +1 -0
- package/dist/hooks/prerun.d.ts.map +1 -1
- package/dist/hooks/prerun.js +49 -8
- package/dist/hooks/prerun.js.map +1 -1
- package/dist/lib/claude-pack/ManagedBlockEditor.d.ts +40 -0
- package/dist/lib/claude-pack/ManagedBlockEditor.d.ts.map +1 -0
- package/dist/lib/claude-pack/ManagedBlockEditor.js +127 -0
- package/dist/lib/claude-pack/ManagedBlockEditor.js.map +1 -0
- package/dist/lib/claude-pack/PackFetcher.d.ts +86 -0
- package/dist/lib/claude-pack/PackFetcher.d.ts.map +1 -0
- package/dist/lib/claude-pack/PackFetcher.js +163 -0
- package/dist/lib/claude-pack/PackFetcher.js.map +1 -0
- package/dist/lib/claude-pack/PackInstaller.d.ts +62 -0
- package/dist/lib/claude-pack/PackInstaller.d.ts.map +1 -0
- package/dist/lib/claude-pack/PackInstaller.js +197 -0
- package/dist/lib/claude-pack/PackInstaller.js.map +1 -0
- package/dist/lib/claude-pack/PackMerger.d.ts +61 -0
- package/dist/lib/claude-pack/PackMerger.d.ts.map +1 -0
- package/dist/lib/claude-pack/PackMerger.js +315 -0
- package/dist/lib/claude-pack/PackMerger.js.map +1 -0
- package/dist/lib/claude-pack/PackOutputFormatter.d.ts +29 -0
- package/dist/lib/claude-pack/PackOutputFormatter.d.ts.map +1 -0
- package/dist/lib/claude-pack/PackOutputFormatter.js +82 -0
- package/dist/lib/claude-pack/PackOutputFormatter.js.map +1 -0
- package/dist/lib/claude-pack/PackPaths.d.ts +80 -0
- package/dist/lib/claude-pack/PackPaths.d.ts.map +1 -0
- package/dist/lib/claude-pack/PackPaths.js +182 -0
- package/dist/lib/claude-pack/PackPaths.js.map +1 -0
- package/dist/lib/claude-pack/PackTypes.d.ts +124 -0
- package/dist/lib/claude-pack/PackTypes.d.ts.map +1 -0
- package/dist/lib/claude-pack/PackTypes.js +32 -0
- package/dist/lib/claude-pack/PackTypes.js.map +1 -0
- package/dist/lib/claude-pack/SettingsMerger.d.ts +47 -0
- package/dist/lib/claude-pack/SettingsMerger.d.ts.map +1 -0
- package/dist/lib/claude-pack/SettingsMerger.js +163 -0
- package/dist/lib/claude-pack/SettingsMerger.js.map +1 -0
- package/dist/lib/cli-plugins.d.ts +16 -0
- package/dist/lib/cli-plugins.d.ts.map +1 -0
- package/dist/lib/cli-plugins.js +26 -0
- package/dist/lib/cli-plugins.js.map +1 -0
- package/dist/lib/domain-usage-command.d.ts +16 -0
- package/dist/lib/domain-usage-command.d.ts.map +1 -0
- package/dist/lib/domain-usage-command.js +23 -0
- package/dist/lib/domain-usage-command.js.map +1 -0
- package/dist/lib/usage-render.d.ts +14 -0
- package/dist/lib/usage-render.d.ts.map +1 -0
- package/dist/lib/usage-render.js +64 -0
- package/dist/lib/usage-render.js.map +1 -0
- package/dist/light-commands.d.ts.map +1 -1
- package/dist/light-commands.js +11 -0
- package/dist/light-commands.js.map +1 -1
- package/oclif.manifest.json +815 -135
- package/package.json +17 -15
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { IncrementalBaker } from '@memberjunction/sql-converter';
|
|
6
|
+
import { MJPostgresTranspiler } from '@memberjunction/sqlglot-ts';
|
|
7
|
+
/**
|
|
8
|
+
* Re-bake the post-baseline PG migration set with INLINE NATIVE CodeGen (Path C), so the set
|
|
9
|
+
* deploys with `mj migrate` alone — no `mj codegen` step.
|
|
10
|
+
*
|
|
11
|
+
* Unlike `convert --bake-codegen` (which bakes UNCONVERTED migrations forward), `rebake` re-bakes
|
|
12
|
+
* migrations that already have a committed `.pg.sql`: it advances a live working DB by applying
|
|
13
|
+
* each migration's known-good committed file in order (which keeps dependent views consistent and
|
|
14
|
+
* registers new entities), then captures native CodeGen read-only and writes the baked file. A
|
|
15
|
+
* migration with a transpile gap (unhandled statement / hand-procedural) keeps its committed file.
|
|
16
|
+
*
|
|
17
|
+
* Requires a LIVE working PG database (DB_PLATFORM=postgresql + PG_* env, as `mj codegen`) seeded
|
|
18
|
+
* to the latest baseline; the command brings it forward as it re-bakes.
|
|
19
|
+
*/
|
|
20
|
+
export default class MigrateRebake extends Command {
|
|
21
|
+
static { this.description = 'Re-bake the post-baseline PG migration set with inline native CodeGen'; }
|
|
22
|
+
static { this.examples = [
|
|
23
|
+
'<%= config.bin %> migrate rebake',
|
|
24
|
+
'<%= config.bin %> migrate rebake --baseline-floor V202605291452 --dry-run',
|
|
25
|
+
]; }
|
|
26
|
+
static { this.flags = {
|
|
27
|
+
'source-dir': Flags.string({ description: 'Source T-SQL migrations directory', default: './migrations/v5' }),
|
|
28
|
+
'output-dir': Flags.string({ description: 'PG migrations directory (committed files read here + baked output written here)', default: './migrations-pg/v5' }),
|
|
29
|
+
schema: Flags.string({ description: 'Target schema name', default: '__mj' }),
|
|
30
|
+
'baseline-floor': Flags.string({ description: 'Re-bake migrations strictly AFTER this version key (default: the latest B* in output-dir)' }),
|
|
31
|
+
'dry-run': Flags.boolean({ description: 'Report what would be re-baked without writing files', default: false }),
|
|
32
|
+
}; }
|
|
33
|
+
async run() {
|
|
34
|
+
const { flags } = await this.parse(MigrateRebake);
|
|
35
|
+
const sourceDir = path.resolve(flags['source-dir']);
|
|
36
|
+
const outputDir = path.resolve(flags['output-dir']);
|
|
37
|
+
if (!fs.existsSync(sourceDir))
|
|
38
|
+
this.error(`Source directory not found: ${sourceDir}`);
|
|
39
|
+
if (!fs.existsSync(outputDir))
|
|
40
|
+
this.error(`Output directory not found: ${outputDir}`);
|
|
41
|
+
const floor = flags['baseline-floor'] ?? this.latestBaselineFloor(outputDir);
|
|
42
|
+
this.log(`Re-baking migrations after baseline floor: ${floor}`);
|
|
43
|
+
const migrations = this.postBaselineMigrations(sourceDir, floor);
|
|
44
|
+
if (migrations.length === 0) {
|
|
45
|
+
this.log('No post-baseline migrations to re-bake.');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this.log(`${migrations.length} post-baseline migration(s) to re-bake.\n`);
|
|
49
|
+
const transpiler = await this.buildTranspiler(sourceDir);
|
|
50
|
+
const { baker, dispose } = await this.buildBaker(transpiler, flags.schema);
|
|
51
|
+
const summary = { baked: [], preserved: [], skipped: [] };
|
|
52
|
+
try {
|
|
53
|
+
for (const m of migrations) {
|
|
54
|
+
const ssSql = fs.readFileSync(path.join(sourceDir, m.source), 'utf8');
|
|
55
|
+
const committedPath = this.committedCounterpart(outputDir, m.outName, m.source);
|
|
56
|
+
if (!committedPath) {
|
|
57
|
+
this.warn(`${m.source}: no committed PG counterpart in output-dir — skipped (use convert --bake-codegen for new migrations).`);
|
|
58
|
+
summary.skipped.push(m.source);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// mj-sync metadata is owned by `mj sync push`, not CodeGen — preserve it verbatim.
|
|
62
|
+
if (/Metadata_Sync/i.test(m.source)) {
|
|
63
|
+
summary.preserved.push(m.source);
|
|
64
|
+
this.log(` kept ${m.source} (metadata-sync — committed preserved)`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const committed = fs.readFileSync(committedPath, 'utf8');
|
|
68
|
+
try {
|
|
69
|
+
const result = await baker.bakeMigration(ssSql, m.source, committed);
|
|
70
|
+
if (result.mode === 'baked') {
|
|
71
|
+
if (!flags['dry-run'])
|
|
72
|
+
fs.writeFileSync(committedPath, result.pgSQL);
|
|
73
|
+
summary.baked.push(m.source);
|
|
74
|
+
this.log(` baked ${m.source} (E(M)=${result.affectedEntities.length})`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
summary.preserved.push(m.source); // transpile gap — committed left in place
|
|
78
|
+
this.log(` kept ${m.source} (transpile gap — committed preserved)`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
// Advancing or capturing this migration failed (e.g. a committed file that only applies
|
|
83
|
+
// under Skyway). Keep the committed file as-is and carry on so one migration can't abort
|
|
84
|
+
// the whole re-bake; the working DB simply lacks this migration's (non-schema) change.
|
|
85
|
+
summary.preserved.push(m.source);
|
|
86
|
+
this.warn(`${m.source}: re-bake failed (${err instanceof Error ? err.message : String(err)}) — committed preserved.`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await dispose();
|
|
92
|
+
}
|
|
93
|
+
this.log('\n=== Re-bake Summary ===');
|
|
94
|
+
this.log(` baked native: ${summary.baked.length}`);
|
|
95
|
+
this.log(` preserved committed: ${summary.preserved.length}`);
|
|
96
|
+
this.log(` skipped (no PG): ${summary.skipped.length}`);
|
|
97
|
+
if (flags['dry-run'])
|
|
98
|
+
this.log(' (dry run — no files written)');
|
|
99
|
+
this.log('\nDeploy: mj migrate + mj sync push (no mj codegen step)');
|
|
100
|
+
// setupDataSource opened pg pools that keep the event loop alive; mirror `mj codegen`.
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
/** Highest `B<version>__...` baseline in the output dir → its version key is the floor. */
|
|
104
|
+
latestBaselineFloor(outputDir) {
|
|
105
|
+
const baselines = fs.readdirSync(outputDir).filter((f) => /^B\d.*\.pg\.sql$/.test(f)).sort();
|
|
106
|
+
if (baselines.length === 0)
|
|
107
|
+
this.error('No baseline (B*.pg.sql) found in output-dir; pass --baseline-floor.');
|
|
108
|
+
return baselines[baselines.length - 1].split('__')[0]; // e.g. "B202605291452" → compared lexically below
|
|
109
|
+
}
|
|
110
|
+
/** SS V-migrations whose version key sorts strictly after the floor (committed order). */
|
|
111
|
+
postBaselineMigrations(sourceDir, floor) {
|
|
112
|
+
const floorKey = floor.replace(/^B/, 'V'); // baseline keys start with B; migrations with V
|
|
113
|
+
return fs
|
|
114
|
+
.readdirSync(sourceDir)
|
|
115
|
+
.filter((f) => /^V.*\.sql$/.test(f) && !f.endsWith('.pg.sql') && !f.endsWith('.pg-only.sql'))
|
|
116
|
+
.filter((f) => f.split('__')[0] > floorKey)
|
|
117
|
+
.sort()
|
|
118
|
+
.map((source) => ({ source, outName: source.replace(/\.sql$/, '.pg.sql') }));
|
|
119
|
+
}
|
|
120
|
+
/** Committed PG counterpart: `.pg.sql` preferred, else a hand-authored `.pg-only.sql`. */
|
|
121
|
+
committedCounterpart(outputDir, outName, source) {
|
|
122
|
+
const pg = path.join(outputDir, outName);
|
|
123
|
+
if (fs.existsSync(pg))
|
|
124
|
+
return pg;
|
|
125
|
+
const pgOnly = path.join(outputDir, source.replace(/\.sql$/, '.pg-only.sql'));
|
|
126
|
+
return fs.existsSync(pgOnly) ? pgOnly : null;
|
|
127
|
+
}
|
|
128
|
+
/** AST transpiler with the cross-file BIT-column registry from the baselines (see convert.ts). */
|
|
129
|
+
async buildTranspiler(sourceDir) {
|
|
130
|
+
const probe = new MJPostgresTranspiler();
|
|
131
|
+
try {
|
|
132
|
+
await probe.preflight();
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
this.error(err instanceof Error ? err.message : String(err));
|
|
136
|
+
}
|
|
137
|
+
const baselines = fs.readdirSync(sourceDir).filter((f) => /^B\d.*\.sql$/.test(f) && !f.endsWith('.pg.sql') && !f.endsWith('.pg-only.sql')).sort();
|
|
138
|
+
const bitColumns = [];
|
|
139
|
+
for (const b of baselines) {
|
|
140
|
+
try {
|
|
141
|
+
bitColumns.push(...(await probe.collectBitColumns(fs.readFileSync(path.join(sourceDir, b), 'utf8'))));
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
this.warn(`Bit-column collection failed for ${b}: ${err instanceof Error ? err.message : String(err)}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return new MJPostgresTranspiler({ extraBitColumns: bitColumns });
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Wire the working-DB capabilities to a live PG connection. The codegen engine is loaded with a
|
|
151
|
+
* dynamic import (Rule 8 case 3 — heavy module, opt-in path only); the matching `import type` is
|
|
152
|
+
* erased at runtime so it does not defeat the deferral.
|
|
153
|
+
*/
|
|
154
|
+
async buildBaker(transpiler, schema) {
|
|
155
|
+
const { RunCodeGenBase, SQLCodeGenBase, initializeConfig, dbPlatform } = await import('@memberjunction/codegen-lib');
|
|
156
|
+
if (dbPlatform() !== 'postgresql') {
|
|
157
|
+
this.error('migrate rebake requires DB_PLATFORM=postgresql with PG_* connection env (the working DB CodeGen is captured from).');
|
|
158
|
+
}
|
|
159
|
+
initializeConfig(process.cwd());
|
|
160
|
+
let ds;
|
|
161
|
+
try {
|
|
162
|
+
ds = await new RunCodeGenBase().setupDataSource();
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
this.error(`migrate rebake could not connect/load metadata from the working PG database: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
166
|
+
'Seed the working DB to the latest baseline before re-baking.');
|
|
167
|
+
}
|
|
168
|
+
this.log(` working DB: ${ds.connectionInfo}\n`);
|
|
169
|
+
const sqlGen = new SQLCodeGenBase();
|
|
170
|
+
const db = {
|
|
171
|
+
// Committed files may still carry the `${flyway:defaultSchema}` macro (Skyway substitutes it
|
|
172
|
+
// at deploy); raw pg.query can't, so substitute before applying to advance the working DB.
|
|
173
|
+
apply: async (sql) => { await ds.connection.query(sql.replaceAll('${flyway:defaultSchema}', schema)); },
|
|
174
|
+
refreshMetadata: async () => { await ds.provider.Refresh(); },
|
|
175
|
+
captureEntity: async (name) => {
|
|
176
|
+
const entity = ds.provider.EntityByName(name);
|
|
177
|
+
if (!entity)
|
|
178
|
+
throw new Error(`migrate rebake: entity not found in working-DB metadata: ${name}`);
|
|
179
|
+
const r = await sqlGen.generateSingleEntitySQLToSeparateFiles({
|
|
180
|
+
pool: ds.connection,
|
|
181
|
+
entity,
|
|
182
|
+
directory: os.tmpdir(),
|
|
183
|
+
onlyPermissions: false,
|
|
184
|
+
writeFiles: false,
|
|
185
|
+
skipExecution: true, // READ-ONLY: capture only; the committed file advances the working DB
|
|
186
|
+
});
|
|
187
|
+
return { sql: r.sql ?? '', permissionsSQL: r.permissionsSQL ?? '' };
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
return { baker: new IncrementalBaker({ transpiler, db, schema }), dispose: async () => { } };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=rebake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rebake.js","sourceRoot":"","sources":["../../../src/commands/migrate/rebake.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Phase 3 convenience: register a third-party CLI plugin by appending its
|
|
4
|
+
* package specifier to `mj-cli-plugins.json` (created if absent). No MJCLI
|
|
5
|
+
* changes needed to extend the CLI — add a package, register it here (plan D9).
|
|
6
|
+
*/
|
|
7
|
+
export default class PluginAdd extends Command {
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static args: {
|
|
11
|
+
package: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
static flags: {
|
|
14
|
+
format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=add.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/commands/plugin/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,EAAS,MAAM,aAAa,CAAC;AAKnD;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO;IAC5C,MAAM,CAAC,WAAW,SAAuF;IAEzG,MAAM,CAAC,QAAQ,WAGb;IAEF,MAAM,CAAC,IAAI;;MAET;IAEF,MAAM,CAAC,KAAK;;MAEV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC3B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { PLUGIN_CONFIG_FILENAME } from '@memberjunction/cli-core';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
/**
|
|
6
|
+
* Phase 3 convenience: register a third-party CLI plugin by appending its
|
|
7
|
+
* package specifier to `mj-cli-plugins.json` (created if absent). No MJCLI
|
|
8
|
+
* changes needed to extend the CLI — add a package, register it here (plan D9).
|
|
9
|
+
*/
|
|
10
|
+
export default class PluginAdd extends Command {
|
|
11
|
+
static { this.description = 'Register a CLI plugin package in mj-cli-plugins.json so `mj` loads it at startup.'; }
|
|
12
|
+
static { this.examples = [
|
|
13
|
+
`<%= config.bin %> <%= command.id %> @my-org/mj-plugin/plugins`,
|
|
14
|
+
`<%= config.bin %> <%= command.id %> ./local-plugins/my-tool`,
|
|
15
|
+
]; }
|
|
16
|
+
static { this.args = {
|
|
17
|
+
package: Args.string({ required: true, description: 'Plugin entry-point specifier (package name, subpath, or relative path)' }),
|
|
18
|
+
}; }
|
|
19
|
+
static { this.flags = {
|
|
20
|
+
format: Flags.string({ options: ['text', 'json'], default: 'text', description: 'Output format' }),
|
|
21
|
+
}; }
|
|
22
|
+
async run() {
|
|
23
|
+
const { args, flags } = await this.parse(PluginAdd);
|
|
24
|
+
const configPath = resolve(process.cwd(), PLUGIN_CONFIG_FILENAME);
|
|
25
|
+
// Preserve any other keys (e.g. "$comment") when rewriting.
|
|
26
|
+
const config = existsSync(configPath)
|
|
27
|
+
? JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
28
|
+
: {};
|
|
29
|
+
const plugins = Array.isArray(config.plugins) ? config.plugins : [];
|
|
30
|
+
const alreadyPresent = plugins.includes(args.package);
|
|
31
|
+
if (!alreadyPresent) {
|
|
32
|
+
plugins.push(args.package);
|
|
33
|
+
config.plugins = plugins;
|
|
34
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
35
|
+
}
|
|
36
|
+
const result = {
|
|
37
|
+
success: true,
|
|
38
|
+
command: 'plugin:add',
|
|
39
|
+
durationSeconds: 0,
|
|
40
|
+
data: { package: args.package, configPath, alreadyPresent, plugins },
|
|
41
|
+
};
|
|
42
|
+
if (flags.format === 'json') {
|
|
43
|
+
this.log(JSON.stringify(result, null, 2));
|
|
44
|
+
}
|
|
45
|
+
else if (alreadyPresent) {
|
|
46
|
+
this.log(`Plugin "${args.package}" is already registered in ${PLUGIN_CONFIG_FILENAME}.`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.log(`Registered plugin "${args.package}" in ${PLUGIN_CONFIG_FILENAME}.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=add.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/commands/plugin/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO;aACrC,gBAAW,GAAG,mFAAmF,CAAC;aAElG,aAAQ,GAAG;QAChB,+DAA+D;QAC/D,6DAA6D;KAC9D,CAAC;aAEK,SAAI,GAAG;QACZ,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wEAAwE,EAAE,CAAC;KAChI,CAAC;aAEK,UAAK,GAAG;QACb,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;KACnG,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,CAAC;QAElE,4DAA4D;QAC5D,MAAM,MAAM,GAA4B,UAAU,CAAC,UAAU,CAAC;YAC5D,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAA6B;YAC5E,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,OAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;QAElF,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;YACzB,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,MAAM,GAAG;YACb,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,YAAY;YACrB,eAAe,EAAE,CAAC;YAClB,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE;SACrE,CAAC;QAEF,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,cAAc,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,OAAO,8BAA8B,sBAAsB,GAAG,CAAC,CAAC;QAC3F,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,OAAO,QAAQ,sBAAsB,GAAG,CAAC,CAAC;QAChF,CAAC;IACH,CAAC"}
|
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
filter: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
-
'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
-
'multi-file': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
-
verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
'no-validate': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
-
'update-existing': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
-
'create-new': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
'backup-before-update': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
15
|
-
'merge-strategy': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
|
-
'backup-directory': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
|
-
'preserve-fields': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
|
-
'exclude-fields': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
-
'target-dir': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
-
incremental: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
|
-
since: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
-
};
|
|
23
|
-
run(): Promise<void>;
|
|
24
|
-
private findEntityDirectories;
|
|
25
|
-
private findAllEntityDirectoriesRecursive;
|
|
26
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Thin oclif shim — the real logic lives in {@link SyncPullPlugin} in
|
|
3
|
+
* `@memberjunction/metadata-sync/plugins` (plan §3). See sync/push.ts for the
|
|
4
|
+
* shim rationale.
|
|
5
|
+
*/
|
|
6
|
+
export { SyncPullPlugin as default } from '@memberjunction/metadata-sync/plugins';
|
|
27
7
|
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/pull.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,cAAc,IAAI,OAAO,EAAE,MAAM,uCAAuC,CAAC"}
|
|
@@ -1,282 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export default class Pull extends Command {
|
|
8
|
-
static { this.description = 'Pull metadata from database to local files'; }
|
|
9
|
-
static { this.examples = [
|
|
10
|
-
`<%= config.bin %> <%= command.id %> --entity="MJ: AI Prompts"`,
|
|
11
|
-
`<%= config.bin %> <%= command.id %> --entity="MJ: AI Prompts" --filter="CategoryID='customer-service-id'"`,
|
|
12
|
-
`<%= config.bin %> <%= command.id %> --entity="MJ: AI Agents" --merge-strategy=overwrite`,
|
|
13
|
-
`<%= config.bin %> <%= command.id %> --entity="Actions" --target-dir=./custom-actions --no-validate`,
|
|
14
|
-
`<%= config.bin %> <%= command.id %> --entity="Templates" --dry-run --verbose`,
|
|
15
|
-
`<%= config.bin %> <%= command.id %> --entity="MJ: AI Prompts" --exclude-fields=InternalNotes,DebugInfo`,
|
|
16
|
-
]; }
|
|
17
|
-
static { this.flags = {
|
|
18
|
-
entity: Flags.string({ description: 'Entity name to pull', required: true }),
|
|
19
|
-
filter: Flags.string({ description: 'Additional filter for pulling specific records' }),
|
|
20
|
-
'dry-run': Flags.boolean({ description: 'Show what would be pulled without actually pulling' }),
|
|
21
|
-
'multi-file': Flags.string({ description: 'Create a single file with multiple records (provide filename)' }),
|
|
22
|
-
verbose: Flags.boolean({ char: 'v', description: 'Show detailed output' }),
|
|
23
|
-
'no-validate': Flags.boolean({ description: 'Skip validation before pull' }),
|
|
24
|
-
'update-existing': Flags.boolean({ description: 'Update existing records during pull', default: true }),
|
|
25
|
-
'create-new': Flags.boolean({ description: 'Create new files for records not found locally', default: true }),
|
|
26
|
-
'backup-before-update': Flags.boolean({ description: 'Create backups before updating files', default: true }),
|
|
27
|
-
'merge-strategy': Flags.string({
|
|
28
|
-
description: 'Merge strategy for updates',
|
|
29
|
-
options: ['merge', 'overwrite', 'skip'],
|
|
30
|
-
default: 'merge'
|
|
31
|
-
}),
|
|
32
|
-
'backup-directory': Flags.string({ description: 'Custom backup directory (default: .backups)' }),
|
|
33
|
-
'preserve-fields': Flags.string({ description: 'Comma-separated list of fields to preserve during updates', multiple: true }),
|
|
34
|
-
'exclude-fields': Flags.string({ description: 'Comma-separated list of fields to exclude from pull', multiple: true }),
|
|
35
|
-
'target-dir': Flags.string({ description: 'Specific target directory (overrides auto-discovery)' }),
|
|
36
|
-
incremental: Flags.boolean({
|
|
37
|
-
description: 'Only pull records updated since last successful pull',
|
|
38
|
-
default: false
|
|
39
|
-
}),
|
|
40
|
-
since: Flags.string({
|
|
41
|
-
description: 'Only pull records updated after this ISO 8601 timestamp (e.g., 2026-04-07T10:00:00Z)'
|
|
42
|
-
}),
|
|
43
|
-
}; }
|
|
44
|
-
async run() {
|
|
45
|
-
const { PullService, ValidationService, FormattingService, loadMJConfig, initializeProvider, getSyncEngine, getSystemUser, resetSyncEngine, configManager, loadEntityConfig, FileBackupManager, } = await import('@memberjunction/metadata-sync');
|
|
46
|
-
const { flags } = await this.parse(Pull);
|
|
47
|
-
const spinner = ora();
|
|
48
|
-
let backupManager = null;
|
|
49
|
-
try {
|
|
50
|
-
// Load MJ config first
|
|
51
|
-
spinner.start('Loading configuration');
|
|
52
|
-
const mjConfig = loadMJConfig();
|
|
53
|
-
if (!mjConfig) {
|
|
54
|
-
this.error('No mj.config.cjs found in current directory or parent directories');
|
|
55
|
-
}
|
|
56
|
-
// Stop spinner before provider initialization
|
|
57
|
-
spinner.stop();
|
|
58
|
-
// Initialize data provider
|
|
59
|
-
await initializeProvider(mjConfig);
|
|
60
|
-
// Get singleton sync engine
|
|
61
|
-
const syncEngine = await getSyncEngine(getSystemUser());
|
|
62
|
-
// Show success after initialization
|
|
63
|
-
if (flags.verbose) {
|
|
64
|
-
spinner.succeed('Configuration and metadata loaded');
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
spinner.stop();
|
|
68
|
-
}
|
|
69
|
-
// Find entity directories
|
|
70
|
-
const entityDirectories = await this.findEntityDirectories(flags.entity, configManager, loadEntityConfig);
|
|
71
|
-
if (entityDirectories.length === 0) {
|
|
72
|
-
this.error(`No directories found for entity "${flags.entity}". Make sure the entity configuration exists in a .mj-sync.json file.`);
|
|
73
|
-
}
|
|
74
|
-
// Select target directory
|
|
75
|
-
let targetDir = flags['target-dir'];
|
|
76
|
-
if (!targetDir) {
|
|
77
|
-
if (entityDirectories.length === 1) {
|
|
78
|
-
targetDir = entityDirectories[0];
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
targetDir = await select({
|
|
82
|
-
message: `Multiple directories found for entity "${flags.entity}". Which one to use?`,
|
|
83
|
-
choices: entityDirectories.map(dir => ({ name: dir, value: dir }))
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Run validation on target directory unless disabled
|
|
88
|
-
if (!flags['no-validate']) {
|
|
89
|
-
spinner.start('Validating target directory...');
|
|
90
|
-
const validator = new ValidationService({ verbose: flags.verbose });
|
|
91
|
-
const formatter = new FormattingService();
|
|
92
|
-
const validationResult = await validator.validateDirectory(targetDir);
|
|
93
|
-
spinner.stop();
|
|
94
|
-
if (!validationResult.isValid || validationResult.warnings.length > 0) {
|
|
95
|
-
// Show validation results
|
|
96
|
-
this.log('\n' + formatter.formatValidationResult(validationResult, flags.verbose));
|
|
97
|
-
if (!validationResult.isValid) {
|
|
98
|
-
// Ask for confirmation
|
|
99
|
-
const shouldContinue = await select({
|
|
100
|
-
message: 'Validation failed with errors. Do you want to continue anyway?',
|
|
101
|
-
choices: [
|
|
102
|
-
{ name: 'No, fix the errors first', value: false },
|
|
103
|
-
{ name: 'Yes, continue anyway', value: true }
|
|
104
|
-
],
|
|
105
|
-
default: false
|
|
106
|
-
});
|
|
107
|
-
if (!shouldContinue) {
|
|
108
|
-
this.error('Pull cancelled due to validation errors.');
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
this.log(chalk.green('✓ Validation passed'));
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// Initialize backup manager if backups are enabled
|
|
117
|
-
if (flags['backup-before-update']) {
|
|
118
|
-
backupManager = new FileBackupManager();
|
|
119
|
-
await backupManager.initialize();
|
|
120
|
-
}
|
|
121
|
-
// Create pull service with optional state manager for incremental sync
|
|
122
|
-
const pullService = new PullService(syncEngine, getSystemUser());
|
|
123
|
-
if (flags.incremental || flags.since) {
|
|
124
|
-
const { SyncStateManager } = await import('@memberjunction/metadata-sync');
|
|
125
|
-
const stateManager = new SyncStateManager(targetDir);
|
|
126
|
-
await stateManager.load();
|
|
127
|
-
pullService.setStateManager(stateManager);
|
|
128
|
-
}
|
|
129
|
-
// Build pull options - only include CLI flags that were explicitly provided
|
|
130
|
-
const pullOptions = {
|
|
131
|
-
entity: flags.entity,
|
|
132
|
-
targetDir,
|
|
133
|
-
dryRun: flags['dry-run'],
|
|
134
|
-
verbose: flags.verbose,
|
|
135
|
-
noValidate: flags['no-validate']
|
|
136
|
-
};
|
|
137
|
-
// Add optional flags only if explicitly provided to avoid overriding entity config
|
|
138
|
-
if (flags.filter !== undefined)
|
|
139
|
-
pullOptions.filter = flags.filter;
|
|
140
|
-
if (flags['multi-file'] !== undefined)
|
|
141
|
-
pullOptions.multiFile = flags['multi-file'];
|
|
142
|
-
if (flags['update-existing'] !== undefined)
|
|
143
|
-
pullOptions.updateExistingRecords = flags['update-existing'];
|
|
144
|
-
if (flags['create-new'] !== undefined)
|
|
145
|
-
pullOptions.createNewFileIfNotFound = flags['create-new'];
|
|
146
|
-
if (flags['backup-before-update'] !== undefined)
|
|
147
|
-
pullOptions.backupBeforeUpdate = flags['backup-before-update'];
|
|
148
|
-
if (flags['merge-strategy'] !== undefined)
|
|
149
|
-
pullOptions.mergeStrategy = flags['merge-strategy'];
|
|
150
|
-
if (flags['backup-directory'] !== undefined)
|
|
151
|
-
pullOptions.backupDirectory = flags['backup-directory'];
|
|
152
|
-
if (flags['preserve-fields'] !== undefined)
|
|
153
|
-
pullOptions.preserveFields = flags['preserve-fields'];
|
|
154
|
-
if (flags['exclude-fields'] !== undefined)
|
|
155
|
-
pullOptions.excludeFields = flags['exclude-fields'];
|
|
156
|
-
if (flags.incremental)
|
|
157
|
-
pullOptions.incremental = flags.incremental;
|
|
158
|
-
if (flags.since !== undefined)
|
|
159
|
-
pullOptions.since = flags.since;
|
|
160
|
-
await pullService.pull(pullOptions, {
|
|
161
|
-
onProgress: (message) => {
|
|
162
|
-
spinner.start(message);
|
|
163
|
-
},
|
|
164
|
-
onSuccess: (message) => {
|
|
165
|
-
spinner.succeed(message);
|
|
166
|
-
},
|
|
167
|
-
onError: (message) => {
|
|
168
|
-
spinner.fail(message);
|
|
169
|
-
},
|
|
170
|
-
onWarn: (message) => {
|
|
171
|
-
this.warn(message);
|
|
172
|
-
},
|
|
173
|
-
onLog: (message) => {
|
|
174
|
-
this.log(message);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
// Clean up backups on success
|
|
178
|
-
if (backupManager && !flags['dry-run']) {
|
|
179
|
-
await backupManager.cleanup();
|
|
180
|
-
if (flags.verbose) {
|
|
181
|
-
this.log(chalk.green('✓ Temporary backup files cleaned up'));
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Clean up persistent backup files created by PullService
|
|
185
|
-
if (!flags['dry-run']) {
|
|
186
|
-
try {
|
|
187
|
-
const backupCount = pullService.getCreatedBackupFiles().length;
|
|
188
|
-
await pullService.cleanupBackupFiles();
|
|
189
|
-
if (flags.verbose && backupCount > 0) {
|
|
190
|
-
this.log(chalk.green(`✓ Cleaned up ${backupCount} persistent backup files`));
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
catch (cleanupError) {
|
|
194
|
-
this.warn(`Failed to cleanup persistent backup files: ${cleanupError}`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (!flags['dry-run']) {
|
|
198
|
-
this.log(`\n✅ Pull completed successfully`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
spinner.fail('Pull failed');
|
|
203
|
-
// Rollback backups on error
|
|
204
|
-
if (backupManager) {
|
|
205
|
-
try {
|
|
206
|
-
await backupManager.rollback();
|
|
207
|
-
if (flags.verbose) {
|
|
208
|
-
this.log(chalk.yellow('↩️ Backup files restored'));
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
catch (rollbackError) {
|
|
212
|
-
this.warn(`Failed to rollback backup files: ${rollbackError}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// Enhanced error logging
|
|
216
|
-
this.log('\n=== Pull Error Details ===');
|
|
217
|
-
this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
|
|
218
|
-
this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
|
|
219
|
-
if (error instanceof Error && error.stack) {
|
|
220
|
-
this.log(`\nStack trace:`);
|
|
221
|
-
this.log(error.stack);
|
|
222
|
-
}
|
|
223
|
-
// Context information
|
|
224
|
-
this.log(`\nContext:`);
|
|
225
|
-
this.log(`- Working directory: ${process.cwd()}`);
|
|
226
|
-
this.log(`- Entity: ${flags.entity || 'not specified'}`);
|
|
227
|
-
this.log(`- Filter: ${flags.filter || 'none'}`);
|
|
228
|
-
this.error(error);
|
|
229
|
-
}
|
|
230
|
-
finally {
|
|
231
|
-
// Reset singletons
|
|
232
|
-
resetSyncEngine();
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async findEntityDirectories(entityName, configMgr, loadConfig) {
|
|
236
|
-
try {
|
|
237
|
-
const workingDir = configMgr.getOriginalCwd();
|
|
238
|
-
// Search recursively for all directories with .mj-sync.json files
|
|
239
|
-
const allDirs = this.findAllEntityDirectoriesRecursive(workingDir);
|
|
240
|
-
// Filter directories that match the requested entity
|
|
241
|
-
const entityDirs = [];
|
|
242
|
-
for (const dir of allDirs) {
|
|
243
|
-
try {
|
|
244
|
-
const config = await loadConfig(dir);
|
|
245
|
-
if (config && config.entity === entityName) {
|
|
246
|
-
entityDirs.push(dir);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
this.warn(`Skipping directory ${dir}: invalid configuration (${error})`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return entityDirs;
|
|
254
|
-
}
|
|
255
|
-
catch (error) {
|
|
256
|
-
this.error(`Failed to find entity directories: ${error}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
findAllEntityDirectoriesRecursive(dir) {
|
|
260
|
-
const directories = [];
|
|
261
|
-
try {
|
|
262
|
-
// Check if current directory has .mj-sync.json
|
|
263
|
-
const syncConfigPath = path.join(dir, '.mj-sync.json');
|
|
264
|
-
if (fs.existsSync(syncConfigPath)) {
|
|
265
|
-
directories.push(dir);
|
|
266
|
-
}
|
|
267
|
-
// Recursively search subdirectories
|
|
268
|
-
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
269
|
-
for (const item of items) {
|
|
270
|
-
if (item.isDirectory() && !item.name.startsWith('.')) {
|
|
271
|
-
const subdirPath = path.join(dir, item.name);
|
|
272
|
-
directories.push(...this.findAllEntityDirectoriesRecursive(subdirPath));
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
// Skip directories we can't read
|
|
278
|
-
}
|
|
279
|
-
return directories;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Thin oclif shim — the real logic lives in {@link SyncPullPlugin} in
|
|
3
|
+
* `@memberjunction/metadata-sync/plugins` (plan §3). See sync/push.ts for the
|
|
4
|
+
* shim rationale.
|
|
5
|
+
*/
|
|
6
|
+
export { SyncPullPlugin as default } from '@memberjunction/metadata-sync/plugins';
|
|
282
7
|
//# sourceMappingURL=pull.js.map
|