@c9up/echo 0.1.15 → 0.1.16

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/quasar.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Resolving a Redis connection by name, from `@c9up/quasar`.
3
3
  *
4
- * Echo does not depend on quasar: it is an optional peer, and this module
5
- * never imports it statically. The specifier is built at runtime so the
6
- * TypeScript build stays free of it too — a hard type import would make echo
7
- * unbuildable for anyone who caches in memory.
4
+ * Echo does not depend on quasar: it is an optional peer, and neither this
5
+ * module nor the vendored loader it calls imports it statically. The specifier
6
+ * is built at runtime so the TypeScript build stays free of it too.
8
7
  *
9
- * The shape is checked before use rather than asserted, the same way echo
10
- * duck-types its host framework.
8
+ * The loading, the shape check and the messages are the same in every package
9
+ * that offers a Redis-backed option, so they are vendored rather than written
10
+ * again: `src/vendor/quasarConnection.ts`, generated from one source. The bus
11
+ * below is echo's alone and stays here.
11
12
  */
12
13
  import type { RedisClient } from "./drivers/RedisDriver.js";
13
14
  import type { CacheBus } from "./drivers/TieredDriver.js";
@@ -1 +1 @@
1
- {"version":3,"file":"quasar.d.ts","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAyCtE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CA+B1E;AAuCD;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,QAAQ,CA8CX"}
1
+ {"version":3,"file":"quasar.d.ts","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAwBtE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAS1E;AAqDD;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,QAAQ,CA8CX"}
package/dist/quasar.js CHANGED
@@ -1,70 +1,54 @@
1
1
  /**
2
2
  * Resolving a Redis connection by name, from `@c9up/quasar`.
3
3
  *
4
- * Echo does not depend on quasar: it is an optional peer, and this module
5
- * never imports it statically. The specifier is built at runtime so the
6
- * TypeScript build stays free of it too — a hard type import would make echo
7
- * unbuildable for anyone who caches in memory.
4
+ * Echo does not depend on quasar: it is an optional peer, and neither this
5
+ * module nor the vendored loader it calls imports it statically. The specifier
6
+ * is built at runtime so the TypeScript build stays free of it too.
8
7
  *
9
- * The shape is checked before use rather than asserted, the same way echo
10
- * duck-types its host framework.
8
+ * The loading, the shape check and the messages are the same in every package
9
+ * that offers a Redis-backed option, so they are vendored rather than written
10
+ * again: `src/vendor/quasarConnection.ts`, generated from one source. The bus
11
+ * below is echo's alone and stays here.
11
12
  */
12
- function isConnectionSource(value) {
13
- return (typeof value === "object" &&
14
- value !== null &&
15
- typeof Reflect.get(value, "connection") === "function");
16
- }
17
- function isRedisClient(value) {
18
- if (typeof value !== "object" || value === null)
19
- return false;
20
- // The commands this driver actually issues. A connection missing one would
21
- // fail on the first cache write, far from the cause.
22
- //
23
- // `keys` and `exists` are deliberately NOT required: `RedisClient` declares
24
- // them, but nothing here calls them — `flush()` walks a SCAN cursor because
25
- // it considers KEYS unsafe in production. Demanding `keys` would reject a
26
- // client that omits it on exactly the grounds this driver agrees with.
27
- // `scan` is optional on the interface, and `flush()` reports its absence
28
- // with a precise message of its own.
29
- const required = [
30
- "get",
31
- "set",
32
- "del",
33
- "sadd",
34
- "srem",
35
- "smembers",
36
- "expire",
37
- "ttl",
38
- ];
39
- return required.every((name) => typeof Reflect.get(value, name) === "function");
40
- }
13
+ import { quasarConnection as loadQuasarConnection } from "./vendor/quasarConnection.js";
14
+ /**
15
+ * The commands this driver actually issues.
16
+ *
17
+ * `keys` and `exists` are deliberately absent: `RedisClient` declares them, but
18
+ * nothing here calls them — `flush()` walks a SCAN cursor because it considers
19
+ * KEYS unsafe in production, so demanding `keys` would reject a client that
20
+ * omits it on exactly the grounds this driver agrees with. `scan` is optional
21
+ * on the interface, and `flush()` reports its absence with a message of its
22
+ * own.
23
+ */
24
+ const REQUIRED = [
25
+ "get",
26
+ "set",
27
+ "del",
28
+ "sadd",
29
+ "srem",
30
+ "smembers",
31
+ "expire",
32
+ "ttl",
33
+ ];
41
34
  /**
42
35
  * A resolver for `drivers.redis({ connection })` — quasar is loaded on the
43
36
  * first cache command, not at config time.
44
37
  */
