@crvouga/mockingbird-service-slack 3.1.0 → 3.3.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.
@@ -0,0 +1,97 @@
1
+ // ../../http/provider/dist/vendor/core/crypto.js
2
+ import { hmac } from "@noble/hashes/hmac.js";
3
+ import { sha1 } from "@noble/hashes/legacy.js";
4
+ import { md5 } from "@noble/hashes/legacy.js";
5
+ import { sha256 } from "@noble/hashes/sha2.js";
6
+ import { Buffer } from "buffer";
7
+ var digest = (algorithm) => {
8
+ if (algorithm === "sha256")
9
+ return sha256;
10
+ if (algorithm === "sha1")
11
+ return sha1;
12
+ if (algorithm === "md5")
13
+ return md5;
14
+ throw new Error(`Unsupported digest ${algorithm}`);
15
+ };
16
+ var bytes = (input) => typeof input === "string" ? new TextEncoder().encode(input) : input;
17
+ function createHash(algorithm) {
18
+ const parts = [];
19
+ const hash = {
20
+ update(input) {
21
+ parts.push(bytes(input));
22
+ return hash;
23
+ },
24
+ digest(encoding) {
25
+ const result = Buffer.from(digest(algorithm)(Buffer.concat(parts)));
26
+ return encoding === "base64url" ? result.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") : encoding ? result.toString(encoding) : result;
27
+ }
28
+ };
29
+ return hash;
30
+ }
31
+ function createHmac(algorithm, key) {
32
+ const parts = [];
33
+ const hash = {
34
+ update(input) {
35
+ parts.push(bytes(input));
36
+ return hash;
37
+ },
38
+ digest(encoding) {
39
+ const result = Buffer.from(hmac(digest(algorithm), bytes(key), Buffer.concat(parts)));
40
+ return encoding === "base64url" ? result.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") : encoding ? result.toString(encoding) : result;
41
+ }
42
+ };
43
+ return hash;
44
+ }
45
+ var source;
46
+ function useRandom(next) {
47
+ source = next;
48
+ }
49
+ function withRandom(next, run) {
50
+ const previous = source;
51
+ source = next;
52
+ try {
53
+ return run();
54
+ } finally {
55
+ source = previous;
56
+ }
57
+ }
58
+ function random() {
59
+ return source ? source() : Math.random();
60
+ }
61
+ function randomBytes(length) {
62
+ if (!source)
63
+ return Buffer.from(crypto.getRandomValues(new Uint8Array(length)));
64
+ const out = new Uint8Array(length);
65
+ for (let i = 0; i < length; i++)
66
+ out[i] = Math.floor(source() * 256);
67
+ return Buffer.from(out);
68
+ }
69
+ function randomUUID() {
70
+ if (!source)
71
+ return crypto.randomUUID();
72
+ const b = randomBytes(16);
73
+ b[6] = b[6] & 15 | 64;
74
+ b[8] = b[8] & 63 | 128;
75
+ const hex = b.toString("hex");
76
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
77
+ }
78
+ function timingSafeEqual(a, b) {
79
+ if (a.length !== b.length)
80
+ throw new Error("Buffers must have equal lengths");
81
+ let difference = 0;
82
+ for (let i = 0; i < a.length; i++)
83
+ difference |= a[i] ^ b[i];
84
+ return difference === 0;
85
+ }
86
+
87
+ export {
88
+ createHash,
89
+ createHmac,
90
+ useRandom,
91
+ withRandom,
92
+ random,
93
+ randomBytes,
94
+ randomUUID,
95
+ timingSafeEqual
96
+ };
97
+ //# sourceMappingURL=chunk-6Z2MR3PO.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../http/provider/src/vendor/core/crypto.ts"],
4
+ "sourcesContent": ["import { hmac } from \"@noble/hashes/hmac.js\"\nimport { sha1 } from \"@noble/hashes/legacy.js\"\nimport { md5 } from \"@noble/hashes/legacy.js\"\nimport { sha256 } from \"@noble/hashes/sha2.js\"\nimport { Buffer } from \"buffer\"\n\nconst digest = (algorithm: string) => {\n if (algorithm === \"sha256\") return sha256\n if (algorithm === \"sha1\") return sha1\n if (algorithm === \"md5\") return md5\n throw new Error(`Unsupported digest ${algorithm}`)\n}\nconst bytes = (input: string | Uint8Array) => typeof input === \"string\" ? new TextEncoder().encode(input) : input\ninterface Hash {\n update(input: string | Uint8Array): Hash\n digest(): Buffer\n digest(encoding: string): string\n}\nexport function createHash(algorithm: string): Hash {\n const parts: Uint8Array[] = []\n const hash = {\n update(input: string | Uint8Array) { parts.push(bytes(input)); return hash },\n digest(encoding?: string): Buffer | string {\n const result = Buffer.from(digest(algorithm)(Buffer.concat(parts)))\n return encoding === \"base64url\" ? result.toString(\"base64\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\") : encoding ? result.toString(encoding as BufferEncoding) : result\n },\n }\n return hash as Hash\n}\nexport function createHmac(algorithm: string, key: string | Uint8Array): Hash {\n const parts: Uint8Array[] = []\n const hash = {\n update(input: string | Uint8Array) { parts.push(bytes(input)); return hash },\n digest(encoding?: string): Buffer | string {\n const result = Buffer.from(hmac(digest(algorithm), bytes(key), Buffer.concat(parts)))\n return encoding === \"base64url\" ? result.toString(\"base64\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\") : encoding ? result.toString(encoding as BufferEncoding) : result\n },\n }\n return hash as Hash\n}\n/**\n * Mockingbird: where ids and tokens come from. With a seeded stream in place, everything the\n * vendor code mints replays from the seed; without one it is the platform's own randomness.\n */\nlet source: (() => number) | undefined\n/** Mint from `next` (a seeded stream of `[0, 1)`) until told otherwise. */\nexport function useRandom(next: (() => number) | undefined): void { source = next }\n/** Mint from `next` for one synchronous section, then put the previous source back. */\nexport function withRandom<T>(next: (() => number) | undefined, run: () => T): T {\n const previous = source\n source = next\n try { return run() } finally { source = previous }\n}\n/** `Math.random`, from the seeded stream when one is in place. */\nexport function random(): number { return source ? source() : Math.random() }\nexport function randomBytes(length: number): Buffer {\n if (!source) return Buffer.from(crypto.getRandomValues(new Uint8Array(length)))\n const out = new Uint8Array(length)\n for (let i = 0; i < length; i++) out[i] = Math.floor(source() * 256)\n return Buffer.from(out)\n}\nexport function randomUUID(): string {\n if (!source) return crypto.randomUUID()\n const b = randomBytes(16)\n b[6] = (b[6] & 0x0f) | 0x40\n b[8] = (b[8] & 0x3f) | 0x80\n const hex = b.toString(\"hex\")\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\nexport function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) throw new Error(\"Buffers must have equal lengths\")\n let difference = 0\n for (let i = 0; i < a.length; i++) difference |= a[i] ^ b[i]\n return difference === 0\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -4,11 +4,14 @@ import {
4
4
  createClock,
5
5
  createRuntime,
6
6
  markSocketClosed,
7
+ normalizeBootControls,
7
8
  openSocketCount,
8
9
  parseDuration,
10
+ parseFaultHeader,
9
11
  resolveAdminPrefix,
12
+ restoreClock,
10
13
  unbindSocketTransport
11
- } from "./chunk-A2QC6SAR.js";
14
+ } from "./chunk-6MIIKZZB.js";
12
15
 
