@enterstellar-ai/adapters 0.1.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/dist/index.js ADDED
@@ -0,0 +1,307 @@
1
+ import { EnterstellarError } from '@enterstellar-ai/types';
2
+
3
+ // src/errors.ts
4
+ function adapterValidationError(adapterType, reason) {
5
+ return new EnterstellarError(
6
+ "ENS-7001",
7
+ "adapters",
8
+ `Adapter validation failed for "${adapterType}": ${reason}`,
9
+ false
10
+ // non-recoverable — developer misconfiguration
11
+ );
12
+ }
13
+ function adapterMethodError(adapterName, methodName, cause) {
14
+ return new EnterstellarError(
15
+ "ENS-7002",
16
+ "adapters",
17
+ `Adapter "${adapterName}" method "${methodName}" threw.`,
18
+ true,
19
+ // recoverable — transient infrastructure failure
20
+ cause
21
+ );
22
+ }
23
+ function adapterQueryError(adapterName, resource, cause) {
24
+ return new EnterstellarError(
25
+ "ENS-7003",
26
+ "adapters",
27
+ `Adapter "${adapterName}" query failed for resource "${resource}".`,
28
+ true,
29
+ // recoverable — transient data source failure
30
+ cause
31
+ );
32
+ }
33
+ function adapterMutationError(adapterName, resource, action, cause) {
34
+ return new EnterstellarError(
35
+ "ENS-7004",
36
+ "adapters",
37
+ `Adapter "${adapterName}" mutation "${action}" failed for resource "${resource}".`,
38
+ true,
39
+ // recoverable — transient data source failure
40
+ cause
41
+ );
42
+ }
43
+ function adapterAuthError(adapterName, operation, cause) {
44
+ return new EnterstellarError(
45
+ "ENS-7005",
46
+ "adapters",
47
+ `Adapter "${adapterName}" auth operation "${operation}" failed.`,
48
+ true,
49
+ // recoverable — auth provider may be temporarily unavailable
50
+ cause
51
+ );
52
+ }
53
+
54
+ // src/validate-adapter.ts
55
+ var REQUIRED_METHODS = {
56
+ auth: ["getSession", "hasRole", "onAuthChange"],
57
+ data: ["query", "mutate", "subscribe"],
58
+ error: ["report", "shouldRetry", "sanitize"],
59
+ analytics: ["track", "identify"]
60
+ };
61
+ function validateAdapterConfig(adapterType, config) {
62
+ const name = config["name"];
63
+ if (typeof name !== "string" || name.length === 0) {
64
+ throw adapterValidationError(
65
+ adapterType,
66
+ '"name" must be a non-empty string.'
67
+ );
68
+ }
69
+ const requiredMethods = REQUIRED_METHODS[adapterType];
70
+ for (const methodName of requiredMethods) {
71
+ const method = config[methodName];
72
+ if (typeof method !== "function") {
73
+ const receivedType = method === null ? "null" : typeof method;
74
+ throw adapterValidationError(
75
+ adapterType,
76
+ `Missing or invalid method "${methodName}". Expected function, received ${receivedType}.`
77
+ );
78
+ }
79
+ }
80
+ }
81
+
82
+ // src/create-auth-adapter.ts
83
+ function createAuthAdapter(config) {
84
+ validateAdapterConfig("auth", config);
85
+ const adapterName = config.name;
86
+ const adapter = {
87
+ /**
88
+ * Wrapped `getSession()` — catches vendor errors → `ENS-7005`.
89
+ */
90
+ async getSession() {
91
+ try {
92
+ return await config.getSession();
93
+ } catch (error) {
94
+ throw adapterAuthError(adapterName, "getSession", error);
95
+ }
96
+ },
97
+ /**
98
+ * Wrapped `hasRole()` — catches vendor errors → `ENS-7005`.
99
+ */
100
+ async hasRole(role) {
101
+ try {
102
+ return await config.hasRole(role);
103
+ } catch (error) {
104
+ throw adapterAuthError(adapterName, "hasRole", error);
105
+ }
106
+ },
107
+ /**
108
+ * Wrapped `onAuthChange()` — catches subscription setup errors → `ENS-7002`.
109
+ * Uses generic method error (not auth-specific) because `onAuthChange` is
110
+ * subscription management, not auth state retrieval.
111
+ */
112
+ onAuthChange(callback) {
113
+ try {
114
+ return config.onAuthChange(callback);
115
+ } catch (error) {
116
+ throw adapterMethodError(adapterName, "onAuthChange", error);
117
+ }
118
+ }
119
+ };
120
+ return Object.freeze(adapter);
121
+ }
122
+ function createNoopAuthAdapter() {
123
+ const adapter = {
124
+ /** Returns `null` — no active session in noop mode. */
125
+ getSession() {
126
+ return Promise.resolve(null);
127
+ },
128
+ /** Returns `false` — no permissions in noop mode. */
129
+ hasRole(_role) {
130
+ return Promise.resolve(false);
131
+ },
132
+ /** Returns a no-op unsubscribe function — never fires a callback. */
133
+ onAuthChange(_callback) {
134
+ return () => {
135
+ };
136
+ }
137
+ };
138
+ return Object.freeze(adapter);
139
+ }
140
+
141
+ // src/create-data-adapter.ts
142
+ function createDataAdapter(config) {
143
+ validateAdapterConfig("data", config);
144
+ const adapterName = config.name;
145
+ const adapter = {
146
+ /**
147
+ * Wrapped `query()` — catches vendor errors → `ENS-7003`.
148
+ * Includes the queried resource name in the error for debugging.
149
+ */
150
+ async query(resource, params) {
151
+ try {
152
+ return await config.query(resource, params);
153
+ } catch (error) {
154
+ throw adapterQueryError(adapterName, resource, error);
155
+ }
156
+ },
157
+ /**
158
+ * Wrapped `mutate()` — catches vendor errors → `ENS-7004`.
159
+ * Includes the resource name and mutation action in the error for debugging.
160
+ */
161
+ async mutate(resource, action, data) {
162
+ try {
163
+ return await config.mutate(resource, action, data);
164
+ } catch (error) {
165
+ throw adapterMutationError(adapterName, resource, action, error);
166
+ }
167
+ },
168
+ /**
169
+ * Wrapped `subscribe()` — catches vendor errors → `ENS-7002`.
170
+ *
171
+ * Only the `subscribe()` invocation itself is wrapped. The consumer's
172
+ * callback is NOT wrapped — callback errors are the consumer's responsibility.
173
+ * The returned unsubscribe function is also NOT wrapped — unsubscribe
174
+ * failures are fire-and-forget cleanup operations.
175
+ */
176
+ subscribe(resource, callback) {
177
+ try {
178
+ return config.subscribe(resource, callback);
179
+ } catch (error) {
180
+ throw adapterMethodError(adapterName, "subscribe", error);
181
+ }
182
+ }
183
+ };
184
+ return Object.freeze(adapter);
185
+ }
186
+ function createNoopDataAdapter() {
187
+ const adapter = {
188
+ query(_resource, _params) {
189
+ return Promise.resolve([]);
190
+ },
191
+ mutate(_resource, _action, _data) {
192
+ return Promise.resolve(null);
193
+ },
194
+ subscribe(_resource, _callback) {
195
+ return () => {
196
+ };
197
+ }
198
+ };
199
+ return Object.freeze(adapter);
200
+ }
201
+
202
+ // src/create-error-adapter.ts
203
+ function createErrorAdapter(config) {
204
+ validateAdapterConfig("error", config);
205
+ const adapterName = config.name;
206
+ const adapter = {
207
+ /**
208
+ * Wrapped `report()` — catches vendor errors → `ENS-7002`.
209
+ * If the error reporting service itself fails, the caller must know.
210
+ */
211
+ async report(error, context) {
212
+ try {
213
+ await config.report(error, context);
214
+ } catch (reportError) {
215
+ throw adapterMethodError(adapterName, "report", reportError);
216
+ }
217
+ },
218
+ /**
219
+ * Wrapped `shouldRetry()` — catches vendor errors → `ENS-7002`.
220
+ * Async per AD2: production implementations may consult remote
221
+ * circuit breakers (LaunchDarkly, Unleash) before deciding.
222
+ */
223
+ async shouldRetry(error, attemptNumber) {
224
+ try {
225
+ return await config.shouldRetry(error, attemptNumber);
226
+ } catch (retryError) {
227
+ throw adapterMethodError(adapterName, "shouldRetry", retryError);
228
+ }
229
+ },
230
+ /**
231
+ * Wrapped `sanitize()` — catches vendor errors → `ENS-7002`.
232
+ * Async per AD2: production implementations may call external
233
+ * PII detection services (Google DLP, AWS Comprehend Medical).
234
+ */
235
+ async sanitize(error) {
236
+ try {
237
+ return await config.sanitize(error);
238
+ } catch (sanitizeError) {
239
+ throw adapterMethodError(adapterName, "sanitize", sanitizeError);
240
+ }
241
+ }
242
+ };
243
+ return Object.freeze(adapter);
244
+ }
245
+ function createNoopErrorAdapter() {
246
+ const adapter = {
247
+ /** No-op — errors silently consumed in noop mode. */
248
+ async report(_error, _context) {
249
+ },
250
+ /** Returns `false` — never retry in noop mode. */
251
+ shouldRetry(_error, _attemptNumber) {
252
+ return Promise.resolve(false);
253
+ },
254
+ /** Returns the original error unchanged — identity pass-through. */
255
+ sanitize(error) {
256
+ return Promise.resolve(error);
257
+ }
258
+ };
259
+ return Object.freeze(adapter);
260
+ }
261
+
262
+ // src/create-analytics-adapter.ts
263
+ function createAnalyticsAdapter(config) {
264
+ validateAdapterConfig("analytics", config);
265
+ const adapterName = config.name;
266
+ const adapter = {
267
+ /**
268
+ * Wrapped `track()` — catches vendor errors → `ENS-7002`.
269
+ * Fire-and-forget: consumers do not await this method.
270
+ */
271
+ track(event, properties) {
272
+ try {
273
+ config.track(event, properties);
274
+ } catch (error) {
275
+ throw adapterMethodError(adapterName, "track", error);
276
+ }
277
+ },
278
+ /**
279
+ * Wrapped `identify()` — catches vendor errors → `ENS-7002`.
280
+ * Fire-and-forget: consumers do not await this method.
281
+ */
282
+ identify(userId, traits) {
283
+ try {
284
+ config.identify(userId, traits);
285
+ } catch (error) {
286
+ throw adapterMethodError(adapterName, "identify", error);
287
+ }
288
+ }
289
+ };
290
+ return Object.freeze(adapter);
291
+ }
292
+ function createNoopAnalyticsAdapter() {
293
+ const adapter = {
294
+ track(_event, _properties) {
295
+ },
296
+ identify(_userId, _traits) {
297
+ }
298
+ };
299
+ return Object.freeze(adapter);
300
+ }
301
+
302
+ // src/version.ts
303
+ var ADAPTERS_VERSION = "0.0.0";
304
+
305
+ export { ADAPTERS_VERSION, adapterAuthError, adapterMethodError, adapterMutationError, adapterQueryError, adapterValidationError, createAnalyticsAdapter, createAuthAdapter, createDataAdapter, createErrorAdapter, createNoopAnalyticsAdapter, createNoopAuthAdapter, createNoopDataAdapter, createNoopErrorAdapter, validateAdapterConfig };
306
+ //# sourceMappingURL=index.js.map
307
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/validate-adapter.ts","../src/create-auth-adapter.ts","../src/create-data-adapter.ts","../src/create-error-adapter.ts","../src/create-analytics-adapter.ts","../src/version.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @enterstellar-ai/adapters/errors\n * @description Error factory functions for the adapters module.\n *\n * Every error is an `EnterstellarError` with:\n * - Machine-readable `code` (`ENS-7xxx`)\n * - Module identifier `'adapters'`\n * - `recoverable` flag per Enterstellar error taxonomy\n *\n * Error taxonomy:\n * - `ENS-7001` — ADAPTER_VALIDATION_FAILED: config missing required methods or invalid name\n * (non-recoverable, developer misconfiguration)\n * - `ENS-7002` — ADAPTER_METHOD_ERROR: adapter method threw during execution (recoverable)\n * - `ENS-7003` — ADAPTER_QUERY_ERROR: DataAdapter query() failed (recoverable)\n * - `ENS-7004` — ADAPTER_MUTATION_ERROR: DataAdapter mutate() failed (recoverable)\n * - `ENS-7005` — ADAPTER_AUTH_ERROR: AuthAdapter session/role check failed (recoverable)\n *\n * @see Coding Rules — Error Taxonomy\n * @see Design Choice AD5 — wrap into EnterstellarError\n * @see Design Choice C14 — error code ranges\n */\n\nimport { EnterstellarError } from '@enterstellar-ai/types';\n\nimport type { AdapterType } from './types.js';\n\n// ---------------------------------------------------------------------------\n// ENS-7001: Adapter Validation Failed\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an error for when an adapter config fails validation.\n *\n * This is a **non-recoverable** error — it indicates developer misconfiguration\n * (missing required methods, empty name, non-function fields). The consumer\n * must fix their adapter config before proceeding.\n *\n * @param adapterType - The adapter category that failed validation (e.g., `'auth'`, `'data'`).\n * @param reason - Human-readable explanation of what is invalid.\n * @returns An `EnterstellarError` with code `ENS-7001`.\n *\n * @example\n * ```ts\n * throw adapterValidationError('auth', 'Missing required method: getSession');\n * // EnterstellarError: Adapter validation failed for \"auth\": Missing required method: getSession\n * // code: 'ENS-7001', module: 'adapters', recoverable: false\n * ```\n */\nexport function adapterValidationError(\n adapterType: AdapterType,\n reason: string,\n): EnterstellarError {\n return new EnterstellarError(\n 'ENS-7001',\n 'adapters',\n `Adapter validation failed for \"${adapterType}\": ${reason}`,\n false, // non-recoverable — developer misconfiguration\n );\n}\n\n// ---------------------------------------------------------------------------\n// ENS-7002: Adapter Method Error\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an error for when an adapter method throws during execution.\n *\n * This is a **recoverable** error — the underlying infrastructure may have\n * experienced a transient failure. The operation can be retried. The original\n * error is preserved in `cause` for debugging (AD5).\n *\n * @param adapterName - The adapter instance name (e.g., `'supabase-auth'`).\n * @param methodName - The method that threw (e.g., `'getSession'`, `'track'`).\n * @param cause - The original error thrown by the adapter implementation.\n * @returns An `EnterstellarError` with code `ENS-7002`.\n *\n * @example\n * ```ts\n * throw adapterMethodError('supabase-auth', 'getSession', originalError);\n * // EnterstellarError: Adapter \"supabase-auth\" method \"getSession\" threw.\n * // code: 'ENS-7002', module: 'adapters', recoverable: true, cause: originalError\n * ```\n */\nexport function adapterMethodError(\n adapterName: string,\n methodName: string,\n cause?: unknown,\n): EnterstellarError {\n return new EnterstellarError(\n 'ENS-7002',\n 'adapters',\n `Adapter \"${adapterName}\" method \"${methodName}\" threw.`,\n true, // recoverable — transient infrastructure failure\n cause,\n );\n}\n\n// ---------------------------------------------------------------------------\n// ENS-7003: Adapter Query Error\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an error for when a {@link DataAdapter}'s `query()` method fails.\n *\n * This is a **recoverable** error — the data source may be temporarily\n * unavailable. Specialized variant of `ENS-7002` that includes the\n * queried resource name for debugging.\n *\n * @param adapterName - The adapter instance name (e.g., `'supabase-data'`).\n * @param resource - The resource that was being queried (e.g., `'patients.vitals'`).\n * @param cause - The original error thrown by the query implementation.\n * @returns An `EnterstellarError` with code `ENS-7003`.\n *\n * @example\n * ```ts\n * throw adapterQueryError('supabase-data', 'patients.vitals', pgError);\n * // EnterstellarError: Adapter \"supabase-data\" query failed for resource \"patients.vitals\".\n * // code: 'ENS-7003', module: 'adapters', recoverable: true, cause: pgError\n * ```\n */\nexport function adapterQueryError(\n adapterName: string,\n resource: string,\n cause?: unknown,\n): EnterstellarError {\n return new EnterstellarError(\n 'ENS-7003',\n 'adapters',\n `Adapter \"${adapterName}\" query failed for resource \"${resource}\".`,\n true, // recoverable — transient data source failure\n cause,\n );\n}\n\n// ---------------------------------------------------------------------------\n// ENS-7004: Adapter Mutation Error\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an error for when a {@link DataAdapter}'s `mutate()` method fails.\n *\n * This is a **recoverable** error — the data source may be temporarily\n * unavailable. Specialized variant of `ENS-7002` that includes the\n * resource name and mutation action for debugging.\n *\n * @param adapterName - The adapter instance name (e.g., `'supabase-data'`).\n * @param resource - The resource being mutated (e.g., `'patients'`).\n * @param action - The mutation action that failed (`'create'`, `'update'`, or `'delete'`).\n * @param cause - The original error thrown by the mutation implementation.\n * @returns An `EnterstellarError` with code `ENS-7004`.\n *\n * @example\n * ```ts\n * throw adapterMutationError('supabase-data', 'patients', 'update', pgError);\n * // EnterstellarError: Adapter \"supabase-data\" mutation \"update\" failed for resource \"patients\".\n * // code: 'ENS-7004', module: 'adapters', recoverable: true, cause: pgError\n * ```\n */\nexport function adapterMutationError(\n adapterName: string,\n resource: string,\n action: 'create' | 'update' | 'delete',\n cause?: unknown,\n): EnterstellarError {\n return new EnterstellarError(\n 'ENS-7004',\n 'adapters',\n `Adapter \"${adapterName}\" mutation \"${action}\" failed for resource \"${resource}\".`,\n true, // recoverable — transient data source failure\n cause,\n );\n}\n\n// ---------------------------------------------------------------------------\n// ENS-7005: Adapter Auth Error\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an error for when an {@link AuthAdapter}'s session or role check fails.\n *\n * This is a **recoverable** error — the auth provider may be temporarily\n * unavailable, or the session may have expired. Specialized variant of\n * `ENS-7002` for authentication operations.\n *\n * @param adapterName - The adapter instance name (e.g., `'clerk-auth'`).\n * @param operation - The auth operation that failed (e.g., `'getSession'`, `'hasRole'`).\n * @param cause - The original error thrown by the auth implementation.\n * @returns An `EnterstellarError` with code `ENS-7005`.\n *\n * @example\n * ```ts\n * throw adapterAuthError('clerk-auth', 'getSession', sessionExpiredError);\n * // EnterstellarError: Adapter \"clerk-auth\" auth operation \"getSession\" failed.\n * // code: 'ENS-7005', module: 'adapters', recoverable: true, cause: sessionExpiredError\n * ```\n */\nexport function adapterAuthError(\n adapterName: string,\n operation: string,\n cause?: unknown,\n): EnterstellarError {\n return new EnterstellarError(\n 'ENS-7005',\n 'adapters',\n `Adapter \"${adapterName}\" auth operation \"${operation}\" failed.`,\n true, // recoverable — auth provider may be temporarily unavailable\n cause,\n );\n}\n","/**\n * @module @enterstellar-ai/adapters/validate-adapter\n * @description Shared runtime validation for adapter configuration objects.\n *\n * Called by each `createXxxAdapter()` factory before wrapping the consumer's\n * implementation. Validates that:\n * - The config has a non-empty `name` string\n * - All required methods for the adapter type are present and are functions\n *\n * Throws `ENS-7001` (`adapterValidationError`) on any violation — this is a\n * non-recoverable developer error per Enterstellar error taxonomy.\n *\n * @see Coding Rules — Error Taxonomy (developer errors → fatal throw)\n * @see Design Choice AD1 — minimal but complete interfaces\n */\n\nimport { adapterValidationError } from './errors.js';\nimport type { AdapterType } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Required Method Maps\n// ---------------------------------------------------------------------------\n\n/**\n * Maps each adapter type to the set of method names that the config\n * object MUST provide as function values.\n *\n * @remarks\n * These lists must stay in sync with the corresponding config types\n * in `types.ts` and the adapter interfaces in `@enterstellar-ai/types/adapters`.\n */\nconst REQUIRED_METHODS: Readonly<Record<AdapterType, readonly string[]>> = {\n auth: ['getSession', 'hasRole', 'onAuthChange'],\n data: ['query', 'mutate', 'subscribe'],\n error: ['report', 'shouldRetry', 'sanitize'],\n analytics: ['track', 'identify'],\n};\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that an adapter config object has a valid name and all required methods.\n *\n * This function performs two checks:\n * 1. **Name check:** `config.name` must be a non-empty string.\n * 2. **Method check:** Every method listed in {@link REQUIRED_METHODS} for the\n * given `adapterType` must be present on the config and be `typeof === 'function'`.\n *\n * Throws `ENS-7001` on the first validation failure encountered.\n *\n * @param adapterType - The adapter category being validated (e.g., `'auth'`, `'data'`).\n * @param config - The raw config object to validate.\n * @throws `EnterstellarError` with code `ENS-7001` if validation fails.\n *\n * @example\n * ```ts\n * // Valid — passes silently\n * validateAdapterConfig('auth', {\n * name: 'supabase-auth',\n * getSession: async () => null,\n * hasRole: async () => false,\n * onAuthChange: (cb) => () => {},\n * });\n *\n * // Invalid — throws ENS-7001\n * validateAdapterConfig('auth', { name: '', getSession: null });\n * // EnterstellarError: Adapter validation failed for \"auth\": \"name\" must be a non-empty string.\n * ```\n */\nexport function validateAdapterConfig(\n adapterType: AdapterType,\n config: Readonly<Record<string, unknown>>,\n): void {\n // -----------------------------------------------------------------------\n // 1. Name validation\n // -----------------------------------------------------------------------\n const name = config['name'];\n\n if (typeof name !== 'string' || name.length === 0) {\n throw adapterValidationError(\n adapterType,\n '\"name\" must be a non-empty string.',\n );\n }\n\n // -----------------------------------------------------------------------\n // 2. Required method validation\n // -----------------------------------------------------------------------\n const requiredMethods = REQUIRED_METHODS[adapterType];\n\n for (const methodName of requiredMethods) {\n const method = config[methodName];\n\n if (typeof method !== 'function') {\n const receivedType = method === null ? 'null' : typeof method;\n throw adapterValidationError(\n adapterType,\n `Missing or invalid method \"${methodName}\". Expected function, received ${receivedType}.`,\n );\n }\n }\n}\n","/**\n * @module @enterstellar-ai/adapters/create-auth-adapter\n * @description Factory functions for creating validated `AuthAdapter` instances.\n *\n * - `createAuthAdapter(config)` — wraps a consumer-provided implementation,\n * validates config via {@link validateAdapterConfig}, and wraps every method\n * in error handling per Design Choice AD5 (raw vendor errors never leak).\n *\n * - `createNoopAuthAdapter()` — returns a no-op adapter for testing and\n * development when no real auth provider is connected.\n *\n * Both factories return a plain object with closures (R1 pattern — no classes).\n *\n * @see Bible §4.15\n * @see Design Choice AD1 — minimal but complete: getSession, hasRole, onAuthChange\n * @see Design Choice AD2 — always async (I/O methods)\n * @see Design Choice AD5 — wrap into EnterstellarError\n */\n\nimport type { AuthAdapter } from '@enterstellar-ai/types';\n\nimport { adapterAuthError, adapterMethodError } from './errors.js';\nimport type { AuthAdapterConfig } from './types.js';\nimport { validateAdapterConfig } from './validate-adapter.js';\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a validated `AuthAdapter` from consumer-provided config.\n *\n * The factory:\n * 1. Validates the config (name + required methods) — throws `ENS-7001` on failure.\n * 2. Wraps `getSession()` and `hasRole()` in error handling → `ENS-7005` on throw.\n * 3. Wraps `onAuthChange()` in error handling → `ENS-7002` on throw.\n *\n * Consumers never see raw vendor errors — all failures are `EnterstellarError` (AD5).\n *\n * @param config - The adapter implementation with a `name` and all required methods.\n * @returns A frozen `AuthAdapter` instance.\n * @throws `EnterstellarError` with code `ENS-7001` if config validation fails.\n *\n * @example\n * ```ts\n * import { createAuthAdapter } from '@enterstellar-ai/adapters';\n *\n * const auth = createAuthAdapter({\n * name: 'supabase-auth',\n * getSession: async () => {\n * const { data } = await supabase.auth.getSession();\n * if (!data.session) return null;\n * return { userId: data.session.user.id, roles: ['clinician'] };\n * },\n * hasRole: async (role) => {\n * const session = await supabase.auth.getSession();\n * return session.data.session?.user.role === role;\n * },\n * onAuthChange: (cb) => {\n * const { data } = supabase.auth.onAuthStateChange((_event, session) => {\n * cb(session ? { userId: session.user.id, roles: ['clinician'] } : null);\n * });\n * return () => data.subscription.unsubscribe();\n * },\n * });\n * ```\n */\nexport function createAuthAdapter(config: AuthAdapterConfig): AuthAdapter {\n // -----------------------------------------------------------------------\n // Step 1: Validate config — throws ENS-7001 on failure\n // -----------------------------------------------------------------------\n validateAdapterConfig('auth', config);\n\n const adapterName = config.name;\n\n // -----------------------------------------------------------------------\n // Step 2: Build wrapped adapter (plain object with closures — R1 pattern)\n // -----------------------------------------------------------------------\n const adapter: AuthAdapter = {\n /**\n * Wrapped `getSession()` — catches vendor errors → `ENS-7005`.\n */\n async getSession(): Promise<{ userId: string; roles: string[] } | null> {\n try {\n return await config.getSession();\n } catch (error: unknown) {\n throw adapterAuthError(adapterName, 'getSession', error);\n }\n },\n\n /**\n * Wrapped `hasRole()` — catches vendor errors → `ENS-7005`.\n */\n async hasRole(role: string): Promise<boolean> {\n try {\n return await config.hasRole(role);\n } catch (error: unknown) {\n throw adapterAuthError(adapterName, 'hasRole', error);\n }\n },\n\n /**\n * Wrapped `onAuthChange()` — catches subscription setup errors → `ENS-7002`.\n * Uses generic method error (not auth-specific) because `onAuthChange` is\n * subscription management, not auth state retrieval.\n */\n onAuthChange(\n callback: (session: { userId: string; roles: string[] } | null) => void,\n ): () => void {\n try {\n return config.onAuthChange(callback);\n } catch (error: unknown) {\n throw adapterMethodError(adapterName, 'onAuthChange', error);\n }\n },\n };\n\n // -----------------------------------------------------------------------\n // Step 3: Freeze and return — prevents accidental mutation (R4 pattern)\n // -----------------------------------------------------------------------\n return Object.freeze(adapter);\n}\n\n// ---------------------------------------------------------------------------\n// No-Op Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a no-op `AuthAdapter` for testing and development.\n *\n * All methods resolve to safe defaults:\n * - `getSession()` → `null` (unauthenticated)\n * - `hasRole()` → `false` (no permissions)\n * - `onAuthChange()` → no-op unsubscribe function (never fires)\n *\n * @returns A frozen, no-op `AuthAdapter` instance.\n *\n * @example\n * ```ts\n * import { createNoopAuthAdapter } from '@enterstellar-ai/adapters';\n *\n * const auth = createNoopAuthAdapter();\n * await auth.getSession(); // null\n * await auth.hasRole('admin'); // false\n * const unsub = auth.onAuthChange(() => {}); // never called\n * unsub(); // no-op\n * ```\n */\nexport function createNoopAuthAdapter(): AuthAdapter {\n const adapter: AuthAdapter = {\n /** Returns `null` — no active session in noop mode. */\n getSession(): Promise<{ userId: string; roles: string[] } | null> {\n return Promise.resolve(null);\n },\n\n /** Returns `false` — no permissions in noop mode. */\n hasRole(_role: string): Promise<boolean> {\n return Promise.resolve(false);\n },\n\n /** Returns a no-op unsubscribe function — never fires a callback. */\n onAuthChange(\n _callback: (session: { userId: string; roles: string[] } | null) => void,\n ): () => void {\n // No-op — no auth state changes to subscribe to.\n return () => { /* noop unsubscribe */ };\n },\n };\n\n return Object.freeze(adapter);\n}\n","/**\n * @module @enterstellar-ai/adapters/create-data-adapter\n * @description Factory functions for creating validated `DataAdapter` instances.\n *\n * - `createDataAdapter(config)` — wraps a consumer-provided implementation,\n * validates config via {@link validateAdapterConfig}, and wraps every method\n * in error handling per Design Choice AD5 (raw vendor errors never leak).\n *\n * - `createNoopDataAdapter()` — returns a no-op adapter for testing and\n * development when no real data source is connected.\n *\n * Both factories return a plain object with closures (R1 pattern — no classes).\n *\n * @see Bible §4.15\n * @see Design Choice AD1 — minimal but complete: query, mutate, subscribe\n * @see Design Choice AD2 — always async (except subscribe's sync unsubscribe return)\n * @see Design Choice AD3 — convention-based dot-notation resolver\n * @see Design Choice AD5 — wrap into EnterstellarError\n */\n\nimport type { DataAdapter } from '@enterstellar-ai/types';\n\nimport { adapterMethodError, adapterMutationError, adapterQueryError } from './errors.js';\nimport type { DataAdapterConfig } from './types.js';\nimport { validateAdapterConfig } from './validate-adapter.js';\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a validated `DataAdapter` from consumer-provided config.\n *\n * The factory:\n * 1. Validates the config (name + required methods) — throws `ENS-7001` on failure.\n * 2. Wraps `query()` in error handling → `ENS-7003` on throw (includes resource name).\n * 3. Wraps `mutate()` in error handling → `ENS-7004` on throw (includes resource + action).\n * 4. Wraps `subscribe()` in error handling → `ENS-7002` on throw (generic method error).\n *\n * Consumers never see raw vendor errors — all failures are `EnterstellarError` (AD5).\n *\n * @param config - The adapter implementation with a `name` and all required methods.\n * @returns A frozen `DataAdapter` instance.\n * @throws `EnterstellarError` with code `ENS-7001` if config validation fails.\n *\n * @example\n * ```ts\n * import { createDataAdapter } from '@enterstellar-ai/adapters';\n *\n * const data = createDataAdapter({\n * name: 'supabase-data',\n * query: async (resource, params) => {\n * const { data } = await supabase.from(resource).select('*').match(params ?? {});\n * return data ?? [];\n * },\n * mutate: async (resource, action, payload) => {\n * if (action === 'create') {\n * const { data } = await supabase.from(resource).insert(payload).select().single();\n * return data;\n * }\n * return null;\n * },\n * subscribe: (resource, callback) => {\n * const channel = supabase.channel(resource)\n * .on('postgres_changes', { event: '*', schema: 'public', table: resource },\n * () => { data.query(resource).then(callback); })\n * .subscribe();\n * return () => { void supabase.removeChannel(channel); };\n * },\n * });\n * ```\n */\nexport function createDataAdapter(config: DataAdapterConfig): DataAdapter {\n // -----------------------------------------------------------------------\n // Step 1: Validate config — throws ENS-7001 on failure\n // -----------------------------------------------------------------------\n validateAdapterConfig('data', config);\n\n const adapterName = config.name;\n\n // -----------------------------------------------------------------------\n // Step 2: Build wrapped adapter (plain object with closures — R1 pattern)\n // -----------------------------------------------------------------------\n const adapter: DataAdapter = {\n /**\n * Wrapped `query()` — catches vendor errors → `ENS-7003`.\n * Includes the queried resource name in the error for debugging.\n */\n async query(\n resource: string,\n params?: Readonly<Record<string, unknown>>,\n ): Promise<readonly Record<string, unknown>[]> {\n try {\n return await config.query(resource, params);\n } catch (error: unknown) {\n throw adapterQueryError(adapterName, resource, error);\n }\n },\n\n /**\n * Wrapped `mutate()` — catches vendor errors → `ENS-7004`.\n * Includes the resource name and mutation action in the error for debugging.\n */\n async mutate(\n resource: string,\n action: 'create' | 'update' | 'delete',\n data: Readonly<Record<string, unknown>>,\n ): Promise<Record<string, unknown> | null> {\n try {\n return await config.mutate(resource, action, data);\n } catch (error: unknown) {\n throw adapterMutationError(adapterName, resource, action, error);\n }\n },\n\n /**\n * Wrapped `subscribe()` — catches vendor errors → `ENS-7002`.\n *\n * Only the `subscribe()` invocation itself is wrapped. The consumer's\n * callback is NOT wrapped — callback errors are the consumer's responsibility.\n * The returned unsubscribe function is also NOT wrapped — unsubscribe\n * failures are fire-and-forget cleanup operations.\n */\n subscribe(\n resource: string,\n callback: (data: readonly Record<string, unknown>[]) => void,\n ): () => void {\n try {\n return config.subscribe(resource, callback);\n } catch (error: unknown) {\n throw adapterMethodError(adapterName, 'subscribe', error);\n }\n },\n };\n\n // -----------------------------------------------------------------------\n // Step 3: Freeze and return — prevents accidental mutation (R4 pattern)\n // -----------------------------------------------------------------------\n return Object.freeze(adapter);\n}\n\n// ---------------------------------------------------------------------------\n// No-Op Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a no-op `DataAdapter` for testing and development.\n *\n * All methods resolve to safe defaults:\n * - `query()` → `[]` (empty result set)\n * - `mutate()` → `null` (no record returned)\n * - `subscribe()` → no-op unsubscribe function\n *\n * @returns A frozen, no-op `DataAdapter` instance.\n *\n * @example\n * ```ts\n * import { createNoopDataAdapter } from '@enterstellar-ai/adapters';\n *\n * const data = createNoopDataAdapter();\n * await data.query('patients.vitals'); // []\n * await data.mutate('patients', 'create', { name: 'Test' }); // null\n * const unsub = data.subscribe('patients', () => {}); // noop unsub\n * unsub(); // no-op\n * ```\n */\nexport function createNoopDataAdapter(): DataAdapter {\n const adapter: DataAdapter = {\n query(\n _resource: string,\n _params?: Readonly<Record<string, unknown>>,\n ): Promise<readonly Record<string, unknown>[]> {\n return Promise.resolve([]);\n },\n\n mutate(\n _resource: string,\n _action: 'create' | 'update' | 'delete',\n _data: Readonly<Record<string, unknown>>,\n ): Promise<Record<string, unknown> | null> {\n return Promise.resolve(null);\n },\n\n subscribe(\n _resource: string,\n _callback: (data: readonly Record<string, unknown>[]) => void,\n ): () => void {\n // Return a no-op unsubscribe function.\n return () => {\n // No-op — no subscription to clean up.\n };\n },\n };\n\n return Object.freeze(adapter);\n}\n","/**\n * @module @enterstellar-ai/adapters/create-error-adapter\n * @description Factory functions for creating validated `ErrorAdapter` instances.\n *\n * - `createErrorAdapter(config)` — wraps a consumer-provided implementation,\n * validates config via {@link validateAdapterConfig}, and wraps every method\n * in error handling per Design Choice AD5 (raw vendor errors never leak).\n *\n * - `createNoopErrorAdapter()` — returns a no-op adapter for testing and\n * development when no real error tracking service is connected.\n *\n * Both factories return a plain object with closures (R1 pattern — no classes).\n *\n * @see Bible §4.15\n * @see Design Choice AD2 — all methods async (I/O + future-proofing)\n * @see Design Choice AD5 — wrap into EnterstellarError\n */\n\nimport type { ErrorAdapter } from '@enterstellar-ai/types';\n\nimport { adapterMethodError } from './errors.js';\nimport type { ErrorAdapterConfig } from './types.js';\nimport { validateAdapterConfig } from './validate-adapter.js';\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a validated `ErrorAdapter` from consumer-provided config.\n *\n * The factory:\n * 1. Validates the config (name + required methods) — throws `ENS-7001` on failure.\n * 2. Wraps `report()` in async error handling → `ENS-7002` on throw.\n * 3. Wraps `shouldRetry()` in async error handling → `ENS-7002` on throw.\n * 4. Wraps `sanitize()` in async error handling → `ENS-7002` on throw.\n *\n * All three methods are async per AD2 — even `shouldRetry` and `sanitize`\n * which may seem synchronous in simple implementations, but production\n * adapters may require remote circuit breaker checks or external PII\n * detection services.\n *\n * Consumers never see raw vendor errors — all failures are `EnterstellarError` (AD5).\n *\n * @param config - The adapter implementation with a `name` and all required methods.\n * @returns A frozen `ErrorAdapter` instance.\n * @throws `EnterstellarError` with code `ENS-7001` if config validation fails.\n *\n * @example\n * ```ts\n * import { createErrorAdapter } from '@enterstellar-ai/adapters';\n *\n * const errors = createErrorAdapter({\n * name: 'sentry-error',\n * report: async (error, context) => {\n * Sentry.captureException(error, { extra: context });\n * },\n * shouldRetry: async (error, attempt) => attempt < 3 && isTransient(error),\n * sanitize: async (error) => {\n * const sanitized = new Error(error.message.replace(/SSN-\\d+/g, '[REDACTED]'));\n * sanitized.stack = error.stack;\n * return sanitized;\n * },\n * });\n * ```\n */\nexport function createErrorAdapter(config: ErrorAdapterConfig): ErrorAdapter {\n // -----------------------------------------------------------------------\n // Step 1: Validate config — throws ENS-7001 on failure\n // -----------------------------------------------------------------------\n validateAdapterConfig('error', config);\n\n const adapterName = config.name;\n\n // -----------------------------------------------------------------------\n // Step 2: Build wrapped adapter (plain object with closures — R1 pattern)\n // -----------------------------------------------------------------------\n const adapter: ErrorAdapter = {\n /**\n * Wrapped `report()` — catches vendor errors → `ENS-7002`.\n * If the error reporting service itself fails, the caller must know.\n */\n async report(\n error: Error,\n context?: Readonly<Record<string, unknown>>,\n ): Promise<void> {\n try {\n await config.report(error, context);\n } catch (reportError: unknown) {\n throw adapterMethodError(adapterName, 'report', reportError);\n }\n },\n\n /**\n * Wrapped `shouldRetry()` — catches vendor errors → `ENS-7002`.\n * Async per AD2: production implementations may consult remote\n * circuit breakers (LaunchDarkly, Unleash) before deciding.\n */\n async shouldRetry(error: Error, attemptNumber: number): Promise<boolean> {\n try {\n return await config.shouldRetry(error, attemptNumber);\n } catch (retryError: unknown) {\n throw adapterMethodError(adapterName, 'shouldRetry', retryError);\n }\n },\n\n /**\n * Wrapped `sanitize()` — catches vendor errors → `ENS-7002`.\n * Async per AD2: production implementations may call external\n * PII detection services (Google DLP, AWS Comprehend Medical).\n */\n async sanitize(error: Error): Promise<Error> {\n try {\n return await config.sanitize(error);\n } catch (sanitizeError: unknown) {\n throw adapterMethodError(adapterName, 'sanitize', sanitizeError);\n }\n },\n };\n\n // -----------------------------------------------------------------------\n // Step 3: Freeze and return — prevents accidental mutation (R4 pattern)\n // -----------------------------------------------------------------------\n return Object.freeze(adapter);\n}\n\n// ---------------------------------------------------------------------------\n// No-Op Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a no-op `ErrorAdapter` for testing and development.\n *\n * All methods resolve to safe defaults:\n * - `report()` → void (errors silently consumed)\n * - `shouldRetry()` → `false` (never retry)\n * - `sanitize()` → returns error as-is (no transformation)\n *\n * @returns A frozen, no-op `ErrorAdapter` instance.\n *\n * @example\n * ```ts\n * import { createNoopErrorAdapter } from '@enterstellar-ai/adapters';\n *\n * const errors = createNoopErrorAdapter();\n * await errors.report(new Error('test')); // no-op\n * await errors.shouldRetry(new Error('test'), 1); // false\n * await errors.sanitize(new Error('test')); // returns same error\n * ```\n */\nexport function createNoopErrorAdapter(): ErrorAdapter {\n const adapter: ErrorAdapter = {\n /** No-op — errors silently consumed in noop mode. */\n async report(\n _error: Error,\n _context?: Readonly<Record<string, unknown>>,\n ): Promise<void> {\n // No-op — errors silently consumed in noop mode.\n },\n\n /** Returns `false` — never retry in noop mode. */\n shouldRetry(_error: Error, _attemptNumber: number): Promise<boolean> {\n return Promise.resolve(false);\n },\n\n /** Returns the original error unchanged — identity pass-through. */\n sanitize(error: Error): Promise<Error> {\n return Promise.resolve(error);\n },\n };\n\n return Object.freeze(adapter);\n}\n","/**\n * @module @enterstellar-ai/adapters/create-analytics-adapter\n * @description Factory functions for creating validated `AnalyticsAdapter` instances.\n *\n * - `createAnalyticsAdapter(config)` — wraps a consumer-provided implementation,\n * validates config via {@link validateAdapterConfig}, and wraps every method\n * in error handling per Design Choice AD5 (raw vendor errors never leak).\n *\n * - `createNoopAnalyticsAdapter()` — returns a no-op adapter for testing and\n * development when no real analytics service is connected.\n *\n * Both factories return a plain object with closures (R1 pattern — no classes).\n *\n * @see Bible §4.15\n * @see Design Choice AD1 — minimal but complete: track, identify\n * @see Design Choice AD5 — wrap into EnterstellarError\n */\n\nimport type { AnalyticsAdapter } from '@enterstellar-ai/types';\n\nimport { adapterMethodError } from './errors.js';\nimport type { AnalyticsAdapterConfig } from './types.js';\nimport { validateAdapterConfig } from './validate-adapter.js';\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a validated `AnalyticsAdapter` from consumer-provided config.\n *\n * The factory:\n * 1. Validates the config (name + required methods) — throws `ENS-7001` on failure.\n * 2. Wraps `track()` in sync error handling → `ENS-7002` on throw.\n * 3. Wraps `identify()` in sync error handling → `ENS-7002` on throw.\n *\n * Both `track()` and `identify()` are fire-and-forget (void return).\n * Consumers never see raw vendor errors — all failures are `EnterstellarError` (AD5).\n *\n * @param config - The adapter implementation with a `name` and all required methods.\n * @returns A frozen `AnalyticsAdapter` instance.\n * @throws `EnterstellarError` with code `ENS-7001` if config validation fails.\n *\n * @example\n * ```ts\n * import { createAnalyticsAdapter } from '@enterstellar-ai/adapters';\n *\n * const analytics = createAnalyticsAdapter({\n * name: 'mixpanel-analytics',\n * track: (event, properties) => {\n * mixpanel.track(event, properties);\n * },\n * identify: (userId, traits) => {\n * mixpanel.identify(userId);\n * if (traits) mixpanel.people.set(traits);\n * },\n * });\n * ```\n */\nexport function createAnalyticsAdapter(config: AnalyticsAdapterConfig): AnalyticsAdapter {\n // -----------------------------------------------------------------------\n // Step 1: Validate config — throws ENS-7001 on failure\n // -----------------------------------------------------------------------\n validateAdapterConfig('analytics', config);\n\n const adapterName = config.name;\n\n // -----------------------------------------------------------------------\n // Step 2: Build wrapped adapter (plain object with closures — R1 pattern)\n // -----------------------------------------------------------------------\n const adapter: AnalyticsAdapter = {\n /**\n * Wrapped `track()` — catches vendor errors → `ENS-7002`.\n * Fire-and-forget: consumers do not await this method.\n */\n track(\n event: string,\n properties?: Readonly<Record<string, unknown>>,\n ): void {\n try {\n config.track(event, properties);\n } catch (error: unknown) {\n throw adapterMethodError(adapterName, 'track', error);\n }\n },\n\n /**\n * Wrapped `identify()` — catches vendor errors → `ENS-7002`.\n * Fire-and-forget: consumers do not await this method.\n */\n identify(\n userId: string,\n traits?: Readonly<Record<string, unknown>>,\n ): void {\n try {\n config.identify(userId, traits);\n } catch (error: unknown) {\n throw adapterMethodError(adapterName, 'identify', error);\n }\n },\n };\n\n // -----------------------------------------------------------------------\n // Step 3: Freeze and return — prevents accidental mutation (R4 pattern)\n // -----------------------------------------------------------------------\n return Object.freeze(adapter);\n}\n\n// ---------------------------------------------------------------------------\n// No-Op Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a no-op `AnalyticsAdapter` for testing and development.\n *\n * All methods are silent no-ops:\n * - `track()` → void (events silently consumed)\n * - `identify()` → void (identity silently consumed)\n *\n * @returns A frozen, no-op `AnalyticsAdapter` instance.\n *\n * @example\n * ```ts\n * import { createNoopAnalyticsAdapter } from '@enterstellar-ai/adapters';\n *\n * const analytics = createNoopAnalyticsAdapter();\n * analytics.track('zone_rendered', { zone: 'main' }); // no-op\n * analytics.identify('user-123', { role: 'clinician' }); // no-op\n * ```\n */\nexport function createNoopAnalyticsAdapter(): AnalyticsAdapter {\n const adapter: AnalyticsAdapter = {\n track(\n _event: string,\n _properties?: Readonly<Record<string, unknown>>,\n ): void {\n // No-op — events silently consumed in noop mode.\n },\n\n identify(\n _userId: string,\n _traits?: Readonly<Record<string, unknown>>,\n ): void {\n // No-op — identity silently consumed in noop mode.\n },\n };\n\n return Object.freeze(adapter);\n}\n","/**\n * @module @enterstellar-ai/adapters/version\n * @description Package version constant for `@enterstellar-ai/adapters`.\n *\n * Used by DevTools for version display and runtime compatibility checks.\n * Must be kept in sync with the `version` field in `package.json`.\n *\n * @see Design Choice T14 — version exports\n */\n\n/**\n * Current version of the `@enterstellar-ai/adapters` package.\n *\n * @remarks\n * This value MUST match the `version` field in `package.json`.\n * Update this constant whenever a new version is released via Changesets.\n */\nexport const ADAPTERS_VERSION = '0.0.0' as const;\n"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@enterstellar-ai/adapters",
3
+ "description": "Infrastructure adapter interfaces — auth, data, error handling, and analytics contracts.",
4
+ "version": "0.1.0",
5
+ "author": "Enterstellar",
6
+ "license": "Apache-2.0",
7
+ "homepage": "https://enterstellar.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/enterstellar-ai/enterstellar.git",
11
+ "directory": "packages/adapters"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/enterstellar-ai/enterstellar/issues"
15
+ },
16
+ "keywords": [
17
+ "adapters",
18
+ "auth",
19
+ "data",
20
+ "infrastructure",
21
+ "generative-ui",
22
+ "enterstellar"
23
+ ],
24
+ "type": "module",
25
+ "private": false,
26
+ "sideEffects": false,
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "import": {
36
+ "types": "./dist/index.d.ts",
37
+ "default": "./dist/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/index.d.cts",
41
+ "default": "./dist/index.cjs"
42
+ }
43
+ }
44
+ },
45
+ "files": [
46
+ "LICENSE",
47
+ "NOTICE",
48
+ "dist"
49
+ ],
50
+ "peerDependencies": {
51
+ "@enterstellar-ai/types": "0.1.0"
52
+ },
53
+ "devDependencies": {
54
+ "@enterstellar-ai/types": "0.1.0"
55
+ },
56
+ "scripts": {
57
+ "build": "tsup",
58
+ "typecheck": "tsc --noEmit",
59
+ "lint": "eslint src/",
60
+ "test": "vitest run",
61
+ "test:watch": "vitest"
62
+ }
63
+ }