45
38
  export function quasarConnection(name) {
46
- return async () => {
47
- const specifier = "@c9up/quasar/services/main";
48
- let loaded;
49
- try {
50
- loaded = await import(/* @vite-ignore */ specifier);
51
- }
52
- catch (cause) {
53
- throw new Error(`Echo: the "${name ?? "default"}" cache store asks for a quasar connection, but @c9up/quasar is not installed.\n` +
54
- " pnpm add @c9up/quasar", { cause });
55
- }
56
- const manager = isConnectionSource(loaded)
57
- ? loaded
58
- : Reflect.get(Object(loaded), "default");
59
- if (!isConnectionSource(manager)) {
60
- throw new Error("Echo: @c9up/quasar/services/main did not expose a connection() manager");
61
- }
62
- const connection = manager.connection(name);
63
- if (!isRedisClient(connection)) {
64
- throw new Error(`Echo: quasar connection "${name ?? "default"}" does not carry the commands this cache needs`);
65
- }
66
- return connection;
67
- };
39
+ return async () => loadQuasarConnection({
40
+ pkg: "echo",
41
+ name,
42
+ required: REQUIRED,
43
+ what: "the cache store",
44
+ raise: (_reason, message, cause) => new Error(message, { cause }),
45
+ });
46
+ }
47
+ function isConnectionSource(value) {
48
+ return (typeof value === "object" &&
49
+ value !== null &&
50
+ "connection" in value &&
51
+ typeof Reflect.get(value, "connection") === "function");
68
52
  }