13
16
  // ../../adapters/node/dist/cli.js
14
17
  import { readFile as readFile2 } from "node:fs/promises";
@@ -55,6 +58,8 @@ var serve = async (api, options = {}) => {
55
58
  req.socket.destroy();
56
59
  return;
57
60
  }
61
+ if (aborted.signal.aborted)
62
+ return;
58
63
  res.writeHead(500, { "content-type": "application/json" });
59
64
  res.end(JSON.stringify({
60
65
  error: {
@@ -86,6 +91,7 @@ var serve = async (api, options = {}) => {
86
91
  }
87
92
  res.end();
88
93
  } catch {
94
+ await new Promise((resolve2) => setImmediate(resolve2));
89
95
  res.destroy();
90
96
  } finally {
91
97
  reader.releaseLock();
@@ -168,10 +174,12 @@ var httpChild = async (target, entry, options) => {
168
174
  adminPrefix: resolveAdminPrefix(entry.adminPrefix),
169
175
  adminKey: entry.adminKey,
170
176
  seed: entry.seed,
171
- onLog: options.onLog
177
+ onLog: options.onLog,
178
+ ...entry.adminControls !== void 0 ? { adminControls: entry.adminControls } : {},
179
+ ...entry.fixtures !== void 0 ? { fixtures: entry.fixtures } : {},
180
+ ...entry.baseUrl !== void 0 ? { baseUrl: entry.baseUrl } : {}
172
181
  });
173
182
  const adminPrefix = resolveAdminPrefix(entry.adminPrefix);
174
- runtime.isolateNamespaces();
175
183
  const locked = /* @__PURE__ */ new Set();
176
184
  const active = /* @__PURE__ */ new Map();
177
185
  const api = {
@@ -433,19 +441,12 @@ async function startFleet(config, options) {
433
441
  return json(400, { error: "freeze must be boolean" });
434
442
  const response2 = await transaction(namespace, selected, async (_name, child) => {
435
443
  const childClock = child.clock(namespace);
436
- childClock.freeze();
437
- childClock.set(target);
438
- if (!frozen)
439
- childClock.unfreeze();
444
+ restoreClock(childClock, { now: target, frozen });
440
445
  await child.settle?.(namespace);
441
446
  return childClock.state();
442
447
  });
443
- if (response2.ok) {
444
- current.freeze();
445
- current.set(target);
446
- if (!frozen)
447
- current.unfreeze();
448
- }
448
+ if (response2.ok)
449
+ restoreClock(current, { now: target, frozen });
449
450
  return response2;
450
451
  }
451
452
  if (!route)
@@ -464,13 +465,8 @@ async function startFleet(config, options) {
464
465
  return json(404, { error: "no snapshot in this namespace" });
465
466
  const restored = new Map([...point.values].map(([name]) => [name, children.get(name)]));
466
467
  const response2 = await transaction(namespace, restored, async (name, child) => child.restore(namespace, point.values.get(name)));
467
- if (response2.ok) {
468
- const current = clock(namespace);
469
- current.freeze();
470
- current.set(point.clock.now);
471
- if (!point.clock.frozen)
472
- current.unfreeze();
473
- }
468
+ if (response2.ok)
469
+ restoreClock(clock(namespace), point.clock);
474
470
  return response2;
475
471
  }
476
472
  const snapshotId = randomUUID();
@@ -496,6 +492,8 @@ async function startFleet(config, options) {
496
492
  for (const [name, entry] of Object.entries(config.services)) {
497
493
  if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name))
498
494
  throw new Error("invalid service name");
495
+ if (config.adminControls !== void 0 || entry.adminControls !== void 0)
496
+ normalizeBootControls({ ...config.adminControls, ...entry.adminControls }, `services.${name}.adminControls`);
499
497
  try {
500
498
  const target = await options.load(name);
501
499
  if (entry.protocol && entry.protocol !== ("protocol" in target ? target.protocol : "http"))
@@ -504,7 +502,9 @@ async function startFleet(config, options) {
504
502
  const child = "start" in target ? await target.start(entry) : await httpChild(target, {
505
503
  ...entry,
506
504
  adminPrefix: resolveAdminPrefix(entry.adminPrefix ?? adminPrefix),
507
- ...adminKey !== void 0 ? { adminKey } : {}
505
+ ...adminKey !== void 0 ? { adminKey } : {},
506
+ // Fleet-wide controls, with the service's own keys replacing them.
507
+ ...config.adminControls !== void 0 || entry.adminControls !== void 0 ? { adminControls: { ...config.adminControls, ...entry.adminControls } } : {}
508
508
  }, options);
509
509
  children.set(name, child);
510
510
  if (!await child.ready())
@@ -637,7 +637,51 @@ var COMMON_SERVE_OPTIONS = {
637
637
  value: "<key>",
638
638
  description: "Require x-mockingbird-admin-key on /__admin/* (env MOCKINGBIRD_ADMIN_KEY)"
639
639
  },
640
- seed: { type: "string", value: "<seed>", description: "Seed for every random choice" },
640
+ seed: {
641
+ type: "string",
642
+ value: "<seed>",
643
+ description: "Seed for every random choice (env MOCKINGBIRD_SEED)"
644
+ },
645
+ "admin-controls": {
646
+ type: "string",
647
+ value: "<json|file>",
648
+ description: "Admin controls to boot with, as JSON or a JSON file: clock, latency, faults, snapshot, namespaces (env MOCKINGBIRD_ADMIN_CONTROLS)"
649
+ },
650
+ clock: {
651
+ type: "string",
652
+ value: "<instant>",
653
+ description: "Start the clock at this instant (epoch ms or ISO-8601)"
654
+ },
655
+ freeze: { type: "boolean", description: "Freeze the clock: it moves only when told to" },
656
+ "auto-advance": {
657
+ type: "string",
658
+ value: "<duration>",
659
+ description: "Step the clock this much before every request (e.g. 1s)"
660
+ },
661
+ latency: {
662
+ type: "string",
663
+ value: "<duration>",
664
+ description: "Baseline response time for every request (e.g. 250ms)"
665
+ },
666
+ jitter: {
667
+ type: "string",
668
+ value: "<duration>",
669
+ description: "Seeded extra latency between 0 and this"
670
+ },
671
+ "virtual-time": {
672
+ type: "boolean",
673
+ description: "Spend latency, delays and timeouts on the mock clock instead of really waiting"
674
+ },
675
+ fault: {
676
+ type: "string",
677
+ value: "<fault>",
678
+ description: 'A fault to boot with, as in x-mockingbird-fault: 503, rate_limited, "status=503; delay=2s"'
679
+ },
680
+ snapshot: {
681
+ type: "string",
682
+ value: "<file>",
683
+ description: "Start from a snapshot document (GET /__admin/snapshots/:id)"
684
+ },
641
685
  log: {
642
686
  type: "string",
643
687
  value: "<pretty|json|off>",
@@ -684,6 +728,68 @@ var formatLog = (format) => {
684
728
  };
685
729
  };
686
730
  var asString = (value) => typeof value === "string" ? value : void 0;
731
+ var readJsonFile = async (path, label) => {
732
+ try {
733
+ return JSON.parse(await readFile2(path, "utf8"));
734
+ } catch (error) {
735
+ throw new Error(`${label}: cannot read ${path}: ${error instanceof Error ? error.message : error}`);
736
+ }
737
+ };
738
+ var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
739
+ var resolveServeControls = async (values, env = process.env, base) => {
740
+ const source = asString(values["admin-controls"]) ?? env.MOCKINGBIRD_ADMIN_CONTROLS;
741
+ let document = isObject2(base) ? { ...base } : {};
742
+ if (source !== void 0 && source.trim() !== "") {
743
+ let parsed;
744
+ if (source.trim().startsWith("{")) {
745
+ try {
746
+ parsed = JSON.parse(source);
747
+ } catch {
748
+ throw new Error("--admin-controls: not valid JSON");
749
+ }
750
+ } else
751
+ parsed = await readJsonFile(source, "--admin-controls");
752
+ if (!isObject2(parsed))
753
+ throw new Error("--admin-controls: expected a JSON object");
754
+ document = { ...document, ...parsed };
755
+ }
756
+ const clock = isObject2(document.clock) ? { ...document.clock } : {};
757
+ const now = asString(values.clock);
758
+ if (now !== void 0)
759
+ clock.now = now;
760
+ if (values.freeze === true)
761
+ clock.frozen = true;
762
+ const autoAdvance = asString(values["auto-advance"]);
763
+ if (autoAdvance !== void 0)
764
+ clock.autoAdvance = autoAdvance;
765
+ if (Object.keys(clock).length > 0)
766
+ document.clock = clock;
767
+ const delay = asString(values.latency);
768
+ const jitter = asString(values.jitter);
769
+ if (delay !== void 0 || jitter !== void 0) {
770
+ const latency = isObject2(document.latency) ? { ...document.latency } : document.latency !== void 0 ? { delay: document.latency } : {};
771
+ if (delay !== void 0)
772
+ latency.delay = delay;
773
+ if (jitter !== void 0)
774
+ latency.jitter = jitter;
775
+ document.latency = latency;
776
+ }
777
+ if (values["virtual-time"] === true)
778
+ document.virtualTime = true;
779
+ const fault = asString(values.fault);
780
+ if (fault !== void 0)
781
+ document.faults = [
782
+ ...Array.isArray(document.faults) ? document.faults : [],
783
+ parseFaultHeader(fault, "--fault")
784
+ ];
785
+ const snapshot = asString(values.snapshot);
786
+ if (snapshot !== void 0)
787
+ document.snapshot = await readJsonFile(snapshot, "--snapshot");
788
+ if (Object.keys(document).length === 0)
789
+ return void 0;
790
+ normalizeBootControls(document, "adminControls");
791
+ return document;
792
+ };
687
793
  var loadTarget = async (name, own) => {
688
794
  if (name === own.name)
689
795
  return own;
@@ -710,7 +816,8 @@ var start = async (target, values, config) => {
710
816
  adminPrefix,
711
817
  adminKey: config.adminKey,
712
818
  seed: config.seed,
713
- onLog: formatLog(config.log)
819
+ onLog: formatLog(config.log),
820
+ ...config.adminControls !== void 0 ? { adminControls: config.adminControls } : {}
714
821
  });
715
822
  const listening = await listen(runtime, { port: config.port, host: config.host });
716
823
  target.listening?.(listening);
@@ -750,6 +857,9 @@ var serveCommand = (target) => ({
750
857
  const adminKey2 = config.adminKey ?? asString(values["admin-key"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY;
751
858
  if (adminKey2 !== void 0)
752
859
  config.adminKey = adminKey2;
860
+ const adminControls = await resolveServeControls(values, process.env, config.adminControls);
861
+ if (adminControls !== void 0)
862
+ config.adminControls = adminControls;
753
863
  const fleet = await startFleet(config, {
754
864
  load: (name) => loadTarget(name, target),
755
865
  onLog: formatLog(config.log ?? log),
@@ -775,10 +885,12 @@ var serveCommand = (target) => ({
775
885
  }
776
886
  const port = asString(values.port);
777
887
  const adminKey = asString(values["admin-key"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY;
778
- const seed = asString(values.seed);
888
+ const seed = asString(values.seed) ?? process.env.MOCKINGBIRD_SEED;
779
889
  let listening;
780
890
  try {
891
+ const adminControls = await resolveServeControls(values);
781
892
  listening = await start(target, values, {
893
+ ...adminControls !== void 0 ? { adminControls } : {},
782
894
  adminPrefix: resolveAdminPrefix(asString(values["admin-prefix"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX),
783
895
  port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
784
896
  host: asString(values.host) ?? "127.0.0.1",
@@ -794,6 +906,56 @@ var serveCommand = (target) => ({
794
906
  }
795
907
  });
796
908
 
909
+ // ../../adapters/node/dist/fixtures.js
910
+ import { readFile as readFile3 } from "node:fs/promises";
911
+ var providerServeOptions = {
912
+ fixtures: {
913
+ type: "string",
914
+ value: "<json-file>",
915
+ description: "Load synthetic provider fixtures from a JSON object"
916
+ },
917
+ "base-url": {
918
+ type: "string",
919
+ value: "<url>",
920
+ description: "Public HTTP(S) base URL advertised in provider responses"
921
+ }
922
+ };
923
+ async function providerServeValues(values, defaults = {}) {
924
+ const options = {};
925
+ if (values.fixtures !== void 0) {
926
+ if (typeof values.fixtures !== "string")
927
+ throw new Error("--fixtures requires a JSON file");
928
+ let parsed;
929
+ try {
930
+ parsed = JSON.parse(await readFile3(values.fixtures, "utf8"));
931
+ } catch {
932
+ throw new Error("Cannot read --fixtures as JSON; supply a synthetic provider fixture object");
933
+ }
934
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
935
+ throw new Error("--fixtures must contain a JSON object");
936
+ options.fixtures = parsed;
937
+ } else if (defaults.fixtures !== void 0) {
938
+ if (defaults.fixtures === null || typeof defaults.fixtures !== "object" || Array.isArray(defaults.fixtures))
939
+ throw new Error("fleet fixtures must contain a JSON object");
940
+ options.fixtures = defaults.fixtures;
941
+ }
942
+ const advertised = values["base-url"] ?? defaults.baseUrl;
943
+ if (advertised !== void 0) {
944
+ let url;
945
+ try {
946
+ if (typeof advertised !== "string")
947
+ throw new Error();
948
+ url = new URL(advertised);
949
+ } catch {
950
+ throw new Error("--base-url must be an absolute HTTP(S) URL");
951
+ }
952
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash)
953
+ throw new Error("--base-url must be HTTP(S) without credentials, query or fragment");
954
+ options.baseUrl = url.href.replace(/\/+$/, "");
955
+ }
956
+ return options;
957
+ }
958
+
797
959
  // src/socket-server.ts
798
960
  import { createHash } from "node:crypto";
799
961
  var GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@@ -1096,6 +1258,7 @@ var serveTarget = {
1096
1258
  name: "slack",
1097
1259
  defaultPort: DEFAULT_PORT,
1098
1260
  options: {
1261
+ ...providerServeOptions,
1099
1262
  token: {
1100
1263
  type: "string",
1101
1264
  value: "<xoxb-\u2026>",
@@ -1106,15 +1269,17 @@ var serveTarget = {
1106
1269
  description: "Answer channel_not_found for channels not created through /__admin/channels"
1107
1270
  }
1108
1271
  },
1109
- create: (values, common) => {
1272
+ create: async (values, common) => {
1110
1273
  const token = text(values.token);
1111
1274
  return createRuntime({
1275
+ ...await providerServeValues(values, common),
1112
1276
  settings: {
1113
1277
  ...token ? { tokens: [token] } : {},
1114
1278
  ...values["strict-channels"] === true ? { strictChannels: true } : {}
1115
1279
  },
1116
1280
  ...common.adminPrefix !== void 0 ? { adminPrefix: common.adminPrefix } : {},
1117
1281
  ...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
1282
+ ...common.adminControls !== void 0 ? { adminControls: common.adminControls } : {},
1118
1283
  ...common.seed !== void 0 ? { seed: common.seed } : {},
1119
1284
  ...common.onLog ? { onLog: common.onLog } : {}
1120
1285
  });
@@ -1137,4 +1302,4 @@ export {
1137
1302
  createServer2 as createServer,
1138
1303
  serveTarget
1139
1304
  };
1140
- //# sourceMappingURL=chunk-AJMQBCFN.js.map
1305
+ //# sourceMappingURL=chunk-ZFHCM6E3.js.map