@procedures/store 0.0.0-stage → 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.
Files changed (115) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +65 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/errors.d.ts +16 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/errors.js +18 -0
  8. package/dist/errors.js.map +1 -0
  9. package/dist/index.d.ts +19 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +19 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/module.d.ts +15 -0
  14. package/dist/module.d.ts.map +1 -0
  15. package/dist/module.js +16 -0
  16. package/dist/module.js.map +1 -0
  17. package/dist/procedures.d.ts +13 -0
  18. package/dist/procedures.d.ts.map +1 -0
  19. package/dist/procedures.js +14 -0
  20. package/dist/procedures.js.map +1 -0
  21. package/dist/program-store/attach.d.ts +20 -0
  22. package/dist/program-store/attach.d.ts.map +1 -0
  23. package/dist/program-store/attach.js +36 -0
  24. package/dist/program-store/attach.js.map +1 -0
  25. package/dist/program-store/collection.d.ts +19 -0
  26. package/dist/program-store/collection.d.ts.map +1 -0
  27. package/dist/program-store/collection.js +34 -0
  28. package/dist/program-store/collection.js.map +1 -0
  29. package/dist/program-store/file.d.ts +29 -0
  30. package/dist/program-store/file.d.ts.map +1 -0
  31. package/dist/program-store/file.js +156 -0
  32. package/dist/program-store/file.js.map +1 -0
  33. package/dist/program-store/index.d.ts +11 -0
  34. package/dist/program-store/index.d.ts.map +1 -0
  35. package/dist/program-store/index.js +10 -0
  36. package/dist/program-store/index.js.map +1 -0
  37. package/dist/program-store/memory.d.ts +15 -0
  38. package/dist/program-store/memory.d.ts.map +1 -0
  39. package/dist/program-store/memory.js +25 -0
  40. package/dist/program-store/memory.js.map +1 -0
  41. package/dist/program-store/procedures.d.ts +23 -0
  42. package/dist/program-store/procedures.d.ts.map +1 -0
  43. package/dist/program-store/procedures.js +221 -0
  44. package/dist/program-store/procedures.js.map +1 -0
  45. package/dist/program-store/types.d.ts +50 -0
  46. package/dist/program-store/types.d.ts.map +1 -0
  47. package/dist/program-store/types.js +41 -0
  48. package/dist/program-store/types.js.map +1 -0
  49. package/dist/register.d.ts +8 -0
  50. package/dist/register.d.ts.map +1 -0
  51. package/dist/register.js +15 -0
  52. package/dist/register.js.map +1 -0
  53. package/dist/storage/adapter.d.ts +148 -0
  54. package/dist/storage/adapter.d.ts.map +1 -0
  55. package/dist/storage/adapter.js +214 -0
  56. package/dist/storage/adapter.js.map +1 -0
  57. package/dist/storage/api.d.ts +125 -0
  58. package/dist/storage/api.d.ts.map +1 -0
  59. package/dist/storage/api.js +220 -0
  60. package/dist/storage/api.js.map +1 -0
  61. package/dist/storage/factory.d.ts +134 -0
  62. package/dist/storage/factory.d.ts.map +1 -0
  63. package/dist/storage/factory.js +166 -0
  64. package/dist/storage/factory.js.map +1 -0
  65. package/dist/storage/hybrid.d.ts +193 -0
  66. package/dist/storage/hybrid.d.ts.map +1 -0
  67. package/dist/storage/hybrid.js +519 -0
  68. package/dist/storage/hybrid.js.map +1 -0
  69. package/dist/storage/index.d.ts +21 -0
  70. package/dist/storage/index.d.ts.map +1 -0
  71. package/dist/storage/index.js +20 -0
  72. package/dist/storage/index.js.map +1 -0
  73. package/dist/storage/procedures.d.ts +14 -0
  74. package/dist/storage/procedures.d.ts.map +1 -0
  75. package/dist/storage/procedures.js +98 -0
  76. package/dist/storage/procedures.js.map +1 -0
  77. package/dist/storage/serialization.d.ts +123 -0
  78. package/dist/storage/serialization.d.ts.map +1 -0
  79. package/dist/storage/serialization.js +216 -0
  80. package/dist/storage/serialization.js.map +1 -0
  81. package/dist/storage/synced-registry.d.ts +246 -0
  82. package/dist/storage/synced-registry.d.ts.map +1 -0
  83. package/dist/storage/synced-registry.js +575 -0
  84. package/dist/storage/synced-registry.js.map +1 -0
  85. package/dist/storage/types.d.ts +130 -0
  86. package/dist/storage/types.d.ts.map +1 -0
  87. package/dist/storage/types.js +8 -0
  88. package/dist/storage/types.js.map +1 -0
  89. package/dist/types.d.ts +7 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +7 -0
  92. package/dist/types.js.map +1 -0
  93. package/package.json +79 -3
  94. package/src/errors.ts +20 -0
  95. package/src/index.ts +19 -0
  96. package/src/module.ts +18 -0
  97. package/src/procedures.ts +17 -0
  98. package/src/program-store/attach.ts +46 -0
  99. package/src/program-store/collection.ts +39 -0
  100. package/src/program-store/file.ts +157 -0
  101. package/src/program-store/index.ts +17 -0
  102. package/src/program-store/memory.ts +30 -0
  103. package/src/program-store/procedures.ts +344 -0
  104. package/src/program-store/types.ts +78 -0
  105. package/src/register.ts +18 -0
  106. package/src/storage/adapter.ts +273 -0
  107. package/src/storage/api.ts +335 -0
  108. package/src/storage/factory.ts +268 -0
  109. package/src/storage/hybrid.ts +676 -0
  110. package/src/storage/index.ts +61 -0
  111. package/src/storage/procedures.ts +146 -0
  112. package/src/storage/serialization.ts +283 -0
  113. package/src/storage/synced-registry.ts +698 -0
  114. package/src/storage/types.ts +180 -0
  115. package/src/types.ts +7 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Procedure Storage Module