69
53
  function isPubSubSource(value) {
70
54
  return (typeof value === "object" &&
@@ -1 +1 @@
1
- {"version":3,"file":"quasar.js","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quasar.js","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export interface QuasarConnectionRequest {
2
+ /** The package asking. It prefixes every message this can raise. */
3
+ pkg: string;
4
+ /**
5
+ * How this package builds an error.
6
+ *
7
+ * Not optional, and not `new Error` by default: a package with its own
8
+ * error type carries codes (`E_<PKG>_<REASON>`) that a caller catches on,
9
+ * and a shared helper throwing a bare Error would silently drop them. The
10
+ * two reasons are distinguished so the codes can be too.
11
+ */
12
+ raise: (reason: "quasar-missing" | "incomplete-connection", message: string, cause?: unknown) => Error;
13
+ /** Connection name, or `undefined` for quasar's default. */
14
+ name?: string;
15
+ /** The commands the caller will actually issue. */
16
+ required: readonly string[];
17
+ /** What the caller does with them, for the message that names a gap. */
18
+ what: string;
19
+ /**
20
+ * A second way out, when this package has one.
21
+ *
22
+ * Several of them accept a client object directly, so "pass a client
23
+ * instead of a connection name" is actionable — and it was in each
24
+ * package's own message before this was shared. Dropping it to unify the
25
+ * wording would have made the error strictly less useful.
26
+ */
27
+ alternative?: string;
28
+ }
29
+ /**
30
+ * The named connection, checked for the commands the caller needs BEFORE it is
31
+ * handed over — a connection missing one would otherwise fail on the first
32
+ * command, far from the line that asked for it.
33
+ */
34
+ export declare function quasarConnection<T>(request: QuasarConnectionRequest): Promise<T>;
35
+ //# sourceMappingURL=quasarConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quasarConnection.d.ts","sourceRoot":"","sources":["../../src/vendor/quasarConnection.ts"],"names":[],"mappings":"AAiDA,MAAM,WAAW,uBAAuB;IACvC,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,KAAK,EAAE,CACN,MAAM,EAAE,gBAAgB,GAAG,uBAAuB,EAClD,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,KACX,KAAK,CAAC;IACX,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACvC,OAAO,EAAE,uBAAuB,GAC9B,OAAO,CAAC,CAAC,CAAC,CAuCZ"}
@@ -0,0 +1,62 @@
1
+ // Generated from scripts/vendor/quasarConnection.ts — do not edit.
2
+ //
3
+ // This package is published and built from its own repository, so the file
4
+ // has to exist here rather than be imported. `pnpm vendor:sync` rewrites it,
5
+ // and `pnpm vendor:check` fails if this copy has drifted from the original.
6
+ /**
7
+ * Read a member without assuming how the object answers.
8
+ *
9
+ * Two shapes have to work, and they disagree about `in`. Quasar's accessor is
10
+ * a Proxy over an empty null-prototype object with only a `get` trap, so
11
+ * `"connection" in manager` is FALSE while reading it returns the function —
12
+ * gating on `in` rejected the real manager. And a module namespace under a
13
+ * test double RAISES for an export it does not have, rather than answering
14
+ * undefined, so a bare read fails on the mock. Reading through a catch is what
15
+ * satisfies both.
16
+ */
17
+ function readMember(value, name) {
18
+ if (typeof value !== "object" || value === null)
19
+ return undefined;
20
+ try {
21
+ return Reflect.get(value, name);
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ }
27
+ function isConnectionSource(value) {
28
+ return typeof readMember(value, "connection") === "function";
29
+ }
30
+ /**
31
+ * The named connection, checked for the commands the caller needs BEFORE it is
32
+ * handed over — a connection missing one would otherwise fail on the first
33
+ * command, far from the line that asked for it.
34
+ */
35
+ export async function quasarConnection(request) {
36
+ const { pkg, name, required, what, raise, alternative } = request;
37
+ const specifier = "@c9up/quasar/services/main";
38
+ let loaded;
39
+ try {
40
+ loaded = await import(/* @vite-ignore */ specifier);
41
+ }
42
+ catch (cause) {
43
+ throw raise("quasar-missing", `[${pkg}] ${what} asks for the quasar connection "${name ?? "default"}", but @c9up/quasar is not installed.\n` +
44
+ ` pnpm add @c9up/quasar${alternative ? `, ${alternative}` : ""}`, cause);
45
+ }
46
+ const manager = isConnectionSource(loaded)
47
+ ? loaded
48
+ : Reflect.get(Object(loaded), "default");
49
+ if (!isConnectionSource(manager)) {
50
+ throw raise("quasar-missing", `[${pkg}] @c9up/quasar/services/main did not expose a connection() manager`);
51
+ }
52
+ const connection = manager.connection(name);
53
+ const missing = required.filter((command) => typeof readMember(connection, command) !== "function");
54
+ if (missing.length > 0) {
55
+ throw raise("incomplete-connection", `[${pkg}] the quasar connection${name ? ` '${name}'` : ""} is missing ${missing.join(", ")}, which ${what} issues`);
56
+ }
57
+ // Load-bearing: every member of `required` has just been checked to be a
58
+ // function on this object. The caller names the type it needs because only
59
+ // the caller knows which commands it asked for.
60
+ return connection;
61
+ }
62
+ //# sourceMappingURL=quasarConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quasarConnection.js","sourceRoot":"","sources":["../../src/vendor/quasarConnection.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAqB5E;;;;;;;;;;GAUG;AACH,SAAS,UAAU,CAAC,KAAc,EAAE,IAAY;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClE,IAAI,CAAC;QACJ,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACzC,OAAO,OAAO,UAAU,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,UAAU,CAAC;AAC9D,CAAC;AAmCD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,OAAgC;IAEhC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAClE,MAAM,SAAS,GAAG,4BAA4B,CAAC;IAC/C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACJ,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,KAAK,CACV,gBAAgB,EAChB,IAAI,GAAG,KAAK,IAAI,oCAAoC,IAAI,IAAI,SAAS,yCAAyC;YAC7G,0BAA0B,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAClE,KAAK,CACL,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC;QACzC,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,MAAM,KAAK,CACV,gBAAgB,EAChB,IAAI,GAAG,oEAAoE,CAC3E,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAC9B,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,UAAU,CAClE,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,KAAK,CACV,uBAAuB,EACvB,IAAI,GAAG,0BAA0B,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,eAAe,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,SAAS,CAClH,CAAC;IACH,CAAC;IACD,yEAAyE;IACzE,2EAA2E;IAC3E,gDAAgD;IAChD,OAAO,UAAe,CAAC;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c9up/echo",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Cache — pluggable cache contract with memory + Redis drivers for the Ream framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/quasar.ts CHANGED
@@ -1,17 +1,55 @@
1
1
  /**
2
2
  * Resolving a Redis connection by name, from `@c9up/quasar`.
3
3
  *
4
- * Echo does not depend on quasar: it is an optional peer, and this module
5
- * never imports it statically. The specifier is built at runtime so the
6
- * TypeScript build stays free of it too — a hard type import would make echo
7
- * unbuildable for anyone who caches in memory.
4
+ * Echo does not depend on quasar: it is an optional peer, and neither this
5
+ * module nor the vendored loader it calls imports it statically. The specifier
6
+ * is built at runtime so the TypeScript build stays free of it too.
8
7
  *
9
- * The shape is checked before use rather than asserted, the same way echo
10
- * duck-types its host framework.
8
+ * The loading, the shape check and the messages are the same in every package
9
+ * that offers a Redis-backed option, so they are vendored rather than written
10
+ * again: `src/vendor/quasarConnection.ts`, generated from one source. The bus
11
+ * below is echo's alone and stays here.
11
12
  */
12
13
 
13
14
  import type { RedisClient } from "./drivers/RedisDriver.js";
14
15
  import type { BusMessage, CacheBus } from "./drivers/TieredDriver.js";
16
+ import { quasarConnection as loadQuasarConnection } from "./vendor/quasarConnection.js";
17
+
18
+ /**
19
+ * The commands this driver actually issues.
20
+ *
21
+ * `keys` and `exists` are deliberately absent: `RedisClient` declares them, but
22
+ * nothing here calls them — `flush()` walks a SCAN cursor because it considers
23
+ * KEYS unsafe in production, so demanding `keys` would reject a client that
24
+ * omits it on exactly the grounds this driver agrees with. `scan` is optional
25
+ * on the interface, and `flush()` reports its absence with a message of its
26
+ * own.
27
+ */
28
+ const REQUIRED = [
29
+ "get",
30
+ "set",
31
+ "del",
32
+ "sadd",
33
+ "srem",
34
+ "smembers",
35
+ "expire",
36
+ "ttl",
37
+ ] as const;
38
+
39
+ /**
40
+ * A resolver for `drivers.redis({ connection })` — quasar is loaded on the
41
+ * first cache command, not at config time.
42
+ */
43
+ export function quasarConnection(name?: string): () => Promise<RedisClient> {
44
+ return async () =>
45
+ loadQuasarConnection<RedisClient>({
46
+ pkg: "echo",
47
+ name,
48
+ required: REQUIRED,
49
+ what: "the cache store",
50
+ raise: (_reason, message, cause) => new Error(message, { cause }),
51
+ });
52
+ }
15
53
 
16
54
  /** The slice of quasar's manager this needs: a connection, by name. */
17
55
  interface ConnectionSource {
@@ -22,73 +60,11 @@ function isConnectionSource(value: unknown): value is ConnectionSource {
22
60
  return (
23
61
  typeof value === "object" &&
24
62
  value !== null &&
63
+ "connection" in value &&
25
64
  typeof Reflect.get(value, "connection") === "function"
26
65
  );
27
66
  }
28
67
 
29
- function isRedisClient(value: unknown): value is RedisClient {
30
- if (typeof value !== "object" || value === null) return false;
31
- // The commands this driver actually issues. A connection missing one would
32
- // fail on the first cache write, far from the cause.
33
- //
34
- // `keys` and `exists` are deliberately NOT required: `RedisClient` declares
35
- // them, but nothing here calls them — `flush()` walks a SCAN cursor because
36
- // it considers KEYS unsafe in production. Demanding `keys` would reject a
37
- // client that omits it on exactly the grounds this driver agrees with.
38
- // `scan` is optional on the interface, and `flush()` reports its absence
39
- // with a precise message of its own.
40
- const required = [
41
- "get",
42
- "set",
43
- "del",
44
- "sadd",
45
- "srem",
46
- "smembers",
47
- "expire",
48
- "ttl",
49
- ];
50
- return required.every(
51
- (name) => typeof Reflect.get(value, name) === "function",
52
- );
53
- }
54
-
55
- /**
56
- * A resolver for `drivers.redis({ connection })` — quasar is loaded on the
57
- * first cache command, not at config time.
58
- */
59
- export function quasarConnection(name?: string): () => Promise<RedisClient> {
60
- return async () => {
61
- const specifier = "@c9up/quasar/services/main";
62
- let loaded: unknown;
63
- try {
64
- loaded = await import(/* @vite-ignore */ specifier);
65
- } catch (cause) {
66
- throw new Error(
67
- `Echo: the "${name ?? "default"}" cache store asks for a quasar connection, but @c9up/quasar is not installed.\n` +
68
- " pnpm add @c9up/quasar",
69
- { cause },
70
- );
71
- }
72
-
73
- const manager = isConnectionSource(loaded)
74
- ? loaded
75
- : Reflect.get(Object(loaded), "default");
76
- if (!isConnectionSource(manager)) {
77
- throw new Error(
78
- "Echo: @c9up/quasar/services/main did not expose a connection() manager",
79
- );
80
- }
81
-
82
- const connection = manager.connection(name);
83
- if (!isRedisClient(connection)) {
84
- throw new Error(
85
- `Echo: quasar connection "${name ?? "default"}" does not carry the commands this cache needs`,
86
- );
87
- }
88
- return connection;
89
- };
90
- }
91
-
92
68
  /** The slice of quasar's manager a pub/sub bus needs. */
93
69
  interface PubSubSource {
94
70
  publish(channel: string, message: string): unknown;
@@ -0,0 +1,129 @@
1
+ // Generated from scripts/vendor/quasarConnection.ts — do not edit.
2
+ //
3
+ // This package is published and built from its own repository, so the file
4
+ // has to exist here rather than be imported. `pnpm vendor:sync` rewrites it,
5
+ // and `pnpm vendor:check` fails if this copy has drifted from the original.
6
+
7
+ /**
8
+ * Resolving a Redis connection by name, from `@c9up/quasar`.
9
+ *
10
+ * No package here depends on quasar: it is an optional peer, and this module
11
+ * never imports it statically — the specifier is built at runtime so the
12
+ * TypeScript build stays free of it too. A host that never names a connection
13
+ * neither installs it nor pays for it.
14
+ *
15
+ * What varies between callers is passed in rather than copied: which commands
16
+ * they will issue, and what they are doing with them. Both end up in the
17
+ * message, because "the connection is missing lpop" is only useful next to
18
+ * "which the queue's atomic pop issues".
19
+ */
20
+
21
+ /** The slice of quasar's manager this needs: a connection, by name. */
22
+ interface ConnectionSource {
23
+ connection(name?: string): unknown;
24
+ }
25
+
26
+ /**
27
+ * Read a member without assuming how the object answers.
28
+ *
29
+ * Two shapes have to work, and they disagree about `in`. Quasar's accessor is
30
+ * a Proxy over an empty null-prototype object with only a `get` trap, so
31
+ * `"connection" in manager` is FALSE while reading it returns the function —
32
+ * gating on `in` rejected the real manager. And a module namespace under a
33
+ * test double RAISES for an export it does not have, rather than answering
34
+ * undefined, so a bare read fails on the mock. Reading through a catch is what
35
+ * satisfies both.
36
+ */
37
+ function readMember(value: unknown, name: string): unknown {
38
+ if (typeof value !== "object" || value === null) return undefined;
39
+ try {
40
+ return Reflect.get(value, name);
41
+ } catch {
42
+ return undefined;
43
+ }
44
+ }
45
+
46
+ function isConnectionSource(value: unknown): value is ConnectionSource {
47
+ return typeof readMember(value, "connection") === "function";
48
+ }
49
+
50
+ export interface QuasarConnectionRequest {
51
+ /** The package asking. It prefixes every message this can raise. */
52
+ pkg: string;
53
+ /**
54
+ * How this package builds an error.
55
+ *
56
+ * Not optional, and not `new Error` by default: a package with its own
57
+ * error type carries codes (`E_<PKG>_<REASON>`) that a caller catches on,
58
+ * and a shared helper throwing a bare Error would silently drop them. The
59
+ * two reasons are distinguished so the codes can be too.
60
+ */
61
+ raise: (
62
+ reason: "quasar-missing" | "incomplete-connection",
63
+ message: string,
64
+ cause?: unknown,
65
+ ) => Error;
66
+ /** Connection name, or `undefined` for quasar's default. */
67
+ name?: string;
68
+ /** The commands the caller will actually issue. */
69
+ required: readonly string[];
70
+ /** What the caller does with them, for the message that names a gap. */
71
+ what: string;
72
+ /**
73
+ * A second way out, when this package has one.
74
+ *
75
+ * Several of them accept a client object directly, so "pass a client
76
+ * instead of a connection name" is actionable — and it was in each
77
+ * package's own message before this was shared. Dropping it to unify the
78
+ * wording would have made the error strictly less useful.
79
+ */
80
+ alternative?: string;
81
+ }
82
+
83
+ /**
84
+ * The named connection, checked for the commands the caller needs BEFORE it is
85
+ * handed over — a connection missing one would otherwise fail on the first
86
+ * command, far from the line that asked for it.
87
+ */
88
+ export async function quasarConnection<T>(
89
+ request: QuasarConnectionRequest,
90
+ ): Promise<T> {
91
+ const { pkg, name, required, what, raise, alternative } = request;
92
+ const specifier = "@c9up/quasar/services/main";
93
+ let loaded: unknown;
94
+ try {
95
+ loaded = await import(/* @vite-ignore */ specifier);
96
+ } catch (cause) {
97
+ throw raise(
98
+ "quasar-missing",
99
+ `[${pkg}] ${what} asks for the quasar connection "${name ?? "default"}", but @c9up/quasar is not installed.\n` +
100
+ ` pnpm add @c9up/quasar${alternative ? `, ${alternative}` : ""}`,
101
+ cause,
102
+ );
103
+ }
104
+
105
+ const manager = isConnectionSource(loaded)
106
+ ? loaded
107
+ : Reflect.get(Object(loaded), "default");
108
+ if (!isConnectionSource(manager)) {
109
+ throw raise(
110
+ "quasar-missing",
111
+ `[${pkg}] @c9up/quasar/services/main did not expose a connection() manager`,
112
+ );
113
+ }
114
+
115
+ const connection = manager.connection(name);
116
+ const missing = required.filter(
117
+ (command) => typeof readMember(connection, command) !== "function",
118
+ );
119
+ if (missing.length > 0) {
120
+ throw raise(
121
+ "incomplete-connection",
122
+ `[${pkg}] the quasar connection${name ? ` '${name}'` : ""} is missing ${missing.join(", ")}, which ${what} issues`,
123
+ );
124
+ }
125
+ // Load-bearing: every member of `required` has just been checked to be a
126
+ // function on this object. The caller names the type it needs because only
127
+ // the caller knows which commands it asked for.
128
+ return connection as T;
129
+ }