@crvouga/mockingbird-service-stripe 3.5.0 → 3.6.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/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Changelog — @crvouga/mockingbird-service-stripe
2
2
 
3
- ## 3.5.0 (2026-10-16)
3
+ ## 3.6.0 (2026-10-17)
4
+
5
+ ### Features
6
+
7
+ - resolve fleet and emulator issue batch ([26d2f8c](https://github.com/crvouga/mockingbird/commit/26d2f8cee99fefed93192204bac44fb1be35c1da))
8
+
9
+ ## 3.5.0 (2026-10-15)
4
10
 
5
11
  ### Features
6
12
 
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-EUF2MBTV.js";
5
5
  import {
6
6
  listen
7
- } from "./chunk-6GPNQX6K.js";
7
+ } from "./chunk-Q3J6JUZM.js";
8
8
 
9
9
  // src/server.ts
10
10
  import { readFileSync } from "node:fs";
@@ -87,4 +87,4 @@ export {
87
87
  createServer,
88
88
  serveTarget
89
89
  };
90
- //# sourceMappingURL=chunk-SBWLNRX7.js.map
90
+ //# sourceMappingURL=chunk-53CRCMWH.js.map
@@ -12,6 +12,38 @@ import {
12
12
 
13
13
  // ../../adapters/node/dist/fleet-archive.js
14
14
  import { createHash } from "node:crypto";
15
+ function validateCompatibilityTags(tags) {
16
+ if (!tags || typeof tags !== "object" || Array.isArray(tags))
17
+ throw new Error("invalid archive compatibility tags");
18
+ const entries = Object.entries(tags);
19
+ if (entries.length > 32 || entries.some(([key, value]) => !/^[A-Za-z0-9_.-]{1,64}$/.test(key) || typeof value !== "string" || value.length < 1 || value.length > 256 || [...value].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)))
20
+ throw new Error("invalid archive compatibility tags");
21
+ }
22
+ function assertCompatibilityTags(tags, policy) {
23
+ if (tags !== void 0)
24
+ validateCompatibilityTags(tags);
25
+ if (!policy)
26
+ return;
27
+ validateCompatibilityTags(policy.tags);
28
+ if (policy.missing !== void 0 && !["allow", "reject"].includes(policy.missing))
29
+ throw new Error("invalid missing compatibility policy");
30
+ if (tags === void 0 && policy.missing === "allow")
31
+ return;
32
+ if (canonical(tags) !== canonical(policy.tags))
33
+ throw new Error("application archive compatibility mismatch");
34
+ }
35
+ function assertArchiveCompatibility(input, policy) {
36
+ const archive = validateArchive(input);
37
+ assertCompatibilityTags(archive.compatibility, policy);
38
+ return archive;
39
+ }
40
+ async function migrateApplicationArchive(input, migration) {
41
+ const source = assertArchiveCompatibility(input, migration.from);
42
+ validateCompatibilityTags(migration.to);
43
+ const transformed = await migration.transform(structuredClone(source));
44
+ const { sha256: _sha, ...content } = transformed;
45
+ return validateArchive(sealArchive({ ...content, compatibility: structuredClone(migration.to) }));
46
+ }
15
47
  var canonical = (value) => {
16
48
  if (Array.isArray(value))
17
49
  return `[${value.map(canonical).join(",")}]`;
@@ -31,6 +63,8 @@ function validateArchive(value) {
31
63
  const { sha256, ...content } = doc;
32
64
  if (doc.format !== "mockingbird.fleet/v1" || typeof doc.sourceId !== "string" || typeof doc.createdAt !== "string" || !doc.namespaces || typeof doc.namespaces !== "object" || Array.isArray(doc.namespaces) || sha256 !== digest(content))
33
65
  throw new Error("unsupported or corrupt fleet archive");
66
+ if (doc.compatibility !== void 0)
67
+ validateCompatibilityTags(doc.compatibility);
34
68
  for (const [namespace, entry] of Object.entries(doc.namespaces)) {
35
69
  if (!/^[A-Za-z0-9_.-]{1,64}$/.test(namespace) || !entry || !Number.isFinite(entry.clock?.now) || typeof entry.clock?.frozen !== "boolean" || !entry.services || !Object.keys(entry.services).length)
36
70
  throw new Error("invalid archive namespace");
@@ -46,6 +80,7 @@ function inspectArchive(input) {
46
80
  format: archive.format,
47
81
  createdAt: archive.createdAt,
48
82
  sha256: archive.sha256,
83
+ ...archive.compatibility ? { compatibility: archive.compatibility } : {},
49
84
  namespaces: Object.fromEntries(Object.entries(archive.namespaces).map(([name, ns]) => [
50
85
  name,
51
86
  {
@@ -1280,11 +1315,28 @@ var ingressSupport = {
1280
1315
  streaming: true,
1281
1316
  signing: "vendor request signing is not used; webhook signatures remain payload based"
1282
1317
  },
1318
+ cognito: {
1319
+ publicOrigin: true,
1320
+ pathPrefix: true,
1321
+ namespaces: "path-or-header",
1322
+ streaming: true,
1323
+ signing: "JWT issuer and JWKS use the configured public namespace; SigV4 verification is not modelled"
1324
+ },
1325
+ medplum: {
1326
+ publicOrigin: true,
1327
+ pathPrefix: true,
1328
+ namespaces: "path-or-header",
1329
+ streaming: true,
1330
+ signing: "FHIR, OAuth and storage URLs use the configured public namespace"
1331
+ },
1283
1332
  other: {
1284
1333
  publicOrigin: false,
1285
1334
  reason: "host/path-sensitive signing and hosted URL rewriting are not certified; use a direct endpoint"
1286
1335
  }
1287
1336
  };
1337
+ function supportsPublicIngress(service) {
1338
+ return service === "stripe" || service === "cognito" || service === "medplum";
1339
+ }
1288
1340
  function advertisedHttpBase(entry) {
1289
1341
  if (entry.publicPath !== void 0 && !entry.publicOrigin)
1290
1342
  throw new Error("publicPath requires publicOrigin");
@@ -1303,7 +1355,7 @@ function createFleetIngress(config, manifest, transport = fetch) {
1303
1355
  const base = advertisedHttpBase(entry);
1304
1356
  if (!base)
1305
1357
  return [];
1306
- if ((entry.target ?? service) !== "stripe")
1358
+ if (!supportsPublicIngress(entry.target ?? service))
1307
1359
  throw new Error(`public ingress unsupported for ${entry.target ?? service}; host/path-sensitive signatures require a direct endpoint`);
1308
1360
  const privateEndpoint = manifest.services[service];
1309
1361
  if (!privateEndpoint || entry.protocol && entry.protocol !== "http")
@@ -1327,6 +1379,8 @@ function createFleetIngress(config, manifest, transport = fetch) {
1327
1379
  const route = routes.find((r) => url.origin === r.origin && (url.pathname === r.prefix || url.pathname.startsWith(`${r.prefix}/`)));
1328
1380
  if (!route)
1329
1381
  return new Response("public ingress route not found", { status: 404 });
1382
+ if ((route.entry.target ?? route.service) === "cognito" && request.headers.get("authorization")?.startsWith("AWS4-HMAC-SHA256"))
1383
+ return new Response("SigV4 requests require a direct endpoint", { status: 403 });
1330
1384
  const path = url.pathname.slice(route.prefix.length) || "/";
1331
1385
  const adminPrefix = route.entry.adminPrefix ?? config.adminPrefix ?? "/__admin";
1332
1386
  if ((path === adminPrefix || path.startsWith(`${adminPrefix}/`)) && !path.startsWith(`${adminPrefix}/ns/`))
@@ -2347,7 +2401,7 @@ data: ${JSON.stringify(data)}
2347
2401
  {
2348
2402
  protocol: c.protocol,
2349
2403
  coverage: c.coverage,
2350
- ingress: c.targetName === "stripe" ? ingressSupport.stripe : ingressSupport.other,
2404
+ ingress: supportsPublicIngress(c.targetName ?? "") ? ingressSupport[c.targetName] : ingressSupport.other,
2351
2405
  target: c.targetName,
2352
2406
  outboundPolicy: c.outboundPolicy ?? "unsupported",
2353
2407
  version: 1,
@@ -3431,11 +3485,12 @@ data: ${JSON.stringify(data)}
3431
3485
  format: "mockingbird.fleet/v1",
3432
3486
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3433
3487
  sourceId: this.ctx.id,
3488
+ ...this.ctx.options.archiveCompatibility ? { compatibility: structuredClone(this.ctx.options.archiveCompatibility.tags) } : {},
3434
3489
  namespaces: contents
3435
3490
  });
3436
3491
  }
3437
3492
  async restoreArchive(input, selectedNamespace) {
3438
- const archive = validateArchive(input);
3493
+ const archive = assertArchiveCompatibility(input, this.ctx.options.archiveCompatibility);
3439
3494
  const entries = Object.entries(archive.namespaces).filter(([ns]) => selectedNamespace === void 0 || ns === selectedNamespace);
3440
3495
  if (!entries.length)
3441
3496
  throw new Error("selected namespace absent from archive");
@@ -3719,7 +3774,7 @@ async function preflightFleet(config, options) {
3719
3774
  if (entry.target !== void 0 && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(entry.target))
3720
3775
  throw new Error("target must name a supported service suffix");
3721
3776
  const service = entry.target ?? name;
3722
- if (advertisedHttpBase(entry) && service !== "stripe")
3777
+ if (advertisedHttpBase(entry) && !supportsPublicIngress(service))
3723
3778
  throw new Error(`public ingress unsupported for ${service}; use a direct endpoint to preserve signed targets`);
3724
3779
  const target = await options.load(service);
3725
3780
  const protocol = "protocol" in target ? target.protocol : "http";
@@ -3770,7 +3825,7 @@ async function preflightFleet(config, options) {
3770
3825
  break;
3771
3826
  }
3772
3827
  }
3773
- targets.set(name, { ...target, packageVersion: packageVersion ?? "3.5.0" });
3828
+ targets.set(name, { ...target, packageVersion: packageVersion ?? "3.6.0" });
3774
3829
  } catch (error) {
3775
3830
  throw new Error(`service ${name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
3776
3831
  }
@@ -3814,6 +3869,11 @@ async function preflightFleet(config, options) {
3814
3869
  return ordered;
3815
3870
  }
3816
3871
  function validateFleetPolicies(options) {
3872
+ if (options.archiveCompatibility) {
3873
+ validateCompatibilityTags(options.archiveCompatibility.tags);
3874
+ if (options.archiveCompatibility.missing !== void 0 && !["allow", "reject"].includes(options.archiveCompatibility.missing))
3875
+ throw new Error("archiveCompatibility.missing must be allow or reject");
3876
+ }
3817
3877
  if (options.journal && !options.repository)
3818
3878
  throw new Error("durable operation journal requires an archive repository");
3819
3879
  if (options.cors) {
@@ -4645,6 +4705,40 @@ async function startFleet(config, options) {
4645
4705
  }
4646
4706
  }
4647
4707
 
4708
+ // ../../adapters/node/dist/fleet-loader.js
4709
+ import { createRequire as createRequire2 } from "node:module";
4710
+ import { resolve as resolve2 } from "node:path";
4711
+ import { pathToFileURL } from "node:url";
4712
+ var suffix = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4713
+ function createInstalledServiceLoader(options) {
4714
+ if (!options.projectRoot)
4715
+ throw new Error("consumer projectRoot required");
4716
+ const allowed = new Set(options.services);
4717
+ for (const name of allowed)
4718
+ if (!suffix.test(name))
4719
+ throw new Error("invalid service suffix");
4720
+ const require2 = createRequire2(resolve2(options.projectRoot, "package.json"));
4721
+ const targets = new Map((options.targets ?? []).map((target) => [target.name, target]));
4722
+ return async (name) => {
4723
+ if (!suffix.test(name) || !allowed.has(name))
4724
+ throw new Error("service is not declared in loader allowlist");
4725
+ const own = targets.get(name);
4726
+ if (own)
4727
+ return own;
4728
+ const specifier = `@crvouga/mockingbird-service-${name}/server`;
4729
+ let location;
4730
+ try {
4731
+ location = require2.resolve(specifier);
4732
+ } catch {
4733
+ throw new Error(`cannot load service "${name}": install @crvouga/mockingbird-service-${name} in the consumer project; its /server export is required`);
4734
+ }
4735
+ const mod = await import(pathToFileURL(location).href);
4736
+ if (!mod.serveTarget || mod.serveTarget.name !== name || ("protocol" in mod.serveTarget ? typeof mod.serveTarget.start !== "function" : typeof mod.serveTarget.create !== "function"))
4737
+ throw new Error(`${specifier} exports no matching serveTarget`);
4738
+ return mod.serveTarget;
4739
+ };
4740
+ }
4741
+
4648
4742
  // ../../adapters/node/dist/fleet-client.js
4649
4743
  import { randomUUID as randomUUID7 } from "node:crypto";
4650
4744
  import { chmod as chmod2, readFile as readFile5, rename as rename4, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
@@ -4657,6 +4751,14 @@ var FleetClientError = class extends Error {
4657
4751
  this.details = details;
4658
4752
  }
4659
4753
  };
4754
+ var FleetClientResponseError = class extends FleetClientError {
4755
+ code;
4756
+ constructor(code, status, origin) {
4757
+ super(status, { code, body: "omitted", ...origin ? { origin } : {} });
4758
+ this.code = code;
4759
+ this.name = "FleetClientResponseError";
4760
+ }
4761
+ };
4660
4762
  var FleetClient = class {
4661
4763
  base;
4662
4764
  options;
@@ -4669,7 +4771,14 @@ var FleetClient = class {
4669
4771
  if (url.username || url.password)
4670
4772
  throw new Error("fleet client URL must not contain credentials");
4671
4773
  url.searchParams.set("namespace", options.namespace ?? this.options.namespace ?? "default");
4774
+ const limit = options.maxResponseBytes ?? this.options.maxResponseBytes ?? 64 * 1024 * 1024;
4775
+ if (!Number.isSafeInteger(limit) || limit < 1)
4776
+ throw new Error("invalid response byte limit");
4777
+ const deadline = AbortSignal.timeout(this.options.timeoutMs ?? 3e4);
4778
+ const signal = options.signal ? AbortSignal.any([options.signal, deadline]) : deadline;
4779
+ signal.throwIfAborted();
4672
4780
  const response = await (this.options.fetch ?? fetch)(url, {
4781
+ redirect: "manual",
4673
4782
  method: options.method ?? (options.body === void 0 ? "GET" : "POST"),
4674
4783
  headers: {
4675
4784
  "content-type": "application/json",
@@ -4677,11 +4786,71 @@ var FleetClient = class {
4677
4786
  ...options.idempotencyKey ? { "idempotency-key": options.idempotencyKey } : {}
4678
4787
  },
4679
4788
  ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
4680
- signal: options.signal ?? AbortSignal.timeout(this.options.timeoutMs ?? 3e4)
4789
+ signal
4681
4790
  });
4682
- const data = await response.json();
4683
- if (!response.ok)
4684
- throw new FleetClientError(response.status, data);
4791
+ if (response.status >= 300 && response.status < 400) {
4792
+ await response.body?.cancel();
4793
+ throw new FleetClientResponseError("redirect", response.status, url.origin);
4794
+ }
4795
+ if (Number(response.headers.get("content-length")) > limit) {
4796
+ await response.body?.cancel();
4797
+ throw new FleetClientResponseError("response_too_large", response.status);
4798
+ }
4799
+ const reader = response.body?.getReader();
4800
+ const chunks = [];
4801
+ let size = 0;
4802
+ const abort = () => {
4803
+ void reader?.cancel(signal.reason).catch(() => {
4804
+ });
4805
+ };
4806
+ signal.addEventListener("abort", abort, { once: true });
4807
+ try {
4808
+ if (reader)
4809
+ while (true) {
4810
+ signal.throwIfAborted();
4811
+ const { done, value } = await reader.read();
4812
+ signal.throwIfAborted();
4813
+ if (done)
4814
+ break;
4815
+ size += value.byteLength;
4816
+ if (size > limit) {
4817
+ await reader.cancel();
4818
+ throw new FleetClientResponseError("response_too_large", response.status);
4819
+ }
4820
+ chunks.push(value);
4821
+ }
4822
+ } finally {
4823
+ signal.removeEventListener("abort", abort);
4824
+ if (signal.aborted)
4825
+ await reader?.cancel(signal.reason).catch(() => {
4826
+ });
4827
+ reader?.releaseLock();
4828
+ }
4829
+ const bytes2 = new Uint8Array(size);
4830
+ let offset = 0;
4831
+ for (const chunk of chunks) {
4832
+ bytes2.set(chunk, offset);
4833
+ offset += chunk.byteLength;
4834
+ }
4835
+ const text = new TextDecoder().decode(bytes2);
4836
+ let data;
4837
+ try {
4838
+ data = text.length ? JSON.parse(text) : void 0;
4839
+ } catch {
4840
+ if (response.ok)
4841
+ throw new FleetClientResponseError("invalid_json", response.status);
4842
+ }
4843
+ if (!response.ok) {
4844
+ const error = data && typeof data === "object" ? data.error : void 0;
4845
+ const detail = { body: "omitted" };
4846
+ if (error && typeof error === "object")
4847
+ for (const key of ["type", "code"]) {
4848
+ const value = error[key];
4849
+ if (typeof value === "string" && /^[A-Za-z0-9_.-]{1,128}$/.test(value))
4850
+ detail[key] = value;
4851
+ }
4852
+ throw new FleetClientError(response.status, detail);
4853
+ }
4685
4854
  return data;
4686
4855
  }
4687
4856
  discover() {
@@ -4747,17 +4916,52 @@ function renderConnections(manifest, privateManifest, namespace, mapping) {
4747
4916
  const child = manifest.services[entry.service], secret = privateManifest.services[entry.service];
4748
4917
  if (!child || !secret)
4749
4918
  throw new Error(`missing service mapping ${entry.service}`);
4919
+ let endpoint;
4750
4920
  if (child.protocol === "http") {
4751
4921
  const template = child.namespaces.path;
4752
4922
  if (typeof template !== "string")
4753
4923
  throw new Error(`missing HTTP namespace mapping ${entry.service}`);
4754
- result[variable] = new URL(template.replace("{name}", encodeURIComponent(namespace)), secret.url).href;
4924
+ endpoint = new URL(template.replace("{name}", encodeURIComponent(namespace)), secret.url).href;
4755
4925
  } else {
4756
- const endpoint = secret.namespaces?.[namespace];
4757
- if (!endpoint)
4926
+ const selected = secret.namespaces?.[namespace];
4927
+ if (!selected)
4758
4928
  throw new Error(`missing namespace mapping ${namespace}/${entry.service}`);
4759
- result[variable] = endpoint;
4929
+ endpoint = selected;
4930
+ }
4931
+ const url = new URL(endpoint);
4932
+ const field = entry.field ?? "url";
4933
+ let value;
4934
+ switch (field) {
4935
+ case "url":
4936
+ value = endpoint;
4937
+ break;
4938
+ case "hostname":
4939
+ value = url.hostname.replace(/^\[|\]$/g, "");
4940
+ break;
4941
+ case "port":
4942
+ value = url.port;
4943
+ break;
4944
+ case "username":
4945
+ value = decodeURIComponent(url.username);
4946
+ break;
4947
+ case "password":
4948
+ value = decodeURIComponent(url.password);
4949
+ break;
4950
+ case "pathname":
4951
+ value = decodeURIComponent(url.pathname);
4952
+ break;
4953
+ case "database":
4954
+ value = decodeURIComponent(url.pathname.replace(/^\//, ""));
4955
+ break;
4956
+ default:
4957
+ throw new Error("unsupported connection field");
4760
4958
  }
4959
+ if (!value) {
4960
+ if (typeof entry.fallback !== "string")
4961
+ throw new Error(`missing connection component ${field}`);
4962
+ value = entry.fallback;
4963
+ }
4964
+ result[variable] = value;
4761
4965
  }
4762
4966
  return result;
4763
4967
  }
@@ -4777,14 +4981,14 @@ async function writeConnections(path, values, format = "json") {
4777
4981
 
4778
4982
  // ../../adapters/node/dist/fleet-tools.js
4779
4983
  import { readFile as readFile6 } from "node:fs/promises";
4780
- import { createRequire as createRequire2 } from "node:module";
4781
- import { resolve as resolve2 } from "node:path";
4984
+ import { createRequire as createRequire3 } from "node:module";
4985
+ import { resolve as resolve3 } from "node:path";
4782
4986
  async function doctorFleet(config, options, probes = []) {
4783
4987
  const findings = [];
4784
4988
  const plan = [];
4785
4989
  try {
4786
4990
  const targets = await preflightFleet(config, options);
4787
- const require2 = createRequire2(resolve2(process.cwd(), "package.json"));
4991
+ const require2 = createRequire3(resolve3(process.cwd(), "package.json"));
4788
4992
  for (const [service, target] of targets) {
4789
4993
  let version = "development";
4790
4994
  try {
@@ -4839,7 +5043,7 @@ async function doctorFleet(config, options, probes = []) {
4839
5043
  };
4840
5044
  }
4841
5045
  async function rehearseFleet(config, options, input, probes = [], timeoutMs = 3e4) {
4842
- const archive = validateArchive(input);
5046
+ const archive = assertArchiveCompatibility(input, options.archiveCompatibility);
4843
5047
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
4844
5048
  const isolated = {
4845
5049
  ...config,
@@ -4851,7 +5055,7 @@ async function rehearseFleet(config, options, input, probes = [], timeoutMs = 3e
4851
5055
  };
4852
5056
  let fleet;
4853
5057
  try {
4854
- fleet = await startFleet(isolated, { load: options.load });
5058
+ fleet = await startFleet(isolated, options);
4855
5059
  const client = new FleetClient(fleet.manifest.adminBase, {
4856
5060
  ...config.adminKey ? { adminKey: config.adminKey } : {}
4857
5061
  });
@@ -4990,9 +5194,6 @@ function fleetCommands(load) {
4990
5194
 
4991
5195
  // ../../adapters/node/dist/cli.js
4992
5196
  import { readFile as readFile7 } from "node:fs/promises";
4993
- import { createRequire as createRequire3 } from "node:module";
4994
- import { resolve as resolve3 } from "node:path";
4995
- import { pathToFileURL } from "node:url";
4996
5197
  import { parseArgs } from "node:util";
4997
5198
  var optionHelp = (options) => Object.entries(options).map(([name, option]) => {
4998
5199
  const flag = `--${name}${option.type === "string" ? ` ${option.value ?? "<value>"}` : ""}`;
@@ -5231,26 +5432,7 @@ var resolveServeControls = async (values, env = process.env, base) => {
5231
5432
  normalizeBootControls(document, "adminControls");
5232
5433
  return document;
5233
5434
  };
5234
- var loadTarget = async (name, own) => {
5235
- if (name === own.name)
5236
- return own;
5237
- const specifier = `@crvouga/mockingbird-service-${name}/server`;
5238
- try {
5239
- let mod;
5240
- try {
5241
- mod = await import(specifier);
5242
- } catch {
5243
- const fromProject = createRequire3(resolve3(process.cwd(), "package.json"));
5244
- mod = await import(pathToFileURL(fromProject.resolve(specifier)).href);
5245
- }
5246
- if (!mod.serveTarget)
5247
- throw new Error(`${specifier} exports no serveTarget`);
5248
- return mod.serveTarget;
5249
- } catch (error) {
5250
- const reason = error instanceof Error ? error.message : String(error);
5251
- throw new Error(`cannot load service "${name}": ${reason}. Install @crvouga/mockingbird-service-${name}.`);
5252
- }
5253
- };
5435
+ var loadTarget = (name, own) => createInstalledServiceLoader({ projectRoot: process.cwd(), services: [name], targets: [own] })(name);
5254
5436
  var start = async (target, values, config) => {
5255
5437
  const adminPrefix = resolveAdminPrefix(config.adminPrefix);
5256
5438
  const runtime = await target.create(values, {
@@ -5900,9 +6082,14 @@ var sourceArchive = (input) => {
5900
6082
  throw new Error("corrupt migration source");
5901
6083
  return structuredClone(source);
5902
6084
  };
5903
- function archiveCompatibility(input, registry) {
6085
+ function archiveCompatibility(input, registry, policy) {
5904
6086
  const source = sourceArchive(input);
5905
6087
  const problems = [], steps = [];
6088
+ try {
6089
+ assertCompatibilityTags(source.compatibility, policy);
6090
+ } catch (error) {
6091
+ problems.push(error instanceof Error ? error.message : "invalid application compatibility");
6092
+ }
5906
6093
  if (source.format !== "mockingbird.fleet/v1" && !registry.sourceFormats.includes(source.format))
5907
6094
  problems.push(`unsupported source format ${source.format}`);
5908
6095
  for (const [namespace, ns] of Object.entries(source.namespaces))
@@ -5954,6 +6141,7 @@ async function migrateArchive(input, registry, parameters = {}) {
5954
6141
  const content = {
5955
6142
  format: "mockingbird.fleet/v1",
5956
6143
  sourceId: source.sourceId,
6144
+ ...source.compatibility ? { compatibility: source.compatibility } : {},
5957
6145
  createdAt: source.createdAt,
5958
6146
  namespaces,
5959
6147
  migration: {
@@ -6320,6 +6508,8 @@ var NamespacePool = class {
6320
6508
  };
6321
6509
 
6322
6510
  export {
6511
+ assertArchiveCompatibility,
6512
+ migrateApplicationArchive,
6323
6513
  validateArchive,
6324
6514
  inspectArchive,
6325
6515
  diffArchives,
@@ -6345,7 +6535,9 @@ export {
6345
6535
  serve,
6346
6536
  listen,
6347
6537
  startFleet,
6538
+ createInstalledServiceLoader,
6348
6539
  FleetClientError,
6540
+ FleetClientResponseError,
6349
6541
  FleetClient,
6350
6542
  renderConnections,
6351
6543
  writeConnections,
@@ -6373,4 +6565,4 @@ export {
6373
6565
  withFleetRun,
6374
6566
  NamespacePool
6375
6567
  };
6376
- //# sourceMappingURL=chunk-6GPNQX6K.js.map
6568
+ //# sourceMappingURL=chunk-Q3J6JUZM.js.map