@crvouga/mockingbird-service-llamacloud 3.4.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,5 +1,17 @@
1
1
  # Changelog — @crvouga/mockingbird-service-llamacloud
2
2
 
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)
10
+
11
+ ### Features
12
+
13
+ - resolve SQL, routing and writer gaps ([73f97d0](https://github.com/crvouga/mockingbird/commit/73f97d0d2379f44b9131180498f4fab76648fed9))
14
+
3
15
  ## 3.4.0 (2026-10-15)
4
16
 
5
17
  ### Features
@@ -3941,7 +3941,7 @@ var stateAdminRoutes = (routes) => {
3941
3941
  };
3942
3942
 
3943
3943
  // ../core/dist/version.js
3944
- var PACKAGE_VERSION = true ? "3.4.0" : UNRELEASED_VERSION;
3944
+ var PACKAGE_VERSION = true ? "3.6.0" : UNRELEASED_VERSION;
3945
3945
 
3946
3946
  // ../core/dist/webhooks.js
3947
3947
  var json4 = (status, body) => new Response(JSON.stringify(body), {
@@ -12,9 +12,6 @@ import {
12
12
 
13
13
  // ../../adapters/node/dist/cli.js
14
14
  import { readFile as readFile5 } from "node:fs/promises";
15
- import { createRequire as createRequire3 } from "node:module";
16
- import { resolve as resolve3 } from "node:path";
17
- import { pathToFileURL } from "node:url";
18
15
  import { parseArgs } from "node:util";
19
16
 
20
17
  // ../../adapters/node/dist/fleet.js
@@ -28,6 +25,31 @@ import { createHash as createHash3, randomBytes, randomUUID as randomUUID3 } fro
28
25
 
29
26
  // ../../adapters/node/dist/fleet-archive.js
30
27
  import { createHash } from "node:crypto";
28
+ function validateCompatibilityTags(tags) {
29
+ if (!tags || typeof tags !== "object" || Array.isArray(tags))
30
+ throw new Error("invalid archive compatibility tags");
31
+ const entries = Object.entries(tags);
32
+ 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)))
33
+ throw new Error("invalid archive compatibility tags");
34
+ }
35
+ function assertCompatibilityTags(tags, policy) {
36
+ if (tags !== void 0)
37
+ validateCompatibilityTags(tags);
38
+ if (!policy)
39
+ return;
40
+ validateCompatibilityTags(policy.tags);
41
+ if (policy.missing !== void 0 && !["allow", "reject"].includes(policy.missing))
42
+ throw new Error("invalid missing compatibility policy");
43
+ if (tags === void 0 && policy.missing === "allow")
44
+ return;
45
+ if (canonical(tags) !== canonical(policy.tags))
46
+ throw new Error("application archive compatibility mismatch");
47
+ }
48
+ function assertArchiveCompatibility(input, policy) {
49
+ const archive = validateArchive(input);
50
+ assertCompatibilityTags(archive.compatibility, policy);
51
+ return archive;
52
+ }
31
53
  var canonical = (value) => {
32
54
  if (Array.isArray(value))
33
55
  return `[${value.map(canonical).join(",")}]`;
@@ -47,6 +69,8 @@ function validateArchive(value) {
47
69
  const { sha256, ...content } = doc;
48
70
  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))
49
71
  throw new Error("unsupported or corrupt fleet archive");
72
+ if (doc.compatibility !== void 0)
73
+ validateCompatibilityTags(doc.compatibility);
50
74
  for (const [namespace, entry] of Object.entries(doc.namespaces)) {
51
75
  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)
52
76
  throw new Error("invalid archive namespace");
@@ -62,6 +86,7 @@ function inspectArchive(input) {
62
86
  format: archive.format,
63
87
  createdAt: archive.createdAt,
64
88
  sha256: archive.sha256,
89
+ ...archive.compatibility ? { compatibility: archive.compatibility } : {},
65
90
  namespaces: Object.fromEntries(Object.entries(archive.namespaces).map(([name, ns]) => [
66
91
  name,
67
92
  {
@@ -704,11 +729,28 @@ var ingressSupport = {
704
729
  streaming: true,
705
730
  signing: "vendor request signing is not used; webhook signatures remain payload based"
706
731
  },
732
+ cognito: {
733
+ publicOrigin: true,
734
+ pathPrefix: true,
735
+ namespaces: "path-or-header",
736
+ streaming: true,
737
+ signing: "JWT issuer and JWKS use the configured public namespace; SigV4 verification is not modelled"
738
+ },
739
+ medplum: {
740
+ publicOrigin: true,
741
+ pathPrefix: true,
742
+ namespaces: "path-or-header",
743
+ streaming: true,
744
+ signing: "FHIR, OAuth and storage URLs use the configured public namespace"
745
+ },
707
746
  other: {
708
747
  publicOrigin: false,
709
748
  reason: "host/path-sensitive signing and hosted URL rewriting are not certified; use a direct endpoint"
710
749
  }
711
750
  };
751
+ function supportsPublicIngress(service) {
752
+ return service === "stripe" || service === "cognito" || service === "medplum";
753
+ }
712
754
  function advertisedHttpBase(entry) {
713
755
  if (entry.publicPath !== void 0 && !entry.publicOrigin)
714
756
  throw new Error("publicPath requires publicOrigin");
@@ -1247,7 +1289,7 @@ data: ${JSON.stringify(data)}
1247
1289
  {
1248
1290
  protocol: c.protocol,
1249
1291
  coverage: c.coverage,
1250
- ingress: c.targetName === "stripe" ? ingressSupport.stripe : ingressSupport.other,
1292
+ ingress: supportsPublicIngress(c.targetName ?? "") ? ingressSupport[c.targetName] : ingressSupport.other,
1251
1293
  target: c.targetName,
1252
1294
  outboundPolicy: c.outboundPolicy ?? "unsupported",
1253
1295
  version: 1,
@@ -2331,11 +2373,12 @@ data: ${JSON.stringify(data)}
2331
2373
  format: "mockingbird.fleet/v1",
2332
2374
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2333
2375
  sourceId: this.ctx.id,
2376
+ ...this.ctx.options.archiveCompatibility ? { compatibility: structuredClone(this.ctx.options.archiveCompatibility.tags) } : {},
2334
2377
  namespaces: contents
2335
2378
  });
2336
2379
  }
2337
2380
  async restoreArchive(input, selectedNamespace) {
2338
- const archive = validateArchive(input);
2381
+ const archive = assertArchiveCompatibility(input, this.ctx.options.archiveCompatibility);
2339
2382
  const entries = Object.entries(archive.namespaces).filter(([ns]) => selectedNamespace === void 0 || ns === selectedNamespace);
2340
2383
  if (!entries.length)
2341
2384
  throw new Error("selected namespace absent from archive");
@@ -2919,7 +2962,7 @@ async function preflightFleet(config, options) {
2919
2962
  if (entry.target !== void 0 && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(entry.target))
2920
2963
  throw new Error("target must name a supported service suffix");
2921
2964
  const service = entry.target ?? name;
2922
- if (advertisedHttpBase(entry) && service !== "stripe")
2965
+ if (advertisedHttpBase(entry) && !supportsPublicIngress(service))
2923
2966
  throw new Error(`public ingress unsupported for ${service}; use a direct endpoint to preserve signed targets`);
2924
2967
  const target = await options.load(service);
2925
2968
  const protocol = "protocol" in target ? target.protocol : "http";
@@ -2970,7 +3013,7 @@ async function preflightFleet(config, options) {
2970
3013
  break;
2971
3014
  }
2972
3015
  }
2973
- targets.set(name, { ...target, packageVersion: packageVersion ?? "3.4.0" });
3016
+ targets.set(name, { ...target, packageVersion: packageVersion ?? "3.6.0" });
2974
3017
  } catch (error) {
2975
3018
  throw new Error(`service ${name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
2976
3019
  }
@@ -3014,6 +3057,11 @@ async function preflightFleet(config, options) {
3014
3057
  return ordered;
3015
3058
  }
3016
3059
  function validateFleetPolicies(options) {
3060
+ if (options.archiveCompatibility) {
3061
+ validateCompatibilityTags(options.archiveCompatibility.tags);
3062
+ if (options.archiveCompatibility.missing !== void 0 && !["allow", "reject"].includes(options.archiveCompatibility.missing))
3063
+ throw new Error("archiveCompatibility.missing must be allow or reject");
3064
+ }
3017
3065
  if (options.journal && !options.repository)
3018
3066
  throw new Error("durable operation journal requires an archive repository");
3019
3067
  if (options.cors) {
@@ -4006,10 +4054,44 @@ async function startFleet(config, options) {
4006
4054
  }
4007
4055
  }
4008
4056
 
4009
- // ../../adapters/node/dist/fleet-tools.js
4010
- import { readFile as readFile4 } from "node:fs/promises";
4057
+ // ../../adapters/node/dist/fleet-loader.js
4011
4058
  import { createRequire as createRequire2 } from "node:module";
4012
4059
  import { resolve as resolve2 } from "node:path";
4060
+ import { pathToFileURL } from "node:url";
4061
+ var suffix = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4062
+ function createInstalledServiceLoader(options) {
4063
+ if (!options.projectRoot)
4064
+ throw new Error("consumer projectRoot required");
4065
+ const allowed = new Set(options.services);
4066
+ for (const name of allowed)
4067
+ if (!suffix.test(name))
4068
+ throw new Error("invalid service suffix");
4069
+ const require2 = createRequire2(resolve2(options.projectRoot, "package.json"));
4070
+ const targets = new Map((options.targets ?? []).map((target) => [target.name, target]));
4071
+ return async (name) => {
4072
+ if (!suffix.test(name) || !allowed.has(name))
4073
+ throw new Error("service is not declared in loader allowlist");
4074
+ const own = targets.get(name);
4075
+ if (own)
4076
+ return own;
4077
+ const specifier = `@crvouga/mockingbird-service-${name}/server`;
4078
+ let location;
4079
+ try {
4080
+ location = require2.resolve(specifier);
4081
+ } catch {
4082
+ throw new Error(`cannot load service "${name}": install @crvouga/mockingbird-service-${name} in the consumer project; its /server export is required`);
4083
+ }
4084
+ const mod = await import(pathToFileURL(location).href);
4085
+ if (!mod.serveTarget || mod.serveTarget.name !== name || ("protocol" in mod.serveTarget ? typeof mod.serveTarget.start !== "function" : typeof mod.serveTarget.create !== "function"))
4086
+ throw new Error(`${specifier} exports no matching serveTarget`);
4087
+ return mod.serveTarget;
4088
+ };
4089
+ }
4090
+
4091
+ // ../../adapters/node/dist/fleet-tools.js
4092
+ import { readFile as readFile4 } from "node:fs/promises";
4093
+ import { createRequire as createRequire3 } from "node:module";
4094
+ import { resolve as resolve3 } from "node:path";
4013
4095
 
4014
4096
  // ../../adapters/node/dist/fleet-client.js
4015
4097
  import { randomUUID as randomUUID5 } from "node:crypto";
@@ -4023,6 +4105,14 @@ var FleetClientError = class extends Error {
4023
4105
  this.details = details;
4024
4106
  }
4025
4107
  };
4108
+ var FleetClientResponseError = class extends FleetClientError {
4109
+ code;
4110
+ constructor(code, status, origin) {
4111
+ super(status, { code, body: "omitted", ...origin ? { origin } : {} });
4112
+ this.code = code;
4113
+ this.name = "FleetClientResponseError";
4114
+ }
4115
+ };
4026
4116
  var FleetClient = class {
4027
4117
  base;
4028
4118
  options;
@@ -4035,7 +4125,14 @@ var FleetClient = class {
4035
4125
  if (url.username || url.password)
4036
4126
  throw new Error("fleet client URL must not contain credentials");
4037
4127
  url.searchParams.set("namespace", options.namespace ?? this.options.namespace ?? "default");
4128
+ const limit = options.maxResponseBytes ?? this.options.maxResponseBytes ?? 64 * 1024 * 1024;
4129
+ if (!Number.isSafeInteger(limit) || limit < 1)
4130
+ throw new Error("invalid response byte limit");
4131
+ const deadline = AbortSignal.timeout(this.options.timeoutMs ?? 3e4);
4132
+ const signal = options.signal ? AbortSignal.any([options.signal, deadline]) : deadline;
4133
+ signal.throwIfAborted();
4038
4134
  const response = await (this.options.fetch ?? fetch)(url, {
4135
+ redirect: "manual",
4039
4136
  method: options.method ?? (options.body === void 0 ? "GET" : "POST"),
4040
4137
  headers: {
4041
4138
  "content-type": "application/json",
@@ -4043,11 +4140,71 @@ var FleetClient = class {
4043
4140
  ...options.idempotencyKey ? { "idempotency-key": options.idempotencyKey } : {}
4044
4141
  },
4045
4142
  ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
4046
- signal: options.signal ?? AbortSignal.timeout(this.options.timeoutMs ?? 3e4)
4143
+ signal
4047
4144
  });
4048
- const data = await response.json();
4049
- if (!response.ok)
4050
- throw new FleetClientError(response.status, data);
4145
+ if (response.status >= 300 && response.status < 400) {
4146
+ await response.body?.cancel();
4147
+ throw new FleetClientResponseError("redirect", response.status, url.origin);
4148
+ }
4149
+ if (Number(response.headers.get("content-length")) > limit) {
4150
+ await response.body?.cancel();
4151
+ throw new FleetClientResponseError("response_too_large", response.status);
4152
+ }
4153
+ const reader = response.body?.getReader();
4154
+ const chunks = [];
4155
+ let size = 0;
4156
+ const abort = () => {
4157
+ void reader?.cancel(signal.reason).catch(() => {
4158
+ });
4159
+ };
4160
+ signal.addEventListener("abort", abort, { once: true });
4161
+ try {
4162
+ if (reader)
4163
+ while (true) {
4164
+ signal.throwIfAborted();
4165
+ const { done, value } = await reader.read();
4166
+ signal.throwIfAborted();
4167
+ if (done)
4168
+ break;
4169
+ size += value.byteLength;
4170
+ if (size > limit) {
4171
+ await reader.cancel();
4172
+ throw new FleetClientResponseError("response_too_large", response.status);
4173
+ }
4174
+ chunks.push(value);
4175
+ }
4176
+ } finally {
4177
+ signal.removeEventListener("abort", abort);
4178
+ if (signal.aborted)
4179
+ await reader?.cancel(signal.reason).catch(() => {
4180
+ });
4181
+ reader?.releaseLock();
4182
+ }
4183
+ const bytes = new Uint8Array(size);
4184
+ let offset = 0;
4185
+ for (const chunk of chunks) {
4186
+ bytes.set(chunk, offset);
4187
+ offset += chunk.byteLength;
4188
+ }
4189
+ const text2 = new TextDecoder().decode(bytes);
4190
+ let data;
4191
+ try {
4192
+ data = text2.length ? JSON.parse(text2) : void 0;
4193
+ } catch {
4194
+ if (response.ok)
4195
+ throw new FleetClientResponseError("invalid_json", response.status);
4196
+ }
4197
+ if (!response.ok) {
4198
+ const error = data && typeof data === "object" ? data.error : void 0;
4199
+ const detail = { body: "omitted" };
4200
+ if (error && typeof error === "object")
4201
+ for (const key of ["type", "code"]) {
4202
+ const value = error[key];
4203
+ if (typeof value === "string" && /^[A-Za-z0-9_.-]{1,128}$/.test(value))
4204
+ detail[key] = value;
4205
+ }
4206
+ throw new FleetClientError(response.status, detail);
4207
+ }
4051
4208
  return data;
4052
4209
  }
4053
4210
  discover() {
@@ -4113,17 +4270,52 @@ function renderConnections(manifest, privateManifest, namespace, mapping) {
4113
4270
  const child = manifest.services[entry.service], secret = privateManifest.services[entry.service];
4114
4271
  if (!child || !secret)
4115
4272
  throw new Error(`missing service mapping ${entry.service}`);
4273
+ let endpoint;
4116
4274
  if (child.protocol === "http") {
4117
4275
  const template = child.namespaces.path;
4118
4276
  if (typeof template !== "string")
4119
4277
  throw new Error(`missing HTTP namespace mapping ${entry.service}`);
4120
- result[variable] = new URL(template.replace("{name}", encodeURIComponent(namespace)), secret.url).href;
4278
+ endpoint = new URL(template.replace("{name}", encodeURIComponent(namespace)), secret.url).href;
4121
4279
  } else {
4122
- const endpoint = secret.namespaces?.[namespace];
4123
- if (!endpoint)
4280
+ const selected = secret.namespaces?.[namespace];
4281
+ if (!selected)
4124
4282
  throw new Error(`missing namespace mapping ${namespace}/${entry.service}`);
4125
- result[variable] = endpoint;
4283
+ endpoint = selected;
4126
4284
  }
4285
+ const url = new URL(endpoint);
4286
+ const field = entry.field ?? "url";
4287
+ let value;
4288
+ switch (field) {
4289
+ case "url":
4290
+ value = endpoint;
4291
+ break;
4292
+ case "hostname":
4293
+ value = url.hostname.replace(/^\[|\]$/g, "");
4294
+ break;
4295
+ case "port":
4296
+ value = url.port;
4297
+ break;
4298
+ case "username":
4299
+ value = decodeURIComponent(url.username);
4300
+ break;
4301
+ case "password":
4302
+ value = decodeURIComponent(url.password);
4303
+ break;
4304
+ case "pathname":
4305
+ value = decodeURIComponent(url.pathname);
4306
+ break;
4307
+ case "database":
4308
+ value = decodeURIComponent(url.pathname.replace(/^\//, ""));
4309
+ break;
4310
+ default:
4311
+ throw new Error("unsupported connection field");
4312
+ }
4313
+ if (!value) {
4314
+ if (typeof entry.fallback !== "string")
4315
+ throw new Error(`missing connection component ${field}`);
4316
+ value = entry.fallback;
4317
+ }
4318
+ result[variable] = value;
4127
4319
  }
4128
4320
  return result;
4129
4321
  }
@@ -4147,7 +4339,7 @@ async function doctorFleet(config, options, probes = []) {
4147
4339
  const plan = [];
4148
4340
  try {
4149
4341
  const targets = await preflightFleet(config, options);
4150
- const require2 = createRequire2(resolve2(process.cwd(), "package.json"));
4342
+ const require2 = createRequire3(resolve3(process.cwd(), "package.json"));
4151
4343
  for (const [service, target] of targets) {
4152
4344
  let version = "development";
4153
4345
  try {
@@ -4202,7 +4394,7 @@ async function doctorFleet(config, options, probes = []) {
4202
4394
  };
4203
4395
  }
4204
4396
  async function rehearseFleet(config, options, input, probes = [], timeoutMs = 3e4) {
4205
- const archive = validateArchive(input);
4397
+ const archive = assertArchiveCompatibility(input, options.archiveCompatibility);
4206
4398
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
4207
4399
  const isolated = {
4208
4400
  ...config,
@@ -4214,7 +4406,7 @@ async function rehearseFleet(config, options, input, probes = [], timeoutMs = 3e
4214
4406
  };
4215
4407
  let fleet;
4216
4408
  try {
4217
- fleet = await startFleet(isolated, { load: options.load });
4409
+ fleet = await startFleet(isolated, options);
4218
4410
  const client = new FleetClient(fleet.manifest.adminBase, {
4219
4411
  ...config.adminKey ? { adminKey: config.adminKey } : {}
4220
4412
  });
@@ -4589,26 +4781,7 @@ var resolveServeControls = async (values, env = process.env, base) => {
4589
4781
  normalizeBootControls(document, "adminControls");
4590
4782
  return document;
4591
4783
  };
4592
- var loadTarget = async (name, own) => {
4593
- if (name === own.name)
4594
- return own;
4595
- const specifier = `@crvouga/mockingbird-service-${name}/server`;
4596
- try {
4597
- let mod;
4598
- try {
4599
- mod = await import(specifier);
4600
- } catch {
4601
- const fromProject = createRequire3(resolve3(process.cwd(), "package.json"));
4602
- mod = await import(pathToFileURL(fromProject.resolve(specifier)).href);
4603
- }
4604
- if (!mod.serveTarget)
4605
- throw new Error(`${specifier} exports no serveTarget`);
4606
- return mod.serveTarget;
4607
- } catch (error) {
4608
- const reason = error instanceof Error ? error.message : String(error);
4609
- throw new Error(`cannot load service "${name}": ${reason}. Install @crvouga/mockingbird-service-${name}.`);
4610
- }
4611
- };
4784
+ var loadTarget = (name, own) => createInstalledServiceLoader({ projectRoot: process.cwd(), services: [name], targets: [own] })(name);
4612
4785
  var start = async (target, values, config) => {
4613
4786
  const adminPrefix = resolveAdminPrefix(config.adminPrefix);
4614
4787
  const runtime = await target.create(values, {
@@ -4771,4 +4944,4 @@ export {
4771
4944
  createServer2 as createServer,
4772
4945
  serveTarget
4773
4946
  };
4774
- //# sourceMappingURL=chunk-UYQQK25C.js.map
4947
+ //# sourceMappingURL=chunk-TOJRRSGZ.js.map