3
+ *
4
+ * Enables procedure registry to be backed by CollectionStorage.
5
+ * Provides persistence, sync, and remote registry capabilities.
6
+ */
7
+
8
+ // Types
9
+ export type {
10
+ SerializedProcedure,
11
+ HandlerReference,
12
+ HandlerLoader,
13
+ SyncDirection,
14
+ ConflictResolution,
15
+ WriteStrategy,
16
+ SyncConflict,
17
+ SyncResult,
18
+ SyncStatus,
19
+ SyncedRegistryOptions,
20
+ ProcedureStorageConfig,
21
+ } from "./types.js";
22
+
23
+ // Serialization
24
+ export {
25
+ serializeProcedure,
26
+ deserializeProcedure,
27
+ deserializeProcedureSync,
28
+ getProcedureKey,
29
+ getSerializedKey,
30
+ serializeProcedures,
31
+ deserializeProcedures,
32
+ createDynamicHandlerLoader,
33
+ } from "./serialization.js";
34
+ export type { SerializeOptions, DeserializeOptions } from "./serialization.js";
35
+
36
+ // The collection storages that need a Client (before: "@procedures/client/collections")
37
+ export { ApiStorage } from "./api.js";
38
+ export type { ApiStorageOptions } from "./api.js";
39
+ export { HybridStorage } from "./hybrid.js";
40
+ export type { HybridStorageOptions } from "./hybrid.js";
41
+
42
+ // Adapter
43
+ export { ProcedureStorageAdapter } from "./adapter.js";
44
+ export type { ProcedureStorageAdapterOptions } from "./adapter.js";
45
+
46
+ // Synced Registry
47
+ export { SyncedProcedureRegistry, getProcedureStore } from "./synced-registry.js";
48
+ export type { SyncedRegistrationOptions } from "./synced-registry.js";
49
+
50
+ // Factory
51
+ export {
52
+ createSyncedRegistry,
53
+ createMemorySyncedRegistry,
54
+ createApiSyncedRegistry,
55
+ createHybridSyncedRegistry,
56
+ createCustomSyncedRegistry,
57
+ } from "./factory.js";
58
+ export type { CreateSyncedRegistryConfig } from "./factory.js";
59
+
60
+ // procedure.register (the other store procedures are in ../program-store)
61
+ export { procedureRegisterProcedure } from "./procedures.js";
@@ -0,0 +1,146 @@
1
+ /**
2
+ * procedure.register: register a procedure at run time, from a handler reference.
3
+ *
4
+ * With `persist`, the procedure goes into the store of a synced registry (`createSyncedRegistry()`),
5
+ * and only with a handler that the store's loader allows: never a stub (deep dive CORE-16). Without
6
+ * a store, `persist` fails with the code NOT_CONFIGURED.
7
+ *
8
+ * The programs of `procedure.define` have their own store: `procedure.store`, `load` and `sync`
9
+ * (../program-store/procedures.ts, roadmap 3.2).
10
+ */
11
+
12
+ import { defineProcedure } from "@procedures/client";
13
+ import type { AnyProcedure, ProcedureContext, ProcedurePath } from "@procedures/client";
14
+ import { PROCEDURE_REGISTRY, type ProcedureRegistry } from "@procedures/client";
15
+ import { pathToKey } from "@procedures/client";
16
+ import type { HandlerReference, SerializedProcedure } from "./types.js";
17
+ import { deserializeProcedure, deserializeProcedureSync } from "./serialization.js";
18
+ import { getProcedureStore, type SyncedProcedureRegistry } from "./synced-registry.js";
19
+ import { ProcedureStoreError } from "../errors.js";
20
+
21
+ // =============================================================================
22
+ // Any Schema Helper
23
+ // =============================================================================
24
+
25
+ const anySchema: {
26
+ parse: (data: unknown) => unknown;
27
+ safeParse: (data: unknown) => { success: true; data: unknown };
28
+ _output: unknown;
29
+ } = {
30
+ parse: (data: unknown) => data,
31
+ safeParse: (data: unknown) => ({ success: true as const, data }),
32
+ _output: undefined as unknown,
33
+ };
34
+
35
+ // =============================================================================
36
+ // The store of the caller
37
+ // =============================================================================
38
+
39
+ export { ProcedureStoreError } from "../errors.js";
40
+
41
+ /** The registry of the caller: the context's registry when it has one, else the global one. */
42
+ function callerRegistry(ctx: ProcedureContext | undefined): ProcedureRegistry {
43
+ return (ctx as (ProcedureContext & { registry?: ProcedureRegistry }) | undefined)?.registry ?? PROCEDURE_REGISTRY;
44
+ }
45
+
46
+ /** The store of the caller's registry, or a NOT_CONFIGURED error. */
47
+ function requireStore(ctx: ProcedureContext | undefined, what: string): SyncedProcedureRegistry {
48
+ const store = getProcedureStore(callerRegistry(ctx));
49
+ if (!store) {
50
+ throw new ProcedureStoreError(
51
+ "NOT_CONFIGURED",
52
+ `${what} needs a procedure store, and this registry has none. ` +
53
+ "Wrap the registry with createSyncedRegistry() or new SyncedProcedureRegistry()."
54
+ );
55
+ }
56
+ return store;
57
+ }
58
+
59
+
60
+ // =============================================================================
61
+ // procedure.register - Register procedure at runtime
62
+ // =============================================================================
63
+
64
+ interface RegisterInput {
65
+ /** Procedure path */
66
+ path: ProcedurePath;
67
+ /** Procedure metadata */
68
+ metadata?: Record<string, unknown>;
69
+ /** Whether procedure supports streaming */
70
+ streaming?: boolean;
71
+ /** Handler reference for loading */
72
+ handlerRef?: HandlerReference;
73
+ /** Whether to persist to storage */
74
+ persist?: boolean;
75
+ }
76
+
77
+ interface RegisterOutput {
78
+ success: boolean;
79
+ message: string;
80
+ path: ProcedurePath;
81
+ /** False for a declaration: the procedure has no handler, and a Client sends its calls to the transport */
82
+ hasHandler: boolean;
83
+ }
84
+
85
+ export const procedureRegisterProcedure: AnyProcedure = defineProcedure({
86
+ path: ["procedure", "register"],
87
+ input: anySchema as any,
88
+ output: anySchema as any,
89
+ metadata: {
90
+ description: "Register a procedure at runtime",
91
+ tags: ["procedure", "registry", "storage"],
92
+ },
93
+ handler: async (input: RegisterInput, ctx: ProcedureContext): Promise<RegisterOutput> => {
94
+ const { path, metadata = {}, streaming, handlerRef, persist = false } = input;
95
+ const key = pathToKey(path);
96
+ const serialized: SerializedProcedure = {
97
+ path,
98
+ metadata,
99
+ streaming,
100
+ handlerRef,
101
+ storedAt: Date.now(),
102
+ };
103
+
104
+ if (persist) {
105
+ // A stored procedure needs a handler: never store a stub (deep dive CORE-16)
106
+ const store = requireStore(ctx, "procedure.register with persist");
107
+ const loader = store.getHandlerLoader();
108
+ const procedure = handlerRef && loader ? await deserializeProcedure(serialized, { handlerLoader: loader }) : undefined;
109
+ if (!procedure?.handler) {
110
+ throw new ProcedureStoreError(
111
+ "NO_HANDLER",
112
+ `procedure.register with persist needs a handler for ${key}: give a handlerRef ` +
113
+ "that the handler loader of the store allows."
114
+ );
115
+ }
116
+ store.register(procedure, { override: false, handlerRef });
117
+ await store.flushWrites();
118
+ return { success: true, message: `Registered and stored ${key}`, path, hasHandler: true };
119
+ }
120
+
121
+ // No persist: load the handler when the store's loader allows it, else declare a stub
122
+ const loader = getProcedureStore(callerRegistry(ctx))?.getHandlerLoader();
123
+ const procedure =
124
+ handlerRef && loader ? await deserializeProcedure(serialized, { handlerLoader: loader }) : deserializeProcedureSync(serialized);
125
+ try {
126
+ callerRegistry(ctx).register(procedure, { override: false });
127
+ } catch (error) {
128
+ return {
129
+ success: false,
130
+ message: error instanceof Error ? error.message : "Registration failed",
131
+ path,
132
+ hasHandler: false,
133
+ };
134
+ }
135
+
136
+ const hasHandler = typeof procedure.handler === "function";
137
+ return {
138
+ success: true,
139
+ message: hasHandler
140
+ ? `Registered ${key}`
141
+ : `Registered ${key} with no handler: a Client sends the calls of this path to its transport`,
142
+ path,
143
+ hasHandler,
144
+ };
145
+ },
146
+ });
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Procedure Serialization
3
+ *
4
+ * Utilities for converting procedures to/from serialized format.
5
+ * Enables storage and transmission of procedure definitions.
6
+ */
7
+
8
+ import type { AnyProcedure, ProcedurePath } from "@procedures/client";
9
+ import { pathToKey } from "@procedures/client";
10
+ import type { SerializedProcedure, HandlerReference, HandlerLoader } from "./types.js";
11
+
12
+ // =============================================================================
13
+ // Any Schema (for deserialized procedures without schemas)
14
+ // =============================================================================
15
+
16
+ /**
17
+ * Passthrough schema that accepts any value.
18
+ * Used for deserialized procedures where original schemas aren't available.
19
+ */
20
+ const anySchema: {
21
+ parse: (data: unknown) => unknown;
22
+ safeParse: (data: unknown) => { success: true; data: unknown };
23
+ _output: unknown;
24
+ } = {
25
+ parse: (data: unknown) => data,
26
+ safeParse: (data: unknown) => ({ success: true as const, data }),
27
+ _output: undefined as unknown,
28
+ };
29
+
30
+ // =============================================================================
31
+ // Serialize Procedure
32
+ // =============================================================================
33
+
34
+ /**
35
+ * Options for serializing a procedure.
36
+ */
37
+ export interface SerializeOptions {
38
+ /** Handler reference to store (optional, handlers can't be serialized directly) */
39
+ handlerRef?: HandlerReference | undefined;
40
+
41
+ /** Whether to include version info */
42
+ includeVersion?: boolean | undefined;
43
+ }
44
+
45
+ /**
46
+ * Serialize a procedure to storable format.
47
+ *
48
+ * Note: Handler functions cannot be serialized. If the procedure has a handler,
49
+ * you should provide a `handlerRef` that can be used to load it later.
50
+ *
51
+ * @param procedure - Procedure to serialize
52
+ * @param options - Serialization options
53
+ * @returns Serialized procedure definition
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const serialized = serializeProcedure(myProcedure, {
58
+ * handlerRef: {
59
+ * module: "@procedures/cli-run",
60
+ * export: "libScanHandler",
61
+ * },
62
+ * });
63
+ * ```
64
+ */
65
+ export function serializeProcedure(
66
+ procedure: AnyProcedure,
67
+ options: SerializeOptions = {}
68
+ ): SerializedProcedure {
69
+ const serialized: SerializedProcedure = {
70
+ path: procedure.path,
71
+ metadata: { ...procedure.metadata },
72
+ storedAt: Date.now(),
73
+ };
74
+
75
+ // Only include streaming if explicitly set
76
+ if (procedure.streaming !== undefined) {
77
+ serialized.streaming = procedure.streaming;
78
+ }
79
+
80
+ // Include handler reference if provided
81
+ if (options.handlerRef) {
82
+ serialized.handlerRef = options.handlerRef;
83
+ }
84
+
85
+ // Include version if requested
86
+ if (options.includeVersion) {
87
+ serialized.version = 1;
88
+ }
89
+
90
+ return serialized;
91
+ }
92
+
93
+ // =============================================================================
94
+ // Deserialize Procedure
95
+ // =============================================================================
96
+
97
+ /**
98
+ * Options for deserializing a procedure.
99
+ */
100
+ export interface DeserializeOptions {
101
+ /** Handler loader to restore handlers from references */
102
+ handlerLoader?: HandlerLoader | undefined;
103
+ }
104
+
105
+ /**
106
+ * Deserialize a procedure from stored format.
107
+ *
108
+ * Returns a stub procedure without a handler unless a handlerLoader is provided
109
+ * and the serialized procedure has a handlerRef.
110
+ *
111
+ * @param data - Serialized procedure data
112
+ * @param options - Deserialization options
113
+ * @returns Procedure (stub if no handler can be loaded)
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * // Deserialize as stub (no handler)
118
+ * const stub = await deserializeProcedure(data);
119
+ *
120
+ * // Deserialize with handler loading
121
+ * const proc = await deserializeProcedure(data, {
122
+ * handlerLoader: myLoader,
123
+ * });
124
+ * ```
125
+ */
126
+ export async function deserializeProcedure(
127
+ data: SerializedProcedure,
128
+ options: DeserializeOptions = {}
129
+ ): Promise<AnyProcedure> {
130
+ const procedure: AnyProcedure = {
131
+ path: data.path,
132
+ input: anySchema,
133
+ output: anySchema,
134
+ metadata: { ...data.metadata },
135
+ };
136
+
137
+ // Set streaming if present
138
+ if (data.streaming !== undefined) {
139
+ procedure.streaming = data.streaming;
140
+ }
141
+
142
+ // Try to load handler if reference exists and loader is provided
143
+ if (data.handlerRef && options.handlerLoader) {
144
+ try {
145
+ const handler = await options.handlerLoader.load(data.handlerRef);
146
+ if (handler && typeof handler === "function") {
147
+ procedure.handler = handler as AnyProcedure["handler"];
148
+ }
149
+ } catch {
150
+ // Handler loading failed, procedure remains a stub
151
+ }
152
+ }
153
+
154
+ return procedure;
155
+ }
156
+
157
+ /**
158
+ * Synchronous version that always returns a stub.
159
+ * Use when you don't need to load handlers.
160
+ */
161
+ export function deserializeProcedureSync(data: SerializedProcedure): AnyProcedure {
162
+ const procedure: AnyProcedure = {
163
+ path: data.path,
164
+ input: anySchema,
165
+ output: anySchema,
166
+ metadata: { ...data.metadata },
167
+ };
168
+
169
+ if (data.streaming !== undefined) {
170
+ procedure.streaming = data.streaming;
171
+ }
172
+
173
+ return procedure;
174
+ }
175
+
176
+ // =============================================================================
177
+ // Storage Key Utilities
178
+ // =============================================================================
179
+
180
+ /**
181
+ * Get the storage key (ID) for a procedure.
182
+ * Uses the dot-separated path as the key.
183
+ *
184
+ * @param pathOrProcedure - Procedure path or procedure object
185
+ * @returns Storage key string
186
+ */
187
+ export function getProcedureKey(pathOrProcedure: ProcedurePath | AnyProcedure): string {
188
+ const path = Array.isArray(pathOrProcedure) ? pathOrProcedure : pathOrProcedure.path;
189
+ return pathToKey(path);
190
+ }
191
+
192
+ /**
193
+ * Get the storage key from a serialized procedure.
194
+ */
195
+ export function getSerializedKey(data: SerializedProcedure): string {
196
+ return pathToKey(data.path);
197
+ }
198
+
199
+ // =============================================================================
200
+ // Batch Operations
201
+ // =============================================================================
202
+
203
+ /**
204
+ * Serialize multiple procedures.
205
+ *
206
+ * @param procedures - Procedures to serialize
207
+ * @param options - Serialization options
208
+ * @returns Array of [key, serialized] tuples for batch storage
209
+ */
210
+ export function serializeProcedures(
211
+ procedures: AnyProcedure[],
212
+ options: SerializeOptions = {}
213
+ ): Array<[string, SerializedProcedure]> {
214
+ return procedures.map((proc) => [
215
+ getProcedureKey(proc),
216
+ serializeProcedure(proc, options),
217
+ ]);
218
+ }
219
+
220
+ /**
221
+ * Deserialize multiple procedures.
222
+ *
223
+ * @param items - Array of serialized procedures
224
+ * @param options - Deserialization options
225
+ * @returns Array of procedures
226
+ */
227
+ export async function deserializeProcedures(
228
+ items: SerializedProcedure[],
229
+ options: DeserializeOptions = {}
230
+ ): Promise<AnyProcedure[]> {
231
+ return Promise.all(items.map((item) => deserializeProcedure(item, options)));
232
+ }
233
+
234
+ // =============================================================================
235
+ // Default Handler Loader
236
+ // =============================================================================
237
+
238
+ /**
239
+ * Create a handler loader using dynamic imports of the allowed modules only.
240
+ *
241
+ * A stored procedure record names the module of its handler, and the storage can be remote
242
+ * (`api`, `hybrid`). So the loader imports only the modules in `allowedModules` (exact package
243
+ * names, or a prefix that ends with "*"), and never a URL or a path (`data:`, `file:`, `node:`,
244
+ * `./x`, `/x`, a drive letter). Before, it imported any module that a record named: a record
245
+ * with `node:child_process` ran a shell command (BUGS-2026-07 M11, deep dive CORE-7, DATA-11).
246
+ *
247
+ * @param allowedModules - Module names the loader may import, such as "@procedures/git"
248
+ * @returns Handler loader that uses import() to load allowed modules
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * const loader = createDynamicHandlerLoader(["@procedures/*"]);
253
+ * const handler = await loader.load({
254
+ * module: "@procedures/cli-run",
255
+ * export: "libScanHandler",
256
+ * });
257
+ * ```
258
+ */
259
+ export function createDynamicHandlerLoader(allowedModules: readonly string[]): HandlerLoader {
260
+ const allowed = (specifier: string): boolean => {
261
+ // A URL scheme (data:, file:, node:, C:) or a relative or absolute path is never allowed
262
+ if (/^[a-z][a-z0-9+.-]*:/i.test(specifier) || /^[./\\]/.test(specifier)) {
263
+ return false;
264
+ }
265
+ return allowedModules.some((entry) =>
266
+ entry.endsWith("*") ? specifier.startsWith(entry.slice(0, -1)) : specifier === entry
267
+ );
268
+ };
269
+ return {
270
+ async load(ref: HandlerReference): Promise<unknown | undefined> {
271
+ if (!allowed(ref.module)) {
272
+ return undefined;
273
+ }
274
+ try {
275
+ // The specifier is checked against the allowlist above. A bundler cannot follow it.
276
+ const module = await import(/* @vite-ignore */ ref.module);
277
+ return module[ref.export];
278
+ } catch {
279
+ return undefined;
280
+ }
281
+ },
282
+ };
283
+ }