mnemocyte 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -85,7 +85,21 @@ const client = createMnemocyte({
85
85
  });
86
86
  ```
87
87
 
88
- Official embedder helpers are planned, starting with `openaiEmbedder()`.
88
+ You can also use the optional OpenAI helper:
89
+
90
+ ```ts
91
+ import { openaiEmbedder } from "mnemocyte/embedders";
92
+
93
+ const embedder = openaiEmbedder({
94
+ model: "text-embedding-3-small",
95
+ });
96
+ ```
97
+
98
+ `openaiEmbedder()` reads `OPENAI_API_KEY` by default. Pass `apiKey`
99
+ explicitly when you use a different key source. The helper uses `fetch`
100
+ directly and does not depend on the OpenAI SDK; plain `import "mnemocyte"` also
101
+ stays provider-free. The direct subpath `mnemocyte/embedders/openai` is also
102
+ supported for consumers that prefer provider-specific imports.
89
103
 
90
104
  ## Postgres
91
105
 
@@ -111,8 +125,23 @@ backend currently requires a 1536-dimensional embedder.
111
125
  If dimensions do not match, `createMnemocyte` throws a `MnemocyteError` with
112
126
  code `"CONFIG"` before opening the connection pool.
113
127
 
114
- The migration creates an HNSW pgvector index for cosine search. Configurable
115
- embedding dimensions are planned for `0.2.0`.
128
+ The migration creates the bundled HNSW pgvector index
129
+ `mnemocyte_memories_embedding_hnsw_idx` for cosine search. HNSW is approximate:
130
+ it is fast for nearest-neighbor recall, but recall quality, index build memory,
131
+ and write overhead should be benchmarked against your data volume and write
132
+ rate. Postgres still applies ordinary filters such as `entity_id`, `type`, and
133
+ tags around vector search, so highly selective filters may need query tuning or
134
+ a workload-specific index strategy.
135
+
136
+ Large production tables should benchmark alternate indexes or custom
137
+ migrations with representative data before changing the default. IVFFlat can be
138
+ a good fit for some large, steady-state tables, but it needs tuning after the
139
+ table has representative vectors. The bundled migration intentionally does not
140
+ add full-text or tag-specific GIN indexes yet; add and benchmark expression
141
+ indexes such as `to_tsvector('english', content)` or tag-oriented indexes only
142
+ when your workload needs them.
143
+
144
+ Configurable embedding dimensions are planned for `0.2.0`.
116
145
 
117
146
  ## API
118
147
 
@@ -262,12 +291,10 @@ const client = createMnemocyte({
262
291
 
263
292
  ## Roadmap
264
293
 
265
- Near-term work:
294
+ Planned larger milestones:
266
295
 
267
- - production safety and HNSW documentation
268
- - official `openaiEmbedder()`
269
296
  - configurable embedding dimensions with `mnemocyte_meta`
270
- - `Store` abstraction
297
+ - `MemoryStore` abstraction
271
298
  - `drizzleStore(db)` for caller-owned Drizzle clients
272
299
  - `@mnemocyte/mcp`
273
300
 
@@ -0,0 +1,2 @@
1
+ import { OpenAIEmbedderOptions, openaiEmbedder } from "./openai.mjs";
2
+ export { type OpenAIEmbedderOptions, openaiEmbedder };
@@ -0,0 +1,2 @@
1
+ import { openaiEmbedder } from "./openai.mjs";
2
+ export { openaiEmbedder };
@@ -0,0 +1,31 @@
1
+ import { c as Embedder } from "../types-QK_43oKJ.mjs";
2
+
3
+ //#region src/embedders/openai.d.ts
4
+ interface OpenAIEmbedderOptions {
5
+ /**
6
+ * OpenAI API key. Defaults to `process.env.OPENAI_API_KEY`.
7
+ */
8
+ readonly apiKey?: string;
9
+ /**
10
+ * OpenAI embedding model name.
11
+ */
12
+ readonly model: string;
13
+ /**
14
+ * Output dimensions. Required for unknown/future model names and accepted
15
+ * only for OpenAI models that support configurable dimensions.
16
+ */
17
+ readonly dimensions?: number;
18
+ /**
19
+ * Override the OpenAI API base URL. Intended for tests and compatible
20
+ * deployments; defaults to `https://api.openai.com/v1`.
21
+ */
22
+ readonly baseUrl?: string;
23
+ }
24
+ /**
25
+ * Create an OpenAI-backed {@link Embedder}. This adapter uses `fetch`
26
+ * directly so provider SDKs never become part of Mnemocyte's dependency graph.
27
+ */
28
+ declare function openaiEmbedder(options: OpenAIEmbedderOptions): Embedder;
29
+ //#endregion
30
+ export { OpenAIEmbedderOptions, openaiEmbedder };
31
+ //# sourceMappingURL=openai.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.mts","names":[],"sources":["../../src/embedders/openai.ts"],"mappings":";;;UAciB,qBAAA;;AAAjB;;WAIU,MAAA;EAJ4B;;;EAAA,SAQ5B,KAAA;EAUA;;AAAO;AA4EjB;EA5EU,SALA,UAAA;;;;;WAKA,OAAA;AAAA;AA4E8D;;;;AAAA,iBAAxD,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,QAAQ"}
@@ -0,0 +1,78 @@
1
+ import { t as MnemocyteError } from "../errors-BG3-mNur.mjs";
2
+ //#region src/embedders/openai.ts
3
+ const KNOWN_MODEL_DIMENSIONS = {
4
+ "text-embedding-3-small": 1536,
5
+ "text-embedding-3-large": 3072,
6
+ "text-embedding-ada-002": 1536
7
+ };
8
+ const CONFIGURABLE_DIMENSION_MODELS = new Set(["text-embedding-3-small", "text-embedding-3-large"]);
9
+ function assertDimensions(dimensions) {
10
+ if (!Number.isInteger(dimensions) || dimensions < 1) throw new MnemocyteError("openaiEmbedder dimensions must be a positive integer.", "CONFIG");
11
+ }
12
+ function resolveApiKey(apiKey) {
13
+ const resolved = apiKey ?? process.env.OPENAI_API_KEY;
14
+ if (!resolved || resolved.trim().length === 0) throw new MnemocyteError("openaiEmbedder requires an API key. Pass apiKey or set OPENAI_API_KEY.", "CONFIG");
15
+ return resolved;
16
+ }
17
+ function resolveDimensions(options) {
18
+ const defaultDimensions = KNOWN_MODEL_DIMENSIONS[options.model];
19
+ if (defaultDimensions === void 0) {
20
+ if (options.dimensions === void 0) throw new MnemocyteError(`openaiEmbedder requires dimensions for unknown model "${options.model}".`, "CONFIG");
21
+ assertDimensions(options.dimensions);
22
+ return {
23
+ embedderDimensions: options.dimensions,
24
+ requestDimensions: options.dimensions
25
+ };
26
+ }
27
+ if (options.dimensions !== void 0 && !CONFIGURABLE_DIMENSION_MODELS.has(options.model)) throw new MnemocyteError(`openaiEmbedder does not support a dimensions override for "${options.model}".`, "CONFIG");
28
+ if (options.dimensions !== void 0) {
29
+ assertDimensions(options.dimensions);
30
+ return {
31
+ embedderDimensions: options.dimensions,
32
+ requestDimensions: options.dimensions
33
+ };
34
+ }
35
+ return { embedderDimensions: defaultDimensions };
36
+ }
37
+ /**
38
+ * Create an OpenAI-backed {@link Embedder}. This adapter uses `fetch`
39
+ * directly so provider SDKs never become part of Mnemocyte's dependency graph.
40
+ */
41
+ function openaiEmbedder(options) {
42
+ const apiKey = resolveApiKey(options.apiKey);
43
+ const { embedderDimensions, requestDimensions } = resolveDimensions(options);
44
+ const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
45
+ return {
46
+ model: options.model,
47
+ dimensions: embedderDimensions,
48
+ async embed(texts, embedOptions) {
49
+ const response = await fetch(`${baseUrl}/embeddings`, {
50
+ method: "POST",
51
+ headers: {
52
+ authorization: `Bearer ${apiKey}`,
53
+ "content-type": "application/json"
54
+ },
55
+ body: JSON.stringify({
56
+ model: options.model,
57
+ input: [...texts],
58
+ encoding_format: "float",
59
+ ...requestDimensions === void 0 ? {} : { dimensions: requestDimensions }
60
+ }),
61
+ ...embedOptions?.signal === void 0 ? {} : { signal: embedOptions.signal }
62
+ });
63
+ if (!response.ok) throw Object.assign(/* @__PURE__ */ new Error(`OpenAI embeddings request failed with status ${response.status}.`), { status: response.status });
64
+ const body = await response.json();
65
+ const embeddings = Array.from({ length: texts.length });
66
+ for (const item of body.data) {
67
+ if (!Number.isInteger(item.index) || item.index < 0 || item.index >= texts.length) throw new MnemocyteError("OpenAI returned an embedding with an invalid index.", "EMBEDDING");
68
+ embeddings[item.index] = item.embedding;
69
+ }
70
+ if (embeddings.some((embedding) => embedding === void 0)) throw new MnemocyteError("OpenAI returned fewer embeddings than requested.", "EMBEDDING");
71
+ return embeddings;
72
+ }
73
+ };
74
+ }
75
+ //#endregion
76
+ export { openaiEmbedder };
77
+
78
+ //# sourceMappingURL=openai.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.mjs","names":[],"sources":["../../src/embedders/openai.ts"],"sourcesContent":["import { MnemocyteError } from \"../errors.js\";\nimport type { Embedder } from \"../types.js\";\n\nconst KNOWN_MODEL_DIMENSIONS: Record<string, number> = {\n\t\"text-embedding-3-small\": 1536,\n\t\"text-embedding-3-large\": 3072,\n\t\"text-embedding-ada-002\": 1536,\n};\n\nconst CONFIGURABLE_DIMENSION_MODELS = new Set([\n\t\"text-embedding-3-small\",\n\t\"text-embedding-3-large\",\n]);\n\nexport interface OpenAIEmbedderOptions {\n\t/**\n\t * OpenAI API key. Defaults to `process.env.OPENAI_API_KEY`.\n\t */\n\treadonly apiKey?: string;\n\t/**\n\t * OpenAI embedding model name.\n\t */\n\treadonly model: string;\n\t/**\n\t * Output dimensions. Required for unknown/future model names and accepted\n\t * only for OpenAI models that support configurable dimensions.\n\t */\n\treadonly dimensions?: number;\n\t/**\n\t * Override the OpenAI API base URL. Intended for tests and compatible\n\t * deployments; defaults to `https://api.openai.com/v1`.\n\t */\n\treadonly baseUrl?: string;\n}\n\ninterface OpenAIEmbeddingItem {\n\treadonly embedding: number[];\n\treadonly index: number;\n}\n\ninterface OpenAIEmbeddingResponse {\n\treadonly data: OpenAIEmbeddingItem[];\n}\n\nfunction assertDimensions(dimensions: number): void {\n\tif (!Number.isInteger(dimensions) || dimensions < 1) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"openaiEmbedder dimensions must be a positive integer.\",\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n}\n\nfunction resolveApiKey(apiKey: string | undefined): string {\n\tconst resolved = apiKey ?? process.env.OPENAI_API_KEY;\n\tif (!resolved || resolved.trim().length === 0) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"openaiEmbedder requires an API key. Pass apiKey or set OPENAI_API_KEY.\",\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nfunction resolveDimensions(options: OpenAIEmbedderOptions): {\n\tembedderDimensions: number;\n\trequestDimensions?: number;\n} {\n\tconst defaultDimensions = KNOWN_MODEL_DIMENSIONS[options.model];\n\tif (defaultDimensions === undefined) {\n\t\tif (options.dimensions === undefined) {\n\t\t\tthrow new MnemocyteError(\n\t\t\t\t`openaiEmbedder requires dimensions for unknown model \"${options.model}\".`,\n\t\t\t\t\"CONFIG\",\n\t\t\t);\n\t\t}\n\t\tassertDimensions(options.dimensions);\n\t\treturn {\n\t\t\tembedderDimensions: options.dimensions,\n\t\t\trequestDimensions: options.dimensions,\n\t\t};\n\t}\n\n\tif (\n\t\toptions.dimensions !== undefined &&\n\t\t!CONFIGURABLE_DIMENSION_MODELS.has(options.model)\n\t) {\n\t\tthrow new MnemocyteError(\n\t\t\t`openaiEmbedder does not support a dimensions override for \"${options.model}\".`,\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n\n\tif (options.dimensions !== undefined) {\n\t\tassertDimensions(options.dimensions);\n\t\treturn {\n\t\t\tembedderDimensions: options.dimensions,\n\t\t\trequestDimensions: options.dimensions,\n\t\t};\n\t}\n\n\treturn { embedderDimensions: defaultDimensions };\n}\n\n/**\n * Create an OpenAI-backed {@link Embedder}. This adapter uses `fetch`\n * directly so provider SDKs never become part of Mnemocyte's dependency graph.\n */\nexport function openaiEmbedder(options: OpenAIEmbedderOptions): Embedder {\n\tconst apiKey = resolveApiKey(options.apiKey);\n\tconst { embedderDimensions, requestDimensions } = resolveDimensions(options);\n\tconst baseUrl = options.baseUrl ?? \"https://api.openai.com/v1\";\n\n\treturn {\n\t\tmodel: options.model,\n\t\tdimensions: embedderDimensions,\n\t\tasync embed(texts, embedOptions) {\n\t\t\tconst response = await fetch(`${baseUrl}/embeddings`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${apiKey}`,\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: options.model,\n\t\t\t\t\tinput: [...texts],\n\t\t\t\t\tencoding_format: \"float\",\n\t\t\t\t\t...(requestDimensions === undefined\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: { dimensions: requestDimensions }),\n\t\t\t\t}),\n\t\t\t\t...(embedOptions?.signal === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { signal: embedOptions.signal }),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow Object.assign(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t`OpenAI embeddings request failed with status ${response.status}.`,\n\t\t\t\t\t),\n\t\t\t\t\t{ status: response.status },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst body = (await response.json()) as OpenAIEmbeddingResponse;\n\n\t\t\tconst embeddings: Array<number[] | undefined> = Array.from({\n\t\t\t\tlength: texts.length,\n\t\t\t});\n\t\t\tfor (const item of body.data) {\n\t\t\t\tif (\n\t\t\t\t\t!Number.isInteger(item.index) ||\n\t\t\t\t\titem.index < 0 ||\n\t\t\t\t\titem.index >= texts.length\n\t\t\t\t) {\n\t\t\t\t\tthrow new MnemocyteError(\n\t\t\t\t\t\t\"OpenAI returned an embedding with an invalid index.\",\n\t\t\t\t\t\t\"EMBEDDING\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tembeddings[item.index] = item.embedding;\n\t\t\t}\n\t\t\tif (embeddings.some((embedding) => embedding === undefined)) {\n\t\t\t\tthrow new MnemocyteError(\n\t\t\t\t\t\"OpenAI returned fewer embeddings than requested.\",\n\t\t\t\t\t\"EMBEDDING\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn embeddings as number[][];\n\t\t},\n\t};\n}\n"],"mappings":";;AAGA,MAAM,yBAAiD;CACtD,0BAA0B;CAC1B,0BAA0B;CAC1B,0BAA0B;AAC3B;AAEA,MAAM,gCAAgC,IAAI,IAAI,CAC7C,0BACA,wBACD,CAAC;AAgCD,SAAS,iBAAiB,YAA0B;CACnD,IAAI,CAAC,OAAO,UAAU,UAAU,KAAK,aAAa,GACjD,MAAM,IAAI,eACT,yDACA,QACD;AAEF;AAEA,SAAS,cAAc,QAAoC;CAC1D,MAAM,WAAW,UAAU,QAAQ,IAAI;CACvC,IAAI,CAAC,YAAY,SAAS,KAAK,EAAE,WAAW,GAC3C,MAAM,IAAI,eACT,0EACA,QACD;CAED,OAAO;AACR;AAEA,SAAS,kBAAkB,SAGzB;CACD,MAAM,oBAAoB,uBAAuB,QAAQ;CACzD,IAAI,sBAAsB,KAAA,GAAW;EACpC,IAAI,QAAQ,eAAe,KAAA,GAC1B,MAAM,IAAI,eACT,yDAAyD,QAAQ,MAAM,KACvE,QACD;EAED,iBAAiB,QAAQ,UAAU;EACnC,OAAO;GACN,oBAAoB,QAAQ;GAC5B,mBAAmB,QAAQ;EAC5B;CACD;CAEA,IACC,QAAQ,eAAe,KAAA,KACvB,CAAC,8BAA8B,IAAI,QAAQ,KAAK,GAEhD,MAAM,IAAI,eACT,8DAA8D,QAAQ,MAAM,KAC5E,QACD;CAGD,IAAI,QAAQ,eAAe,KAAA,GAAW;EACrC,iBAAiB,QAAQ,UAAU;EACnC,OAAO;GACN,oBAAoB,QAAQ;GAC5B,mBAAmB,QAAQ;EAC5B;CACD;CAEA,OAAO,EAAE,oBAAoB,kBAAkB;AAChD;;;;;AAMA,SAAgB,eAAe,SAA0C;CACxE,MAAM,SAAS,cAAc,QAAQ,MAAM;CAC3C,MAAM,EAAE,oBAAoB,sBAAsB,kBAAkB,OAAO;CAC3E,MAAM,UAAU,QAAQ,WAAW;CAEnC,OAAO;EACN,OAAO,QAAQ;EACf,YAAY;EACZ,MAAM,MAAM,OAAO,cAAc;GAChC,MAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,cAAc;IACrD,QAAQ;IACR,SAAS;KACR,eAAe,UAAU;KACzB,gBAAgB;IACjB;IACA,MAAM,KAAK,UAAU;KACpB,OAAO,QAAQ;KACf,OAAO,CAAC,GAAG,KAAK;KAChB,iBAAiB;KACjB,GAAI,sBAAsB,KAAA,IACvB,CAAC,IACD,EAAE,YAAY,kBAAkB;IACpC,CAAC;IACD,GAAI,cAAc,WAAW,KAAA,IAC1B,CAAC,IACD,EAAE,QAAQ,aAAa,OAAO;GAClC,CAAC;GAED,IAAI,CAAC,SAAS,IACb,MAAM,OAAO,uBACZ,IAAI,MACH,gDAAgD,SAAS,OAAO,EACjE,GACA,EAAE,QAAQ,SAAS,OAAO,CAC3B;GAGD,MAAM,OAAQ,MAAM,SAAS,KAAK;GAElC,MAAM,aAA0C,MAAM,KAAK,EAC1D,QAAQ,MAAM,OACf,CAAC;GACD,KAAK,MAAM,QAAQ,KAAK,MAAM;IAC7B,IACC,CAAC,OAAO,UAAU,KAAK,KAAK,KAC5B,KAAK,QAAQ,KACb,KAAK,SAAS,MAAM,QAEpB,MAAM,IAAI,eACT,uDACA,WACD;IAED,WAAW,KAAK,SAAS,KAAK;GAC/B;GACA,IAAI,WAAW,MAAM,cAAc,cAAc,KAAA,CAAS,GACzD,MAAM,IAAI,eACT,oDACA,WACD;GAED,OAAO;EACR;CACD;AACD"}
@@ -0,0 +1,53 @@
1
+ //#region src/errors.ts
2
+ /**
3
+ * Error type thrown by every public Mnemocyte API.
4
+ *
5
+ * Use the {@link MnemocyteError.code} property — not the message — to branch
6
+ * on failure modes, or call {@link isMnemocyteError} as a type guard when
7
+ * working with `unknown` errors.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * try {
12
+ * await client.recall({ entityId: "u1", query: "hi" });
13
+ * } catch (err) {
14
+ * if (isMnemocyteError(err) && err.code === "DB") {
15
+ * // handle database failure
16
+ * }
17
+ * throw err;
18
+ * }
19
+ * ```
20
+ */
21
+ var MnemocyteError = class extends Error {
22
+ /** Stable, machine-readable failure code. See {@link MnemocyteErrorCode}. */
23
+ code;
24
+ /** Underlying cause, if any (e.g. an original driver or embedder error). */
25
+ cause;
26
+ /**
27
+ * @param message - Human-readable error message.
28
+ * @param code - Stable failure code; see {@link MnemocyteErrorCode}.
29
+ * @param cause - Optional underlying cause (driver error, network error, etc.).
30
+ */
31
+ constructor(message, code, cause) {
32
+ super(message);
33
+ this.name = "MnemocyteError";
34
+ this.code = code;
35
+ this.cause = cause;
36
+ }
37
+ };
38
+ /**
39
+ * Type guard for {@link MnemocyteError}.
40
+ *
41
+ * Prefer this over `instanceof` when working across module boundaries or
42
+ * bundlers where multiple copies of the class may exist.
43
+ *
44
+ * @param error - Value to test (typically a caught `unknown`).
45
+ * @returns `true` if `error` is a {@link MnemocyteError}.
46
+ */
47
+ function isMnemocyteError(error) {
48
+ return error instanceof MnemocyteError;
49
+ }
50
+ //#endregion
51
+ export { isMnemocyteError as n, MnemocyteError as t };
52
+
53
+ //# sourceMappingURL=errors-BG3-mNur.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-BG3-mNur.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Stable, machine-readable error code attached to every {@link MnemocyteError}.\n *\n * - `\"CONFIG\"` — invalid client configuration (e.g. missing or malformed embedder).\n * - `\"VALIDATION\"` — invalid argument passed to a client method.\n * - `\"DB\"` — Postgres or driver-level failure.\n * - `\"EMBEDDING\"` — embedder threw or returned an invalid vector.\n * - `\"NOT_FOUND\"` — referenced memory or entity does not exist.\n * - `\"MIGRATION\"` — schema/migration check failed against the target database.\n * - `\"TIMEOUT\"` — a provider call exceeded its configured timeout.\n * - `\"ABORTED\"` — the operation was cancelled via an `AbortSignal`.\n */\nexport type MnemocyteErrorCode =\n\t| \"CONFIG\"\n\t| \"VALIDATION\"\n\t| \"DB\"\n\t| \"EMBEDDING\"\n\t| \"NOT_FOUND\"\n\t| \"MIGRATION\"\n\t| \"TIMEOUT\"\n\t| \"ABORTED\";\n\n/**\n * Error type thrown by every public Mnemocyte API.\n *\n * Use the {@link MnemocyteError.code} property — not the message — to branch\n * on failure modes, or call {@link isMnemocyteError} as a type guard when\n * working with `unknown` errors.\n *\n * @example\n * ```ts\n * try {\n * await client.recall({ entityId: \"u1\", query: \"hi\" });\n * } catch (err) {\n * if (isMnemocyteError(err) && err.code === \"DB\") {\n * // handle database failure\n * }\n * throw err;\n * }\n * ```\n */\nexport class MnemocyteError extends Error {\n\t/** Stable, machine-readable failure code. See {@link MnemocyteErrorCode}. */\n\treadonly code: MnemocyteErrorCode;\n\t/** Underlying cause, if any (e.g. an original driver or embedder error). */\n\toverride readonly cause?: unknown;\n\n\t/**\n\t * @param message - Human-readable error message.\n\t * @param code - Stable failure code; see {@link MnemocyteErrorCode}.\n\t * @param cause - Optional underlying cause (driver error, network error, etc.).\n\t */\n\tconstructor(message: string, code: MnemocyteErrorCode, cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"MnemocyteError\";\n\t\tthis.code = code;\n\t\tthis.cause = cause;\n\t}\n}\n\n/**\n * Type guard for {@link MnemocyteError}.\n *\n * Prefer this over `instanceof` when working across module boundaries or\n * bundlers where multiple copies of the class may exist.\n *\n * @param error - Value to test (typically a caught `unknown`).\n * @returns `true` if `error` is a {@link MnemocyteError}.\n */\nexport function isMnemocyteError(error: unknown): error is MnemocyteError {\n\treturn error instanceof MnemocyteError;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyCA,IAAa,iBAAb,cAAoC,MAAM;;CAEzC;;CAEA;;;;;;CAOA,YAAY,SAAiB,MAA0B,OAAiB;EACvE,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,QAAQ;CACd;AACD;;;;;;;;;;AAWA,SAAgB,iBAAiB,OAAyC;CACzE,OAAO,iBAAiB;AACzB"}