@memberjunction/open-app-engine 6.2.0-edge.6 → 6.2.0-edge.8

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.
@@ -12,7 +12,7 @@ import { OfferTemplateUser } from './template-user.js';
12
12
  import { ExtractApplicationIds } from './migration-application-ids.js';
13
13
  import { CreateAppTempDir } from './temp-dir.js';
14
14
  import { RunAppMigrations } from './migration-runner.js';
15
- import { AddAppPackages, RemoveAppPackages, RunPackageInstallAsync, BumpPrefixedDependencies } from './package-manager.js';
15
+ import { AddAppPackages, RemoveAppPackages, RunPackageInstallAsync, BumpPrefixedDependencies, PreflightAppPackages } from './package-manager.js';
16
16
  import { BuildHookResolutionBases, ResolveHookModule } from './hook-module-resolver.js';
17
17
  import { AddServerDynamicPackages, AddClientDynamicPackages, RemoveServerDynamicPackages, PruneDynamicPackagesNotInManifest, ToggleServerDynamicPackages, AddEntityPackageMapping, RemoveEntityPackageMapping, AddExcludeSchema, RemoveExcludeSchema } from './config-manager.js';
18
18
  import { AngularConfigManager } from './angular-config-manager.js';
@@ -56,6 +56,7 @@ export async function InstallApp(options, context) {
56
56
  const { Callbacks } = context;
57
57
  let createdAppId;
58
58
  let manifest;
59
+ let teardownScripts;
59
60
  const subpath = options.Subpath ?? ParseGitHubUrl(options.Source)?.Subpath;
60
61
  try {
61
62
  const explicitVersion = options.Version;
@@ -84,6 +85,10 @@ export async function InstallApp(options, context) {
84
85
  if (!compatResult.Compatible) {
85
86
  return BuildFailureResult('Install', manifest.name, manifest.version, 'Schema', startTime, compatResult.Message ?? 'Incompatible MJ version');
86
87
  }
88
+ const preflight = await PreflightManifestPackages(manifest, context, effectivePackageVersion, effectiveVersionStrategy);
89
+ if (!preflight.Success) {
90
+ return BuildFailureResult('Install', manifest.name, manifest.version, 'Packages', startTime, preflight.ErrorMessage ?? 'Package preflight failed');
91
+ }
87
92
  if (!options._skipDependencyResolution) {
88
93
  const depResult = await ResolveDependencyChain(manifest, context);
89
94
  if (!depResult.Success) {
@@ -138,16 +143,19 @@ export async function InstallApp(options, context) {
138
143
  await PersistCanonicalSchemaName(manifest, context);
139
144
  }
140
145
  if (manifest.migrations && manifest.schema) {
146
+ if (schemaCreated) {
147
+ teardownScripts = await PrefetchTeardownScripts(manifest, context, subpath);
148
+ }
141
149
  const migrationResult = await HandleMigrations(manifest, context, subpath);
142
150
  if (!migrationResult.Success) {
143
- await CompensateSchemaOnFailure(manifest, context, schemaCreated, options.AllowDoubleUnderscoreSchema === true, Callbacks, subpath);
151
+ await CompensateSchemaOnFailure(manifest, context, schemaCreated, options.AllowDoubleUnderscoreSchema === true, Callbacks, subpath, teardownScripts);
144
152
  return BuildFailureResult('Install', manifest.name, manifest.version, 'Migration', startTime, migrationResult.ErrorMessage ?? 'Migration failed');
145
153
  }
146
154
  }
147
155
  Callbacks?.OnProgress?.('Record', 'Recording app installation...');
148
156
  const recordResult = await RecordInstallationAtomically(context.ContextUser, manifest, Callbacks, undefined, subpath);
149
157
  if (!recordResult.Success) {
150
- await CompensateSchemaOnFailure(manifest, context, schemaCreated, options.AllowDoubleUnderscoreSchema === true, Callbacks, subpath);
158
+ await CompensateSchemaOnFailure(manifest, context, schemaCreated, options.AllowDoubleUnderscoreSchema === true, Callbacks, subpath, teardownScripts);
151
159
  return BuildFailureResult('Install', manifest.name, manifest.version, 'Record', startTime, recordResult.ErrorMessage ?? 'Failed to record installation');
152
160
  }
153
161
  createdAppId = recordResult.AppId;
@@ -267,6 +275,9 @@ export async function InstallApp(options, context) {
267
275
  Callbacks?.OnError?.('Install', message);
268
276
  return BuildFailureResult('Install', options.Source, '', phase, startTime, message);
269
277
  }
278
+ finally {
279
+ ReleaseTeardownScripts(teardownScripts, context);
280
+ }
270
281
  }
271
282
  async function RecordInstallationAtomically(contextUser, manifest, callbacks, provider, subpath) {
272
283
  const md = (provider ?? new Metadata());
@@ -289,7 +300,7 @@ async function RecordInstallationAtomically(contextUser, manifest, callbacks, pr
289
300
  return { Success: false, ErrorMessage: `Transaction failed: ${message}` };
290
301
  }
291
302
  }
292
- async function CompensateSchemaOnFailure(manifest, context, schemaWasCreated, allowDoubleUnderscore, callbacks, subpath) {
303
+ async function CompensateSchemaOnFailure(manifest, context, schemaWasCreated, allowDoubleUnderscore, callbacks, subpath, teardownScripts) {
293
304
  if (!schemaWasCreated || !manifest.schema) {
294
305
  return;
295
306
  }
@@ -310,37 +321,65 @@ async function CompensateSchemaOnFailure(manifest, context, schemaWasCreated, al
310
321
  const msg = error instanceof Error ? error.message : String(error);
311
322
  callbacks?.OnError?.('Rollback', `Failed to remove entity metadata for '${schemaName}' during rollback: ${msg}`);
312
323
  }
313
- if (manifest.migrations?.teardownDirectory) {
314
- try {
315
- const teardownResult = await HandleTeardown(manifest, context, subpath);
316
- if (!teardownResult.Success) {
317
- callbacks?.OnError?.('Rollback', `Teardown failed during rollback: ${teardownResult.ErrorMessage ?? 'unknown error'}`);
318
- }
319
- }
320
- catch (error) {
321
- const msg = error instanceof Error ? error.message : String(error);
322
- callbacks?.OnError?.('Rollback', `Teardown failed during rollback: ${msg}`);
324
+ const dropOptions = { allowDoubleUnderscore, CoreSchema: context.MJCoreSchema };
325
+ if (!manifest.migrations?.teardownDirectory) {
326
+ if (manifest.migrations) {
327
+ callbacks?.OnWarn?.('Rollback', `'${manifest.name}' declares migrations but no teardownDirectory — rows its migrations wrote to the shared core schema WILL remain after this rollback. ` +
328
+ `If those rows were seeded with fixed IDs, reinstalling this app may fail with a primary-key violation until they are removed manually.`);
323
329
  }
330
+ await DropSchemaAndReport(schemaName, manifest.name, 'Rollback', dropOptions, context);
331
+ return;
324
332
  }
325
- else if (manifest.migrations) {
326
- callbacks?.OnWarn?.('Rollback', `'${manifest.name}' declares migrations but no teardownDirectory — rows its migrations wrote to the shared core schema WILL remain after this rollback. ` +
327
- `If those rows were seeded with fixed IDs, reinstalling this app may fail with a primary-key violation until they are removed manually.`);
333
+ const firstAttempt = await RunRollbackTeardown(manifest, context, teardownScripts, subpath);
334
+ const dropResult = await DropSchemaAndReport(schemaName, manifest.name, 'Rollback', dropOptions, context);
335
+ if (firstAttempt.Success) {
336
+ return;
328
337
  }
338
+ if (!dropResult.Success) {
339
+ callbacks?.OnError?.('Rollback', `Teardown failed during rollback: ${firstAttempt.ErrorMessage ?? 'unknown error'}`);
340
+ return;
341
+ }
342
+ callbacks?.OnProgress?.('Rollback', `Teardown failed with schema '${schemaName}' in place; running it again now that the schema is dropped...`);
343
+ const secondAttempt = await RunRollbackTeardown(manifest, context, teardownScripts, subpath);
344
+ if (!secondAttempt.Success) {
345
+ callbacks?.OnError?.('Rollback', `Teardown failed during rollback, before and after dropping schema '${schemaName}'. ` +
346
+ `With the schema: ${firstAttempt.ErrorMessage ?? 'unknown error'}. Without it: ${secondAttempt.ErrorMessage ?? 'unknown error'}`);
347
+ }
348
+ }
349
+ async function RunRollbackTeardown(manifest, context, teardownScripts, subpath) {
329
350
  try {
330
- callbacks?.OnProgress?.('Rollback', `Dropping schema '${schemaName}'...`);
331
- const dropResult = await DropAppSchema(schemaName, context.DatabaseProvider, { allowDoubleUnderscore });
332
- if (!dropResult.Success) {
333
- callbacks?.OnError?.('Rollback', `Failed to drop schema '${schemaName}' during rollback: ${dropResult.ErrorMessage ?? 'unknown error'}`);
334
- }
335
- else {
336
- callbacks?.OnProgress?.('Rollback', `Schema '${schemaName}' dropped successfully`);
337
- }
351
+ return teardownScripts
352
+ ? await RunTeardownScripts(manifest, context, teardownScripts)
353
+ : await HandleTeardown(manifest, context, subpath);
338
354
  }
339
- catch (rollbackError) {
340
- const msg = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
341
- callbacks?.OnError?.('Rollback', `Failed to drop schema '${schemaName}' during rollback: ${msg}`);
355
+ catch (error) {
356
+ return { Success: false, ErrorMessage: error instanceof Error ? error.message : String(error) };
342
357
  }
343
358
  }
359
+ export async function DropSchemaAndReport(schemaName, appName, phase, options, context) {
360
+ const callbacks = context.Callbacks;
361
+ const nameCheck = ValidateSchemaName(schemaName, options);
362
+ if (!nameCheck.Success) {
363
+ const refusal = BuildSchemaDropRefusalMessage(nameCheck, appName, schemaName);
364
+ callbacks?.OnError?.(phase, refusal);
365
+ return { Success: false, ErrorMessage: refusal };
366
+ }
367
+ callbacks?.OnProgress?.(phase, `Dropping schema '${schemaName}'...`);
368
+ let result;
369
+ try {
370
+ result = await DropAppSchema(schemaName, context.DatabaseProvider, options);
371
+ }
372
+ catch (error) {
373
+ const message = error instanceof Error ? error.message : String(error);
374
+ result = { Success: false, ErrorMessage: `Failed to drop schema '${schemaName}': ${message}` };
375
+ }
376
+ if (!result.Success) {
377
+ callbacks?.OnError?.(phase, `Schema '${schemaName}' was not dropped: ${result.ErrorMessage ?? 'unknown error'}`);
378
+ return result;
379
+ }
380
+ callbacks?.OnProgress?.(phase, `Schema '${schemaName}' dropped successfully`);
381
+ return result;
382
+ }
344
383
  export async function UpgradeApp(options, context) {
345
384
  const startTime = Date.now();
346
385
  const { Callbacks } = context;
@@ -401,6 +440,7 @@ export async function UpgradeApp(options, context) {
401
440
  if (manifest.schema) {
402
441
  const schemaValidation = ValidateSchemaName(manifest.schema.name, {
403
442
  allowDoubleUnderscore: options.AllowDoubleUnderscoreSchema === true,
443
+ CoreSchema: context.MJCoreSchema,
404
444
  });
405
445
  if (!schemaValidation.Success) {
406
446
  return BuildFailureResult('Upgrade', options.AppName, targetVersion, 'Schema', startTime, schemaValidation.ErrorMessage ?? 'Invalid schema name');
@@ -412,6 +452,12 @@ export async function UpgradeApp(options, context) {
412
452
  return BuildFailureResult('Upgrade', options.AppName, targetVersion, 'Schema', startTime, migratable.ErrorMessage ?? 'Cannot run migrations in the app schema');
413
453
  }
414
454
  }
455
+ const effectiveUpgradeVersion = explicitUpgradeVersion ? targetVersion.replace(/^v/, '') : manifest.version;
456
+ const effectiveUpgradeStrategy = explicitUpgradeVersion ? 'exact' : context.VersionStrategy;
457
+ const preflight = await PreflightManifestPackages(manifest, context, effectiveUpgradeVersion, effectiveUpgradeStrategy);
458
+ if (!preflight.Success) {
459
+ return BuildFailureResult('Upgrade', options.AppName, targetVersion, 'Packages', startTime, preflight.ErrorMessage ?? 'Package preflight failed');
460
+ }
415
461
  if (manifest.dependencies && Object.keys(manifest.dependencies).length > 0) {
416
462
  const depResult = await ResolveDependencyChain(manifest, context);
417
463
  if (!depResult.Success) {
@@ -455,8 +501,6 @@ export async function UpgradeApp(options, context) {
455
501
  }
456
502
  let npmInstallWarning;
457
503
  if (!IsStepDone(UPGRADE_STEP_ORDER, resumeCheckpoint, 'PackagesInstalled')) {
458
- const effectiveUpgradeVersion = explicitUpgradeVersion ? targetVersion.replace(/^v/, '') : manifest.version;
459
- const effectiveUpgradeStrategy = explicitUpgradeVersion ? 'exact' : context.VersionStrategy;
460
504
  const pkgResult = await HandlePackageInstallation(manifest, context, effectiveUpgradeVersion, effectiveUpgradeStrategy, options.Verbose);
461
505
  if (!pkgResult.Success) {
462
506
  if (pkgResult.PackageJsonUpdated) {
@@ -668,23 +712,11 @@ export async function RemoveApp(options, context) {
668
712
  }
669
713
  let schemaDropError;
670
714
  if (!options.KeepData && existingApp.SchemaName && !schemaShared) {
671
- const nameCheck = ValidateSchemaName(existingApp.SchemaName, {
715
+ const dropResult = await DropSchemaAndReport(existingApp.SchemaName, existingApp.Name, 'Schema', {
672
716
  allowDoubleUnderscore: options.AllowDoubleUnderscoreSchema === true,
673
- });
674
- if (!nameCheck.Success) {
675
- schemaDropError = BuildSchemaDropRefusalMessage(nameCheck, existingApp.Name, existingApp.SchemaName);
676
- Callbacks?.OnError?.('Schema', schemaDropError);
677
- }
678
- else {
679
- Callbacks?.OnProgress?.('Schema', `Dropping schema '${existingApp.SchemaName}'...`);
680
- const dropResult = await DropAppSchema(existingApp.SchemaName, context.DatabaseProvider, {
681
- allowDoubleUnderscore: options.AllowDoubleUnderscoreSchema === true,
682
- });
683
- if (!dropResult.Success) {
684
- schemaDropError = dropResult.ErrorMessage;
685
- Callbacks?.OnError?.('Schema', `Failed to drop schema: ${dropResult.ErrorMessage}`);
686
- }
687
- }
717
+ CoreSchema: context.MJCoreSchema,
718
+ }, context);
719
+ schemaDropError = dropResult.Success ? undefined : dropResult.ErrorMessage;
688
720
  }
689
721
  const removalErrors = [
690
722
  shareCheckError,
@@ -859,18 +891,18 @@ async function ResolveDependencyChain(manifest, context) {
859
891
  Repository: manifest.repository,
860
892
  Dependencies: dependencies,
861
893
  };
862
- const result = await ResolveDependencyGraph(root, installedMap, BuildManifestFetcher(context));
894
+ const result = await ResolveDependencyGraph(root, installedMap, BuildManifestFetcher(context), (repoUrl, versionRange) => ResolveDependencyVersion(repoUrl, versionRange, context.GitHubOptions));
863
895
  if (!result.Success) {
864
896
  return { Success: false, ErrorMessage: result.ErrorMessage };
865
897
  }
866
898
  const depsToInstall = (result.InstallOrder ?? [])
867
899
  .filter((d) => !d.AlreadyInstalled)
868
- .map((d) => ({ AppName: d.AppName, Repository: d.Repository, VersionRange: d.VersionRange, Subpath: d.Subpath }));
900
+ .map((d) => ({ AppName: d.AppName, Repository: d.Repository, VersionRange: d.VersionRange, Subpath: d.Subpath, Version: d.ResolvedVersion }));
869
901
  return { Success: true, DepsToInstall: depsToInstall };
870
902
  }
871
903
  function BuildManifestFetcher(context) {
872
- return async (repoUrl, subpath) => {
873
- const fetched = await FetchManifestFromGitHub(repoUrl, undefined, context.GitHubOptions, subpath);
904
+ return async (repoUrl, subpath, version) => {
905
+ const fetched = await FetchManifestFromGitHub(repoUrl, version, context.GitHubOptions, subpath);
874
906
  if (!fetched.Success || !fetched.ManifestJSON) {
875
907
  return { Success: false, ErrorMessage: fetched.ErrorMessage ?? 'Failed to fetch manifest' };
876
908
  }
@@ -883,6 +915,7 @@ function BuildManifestFetcher(context) {
883
915
  Manifest: {
884
916
  name: parsed.Manifest.name,
885
917
  repository: parsed.Manifest.repository,
918
+ version: parsed.Manifest.version,
886
919
  dependencies: (parsed.Manifest.dependencies ?? {}),
887
920
  },
888
921
  };
@@ -897,14 +930,10 @@ async function InstallDependencies(deps, context, inherited) {
897
930
  };
898
931
  }
899
932
  context.Callbacks?.OnProgress?.('Dependencies', `Installing dependency from ${dep.Repository}...`);
900
- const resolved = await ResolveDependencyVersion(dep.Repository, dep.VersionRange, context.GitHubOptions);
901
- if (resolved.ErrorMessage) {
902
- return { Success: false, ErrorMessage: `Cannot install dependency '${dep.AppName}': ${resolved.ErrorMessage}` };
903
- }
904
933
  const result = await InstallApp({
905
934
  Source: dep.Repository,
906
935
  Subpath: dep.Subpath,
907
- Version: resolved.Version,
936
+ Version: dep.Version,
908
937
  _skipDependencyResolution: true,
909
938
  AllowDoubleUnderscoreSchema: inherited.AllowDoubleUnderscoreSchema,
910
939
  Verbose: inherited.Verbose,
@@ -961,7 +990,10 @@ async function HandleSchemaCreation(manifest, context, options) {
961
990
  if (!manifest.schema) {
962
991
  return { Success: true };
963
992
  }
964
- const validation = ValidateSchemaName(manifest.schema.name, { allowDoubleUnderscore: options.AllowDoubleUnderscore });
993
+ const validation = ValidateSchemaName(manifest.schema.name, {
994
+ allowDoubleUnderscore: options.AllowDoubleUnderscore,
995
+ CoreSchema: context.MJCoreSchema,
996
+ });
965
997
  if (!validation.Success) {
966
998
  return { Success: false, ErrorMessage: validation.ErrorMessage };
967
999
  }
@@ -1058,6 +1090,18 @@ function CleanupTempDir(tempDir, phase, context) {
1058
1090
  }
1059
1091
  }
1060
1092
  async function HandleTeardown(manifest, context, subpath) {
1093
+ const download = await DownloadTeardownScripts(manifest, context, subpath);
1094
+ if (!download.Success || !download.Scripts) {
1095
+ return download;
1096
+ }
1097
+ try {
1098
+ return await RunTeardownScripts(manifest, context, download.Scripts);
1099
+ }
1100
+ finally {
1101
+ ReleaseTeardownScripts(download.Scripts, context);
1102
+ }
1103
+ }
1104
+ async function DownloadTeardownScripts(manifest, context, subpath) {
1061
1105
  const teardownDir = manifest.migrations?.teardownDirectory;
1062
1106
  if (!teardownDir) {
1063
1107
  return { Success: true };
@@ -1069,36 +1113,61 @@ async function HandleTeardown(manifest, context, subpath) {
1069
1113
  context.Callbacks?.OnProgress?.('Metadata', 'Downloading teardown scripts...');
1070
1114
  const download = await DownloadMigrations(manifest.repository, manifest.version, dir, tempDir, context.GitHubOptions, subpath, manifest.name);
1071
1115
  if (!download.Success) {
1116
+ CleanupTempDir(tempDir, 'Metadata', context);
1072
1117
  return { Success: false, ErrorMessage: `Failed to download teardown scripts: ${download.ErrorMessage}` };
1073
1118
  }
1074
1119
  const files = (download.Files ?? []).filter((f) => f.endsWith('.sql')).sort();
1075
- if (files.length === 0) {
1076
- context.Callbacks?.OnWarn?.('Metadata', `No teardown scripts in '${dir}' — this app's rows in the shared core schema will NOT be retired on remove.`);
1077
- return { Success: true };
1120
+ return { Success: true, Scripts: { TempDir: tempDir, SourceDir: dir, Files: files } };
1121
+ }
1122
+ catch (error) {
1123
+ CleanupTempDir(tempDir, 'Metadata', context);
1124
+ throw error;
1125
+ }
1126
+ }
1127
+ async function PrefetchTeardownScripts(manifest, context, subpath) {
1128
+ try {
1129
+ const download = await DownloadTeardownScripts(manifest, context, subpath);
1130
+ if (!download.Success) {
1131
+ context.Callbacks?.OnWarn?.('Migration', `${download.ErrorMessage ?? 'Failed to download teardown scripts'}. A rollback will try to download them again.`);
1078
1132
  }
1079
- const mjSchema = context.MJCoreSchema ?? '__mj';
1080
- context.Callbacks?.OnProgress?.('Metadata', `Running ${files.length} teardown script(s) against '${mjSchema}'...`);
1081
- await context.DatabaseProvider.BeginTransaction();
1082
- try {
1083
- for (const file of files) {
1084
- const sql = readFileSync(join(tempDir, file), 'utf-8').split('${mjSchema}').join(mjSchema);
1085
- if (sql.trim()) {
1086
- await context.DatabaseProvider.ExecuteSQL(sql);
1087
- }
1133
+ return download.Scripts;
1134
+ }
1135
+ catch (error) {
1136
+ const message = error instanceof Error ? error.message : String(error);
1137
+ context.Callbacks?.OnWarn?.('Migration', `Failed to download teardown scripts: ${message}. A rollback will try to download them again.`);
1138
+ return undefined;
1139
+ }
1140
+ }
1141
+ function ReleaseTeardownScripts(scripts, context) {
1142
+ if (scripts) {
1143
+ CleanupTempDir(scripts.TempDir, 'Metadata', context);
1144
+ }
1145
+ }
1146
+ async function RunTeardownScripts(manifest, context, scripts) {
1147
+ const files = scripts.Files;
1148
+ if (files.length === 0) {
1149
+ context.Callbacks?.OnWarn?.('Metadata', `No teardown scripts in '${scripts.SourceDir}' — this app's rows in the shared core schema will NOT be retired on remove.`);
1150
+ return { Success: true };
1151
+ }
1152
+ const mjSchema = context.MJCoreSchema ?? '__mj';
1153
+ context.Callbacks?.OnProgress?.('Metadata', `Running ${files.length} teardown script(s) against '${mjSchema}'...`);
1154
+ await context.DatabaseProvider.BeginTransaction();
1155
+ try {
1156
+ for (const file of files) {
1157
+ const sql = readFileSync(join(scripts.TempDir, file), 'utf-8').split('${mjSchema}').join(mjSchema);
1158
+ if (sql.trim()) {
1159
+ await context.DatabaseProvider.ExecuteSQL(sql);
1088
1160
  }
1089
- await context.DatabaseProvider.CommitTransaction();
1090
1161
  }
1091
- catch (error) {
1092
- await context.DatabaseProvider.RollbackTransaction();
1093
- const message = error instanceof Error ? error.message : String(error);
1094
- return { Success: false, ErrorMessage: `Teardown failed for '${manifest.name}' (rolled back): ${message}` };
1095
- }
1096
- context.Callbacks?.OnSuccess?.('Metadata', `Retired this app's rows from '${mjSchema}' (${files.length} teardown script(s)).`);
1097
- return { Success: true };
1162
+ await context.DatabaseProvider.CommitTransaction();
1098
1163
  }
1099
- finally {
1100
- CleanupTempDir(tempDir, 'Metadata', context);
1164
+ catch (error) {
1165
+ await context.DatabaseProvider.RollbackTransaction();
1166
+ const message = error instanceof Error ? error.message : String(error);
1167
+ return { Success: false, ErrorMessage: `Teardown failed for '${manifest.name}' (rolled back): ${message}` };
1101
1168
  }
1169
+ context.Callbacks?.OnSuccess?.('Metadata', `Retired this app's rows from '${mjSchema}' (${files.length} teardown script(s)).`);
1170
+ return { Success: true };
1102
1171
  }
1103
1172
  async function DownloadAppMigrations(manifest, context, tempDir, subpath) {
1104
1173
  const baseDir = manifest.migrations.directory;
@@ -1116,6 +1185,29 @@ async function DownloadAppMigrations(manifest, context, tempDir, subpath) {
1116
1185
  }
1117
1186
  return DownloadMigrations(manifest.repository, manifest.version, baseDir, tempDir, context.GitHubOptions, subpath, manifest.name);
1118
1187
  }
1188
+ async function PreflightManifestPackages(manifest, context, packageVersion, versionStrategy) {
1189
+ if (!manifest.packages) {
1190
+ return { Success: true };
1191
+ }
1192
+ context.Callbacks?.OnProgress?.('Packages', 'Checking app packages against the npm registry...');
1193
+ const result = await PreflightAppPackages({
1194
+ RepoRoot: context.RepoRoot,
1195
+ ServerPackages: manifest.packages.server ?? [],
1196
+ ClientPackages: manifest.packages.client ?? [],
1197
+ SharedPackages: manifest.packages.shared ?? [],
1198
+ Version: packageVersion,
1199
+ PackageManager: context.PackageManager,
1200
+ VersionStrategy: versionStrategy,
1201
+ RegistryUrl: manifest.packages.registry,
1202
+ });
1203
+ if (result.SkippedReason) {
1204
+ context.Callbacks?.OnProgress?.('Packages', `Skipped the registry check: ${result.SkippedReason}.`);
1205
+ }
1206
+ for (const warning of result.Warnings) {
1207
+ context.Callbacks?.OnWarn?.('Packages', warning);
1208
+ }
1209
+ return result.Success ? { Success: true } : { Success: false, ErrorMessage: result.ErrorMessage };
1210
+ }
1119
1211
  async function HandlePackageInstallation(manifest, context, packageVersion, versionStrategy, verbose) {
1120
1212
  if (!manifest.packages) {
1121
1213
  return { Success: true };