@almadar/runtime 1.0.10 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,319 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { OrbitalSchemaSchema } from '@almadar/core';
4
+
5
+ // src/loader/external-loader.ts
6
+ var ImportChain = class _ImportChain {
7
+ chain = [];
8
+ /**
9
+ * Try to add a path to the chain.
10
+ * @returns Error message if circular, null if OK
11
+ */
12
+ push(absolutePath) {
13
+ if (this.chain.includes(absolutePath)) {
14
+ const cycle = [...this.chain.slice(this.chain.indexOf(absolutePath)), absolutePath];
15
+ return `Circular import detected: ${cycle.join(" -> ")}`;
16
+ }
17
+ this.chain.push(absolutePath);
18
+ return null;
19
+ }
20
+ /**
21
+ * Remove the last path from the chain.
22
+ */
23
+ pop() {
24
+ this.chain.pop();
25
+ }
26
+ /**
27
+ * Clone the chain for nested loading.
28
+ */
29
+ clone() {
30
+ const newChain = new _ImportChain();
31
+ newChain.chain = [...this.chain];
32
+ return newChain;
33
+ }
34
+ };
35
+ var LoaderCache = class {
36
+ cache = /* @__PURE__ */ new Map();
37
+ get(absolutePath) {
38
+ return this.cache.get(absolutePath);
39
+ }
40
+ set(absolutePath, schema) {
41
+ this.cache.set(absolutePath, schema);
42
+ }
43
+ has(absolutePath) {
44
+ return this.cache.has(absolutePath);
45
+ }
46
+ clear() {
47
+ this.cache.clear();
48
+ }
49
+ get size() {
50
+ return this.cache.size;
51
+ }
52
+ };
53
+ var ExternalOrbitalLoader = class {
54
+ options;
55
+ cache;
56
+ constructor(options) {
57
+ this.options = {
58
+ stdLibPath: options.stdLibPath ?? "",
59
+ scopedPaths: options.scopedPaths ?? {},
60
+ allowOutsideBasePath: options.allowOutsideBasePath ?? false,
61
+ ...options
62
+ };
63
+ this.cache = new LoaderCache();
64
+ }
65
+ /**
66
+ * Load a schema from an import path.
67
+ *
68
+ * @param importPath - The import path (e.g., "./health.orb", "std/behaviors/game-core")
69
+ * @param fromPath - The path of the file doing the import (for relative resolution)
70
+ * @param chain - Import chain for circular detection
71
+ */
72
+ async load(importPath, fromPath, chain) {
73
+ const importChain = chain ?? new ImportChain();
74
+ const resolveResult = this.resolvePath(importPath, fromPath);
75
+ if (!resolveResult.success) {
76
+ return resolveResult;
77
+ }
78
+ const absolutePath = resolveResult.data;
79
+ const circularError = importChain.push(absolutePath);
80
+ if (circularError) {
81
+ return { success: false, error: circularError };
82
+ }
83
+ try {
84
+ const cached = this.cache.get(absolutePath);
85
+ if (cached) {
86
+ return { success: true, data: cached };
87
+ }
88
+ const loadResult = await this.loadFile(absolutePath);
89
+ if (!loadResult.success) {
90
+ return loadResult;
91
+ }
92
+ const loaded = {
93
+ schema: loadResult.data,
94
+ sourcePath: absolutePath,
95
+ importPath
96
+ };
97
+ this.cache.set(absolutePath, loaded);
98
+ return { success: true, data: loaded };
99
+ } finally {
100
+ importChain.pop();
101
+ }
102
+ }
103
+ /**
104
+ * Load a specific orbital from a schema by name.
105
+ *
106
+ * @param importPath - The import path
107
+ * @param orbitalName - The orbital name (optional, defaults to first orbital)
108
+ * @param fromPath - The path of the file doing the import
109
+ * @param chain - Import chain for circular detection
110
+ */
111
+ async loadOrbital(importPath, orbitalName, fromPath, chain) {
112
+ const schemaResult = await this.load(importPath, fromPath, chain);
113
+ if (!schemaResult.success) {
114
+ return schemaResult;
115
+ }
116
+ const schema = schemaResult.data.schema;
117
+ let orbital;
118
+ if (orbitalName) {
119
+ const found = schema.orbitals.find((o) => o.name === orbitalName);
120
+ if (!found) {
121
+ return {
122
+ success: false,
123
+ error: `Orbital "${orbitalName}" not found in ${importPath}. Available: ${schema.orbitals.map((o) => o.name).join(", ")}`
124
+ };
125
+ }
126
+ orbital = found;
127
+ } else {
128
+ if (schema.orbitals.length === 0) {
129
+ return {
130
+ success: false,
131
+ error: `No orbitals found in ${importPath}`
132
+ };
133
+ }
134
+ orbital = schema.orbitals[0];
135
+ }
136
+ return {
137
+ success: true,
138
+ data: {
139
+ orbital,
140
+ sourcePath: schemaResult.data.sourcePath,
141
+ importPath
142
+ }
143
+ };
144
+ }
145
+ /**
146
+ * Resolve an import path to an absolute filesystem path.
147
+ */
148
+ resolvePath(importPath, fromPath) {
149
+ if (importPath.startsWith("std/")) {
150
+ return this.resolveStdPath(importPath);
151
+ }
152
+ if (importPath.startsWith("@")) {
153
+ return this.resolveScopedPath(importPath);
154
+ }
155
+ if (importPath.startsWith("./") || importPath.startsWith("../")) {
156
+ return this.resolveRelativePath(importPath, fromPath);
157
+ }
158
+ if (path.isAbsolute(importPath)) {
159
+ if (!this.options.allowOutsideBasePath) {
160
+ return {
161
+ success: false,
162
+ error: `Absolute paths not allowed: ${importPath}`
163
+ };
164
+ }
165
+ return { success: true, data: importPath };
166
+ }
167
+ return this.resolveRelativePath(`./${importPath}`, fromPath);
168
+ }
169
+ /**
170
+ * Resolve a standard library path.
171
+ */
172
+ resolveStdPath(importPath) {
173
+ if (!this.options.stdLibPath) {
174
+ return {
175
+ success: false,
176
+ error: `Standard library path not configured. Cannot load: ${importPath}`
177
+ };
178
+ }
179
+ const relativePath = importPath.slice(4);
180
+ let absolutePath = path.join(this.options.stdLibPath, relativePath);
181
+ if (!absolutePath.endsWith(".orb")) {
182
+ absolutePath += ".orb";
183
+ }
184
+ const normalizedPath = path.normalize(absolutePath);
185
+ const normalizedStdLib = path.normalize(this.options.stdLibPath);
186
+ if (!normalizedPath.startsWith(normalizedStdLib)) {
187
+ return {
188
+ success: false,
189
+ error: `Path traversal outside std library: ${importPath}`
190
+ };
191
+ }
192
+ return { success: true, data: absolutePath };
193
+ }
194
+ /**
195
+ * Resolve a scoped package path.
196
+ */
197
+ resolveScopedPath(importPath) {
198
+ const match = importPath.match(/^(@[^/]+)/);
199
+ if (!match) {
200
+ return {
201
+ success: false,
202
+ error: `Invalid scoped package path: ${importPath}`
203
+ };
204
+ }
205
+ const scope = match[1];
206
+ const scopeRoot = this.options.scopedPaths[scope];
207
+ if (!scopeRoot) {
208
+ return {
209
+ success: false,
210
+ error: `Scoped package "${scope}" not configured. Available: ${Object.keys(this.options.scopedPaths).join(", ") || "none"}`
211
+ };
212
+ }
213
+ const relativePath = importPath.slice(scope.length + 1);
214
+ let absolutePath = path.join(scopeRoot, relativePath);
215
+ if (!absolutePath.endsWith(".orb")) {
216
+ absolutePath += ".orb";
217
+ }
218
+ const normalizedPath = path.normalize(absolutePath);
219
+ const normalizedRoot = path.normalize(scopeRoot);
220
+ if (!normalizedPath.startsWith(normalizedRoot)) {
221
+ return {
222
+ success: false,
223
+ error: `Path traversal outside scoped package: ${importPath}`
224
+ };
225
+ }
226
+ return { success: true, data: absolutePath };
227
+ }
228
+ /**
229
+ * Resolve a relative path.
230
+ */
231
+ resolveRelativePath(importPath, fromPath) {
232
+ const baseDir = fromPath ? path.dirname(fromPath) : this.options.basePath;
233
+ let absolutePath = path.resolve(baseDir, importPath);
234
+ if (!absolutePath.endsWith(".orb")) {
235
+ absolutePath += ".orb";
236
+ }
237
+ if (!this.options.allowOutsideBasePath) {
238
+ const normalizedPath = path.normalize(absolutePath);
239
+ const normalizedBase = path.normalize(this.options.basePath);
240
+ if (!normalizedPath.startsWith(normalizedBase)) {
241
+ return {
242
+ success: false,
243
+ error: `Path traversal outside base path: ${importPath}. Base: ${this.options.basePath}`
244
+ };
245
+ }
246
+ }
247
+ return { success: true, data: absolutePath };
248
+ }
249
+ /**
250
+ * Load and parse an .orb file.
251
+ */
252
+ async loadFile(absolutePath) {
253
+ try {
254
+ if (!fs.existsSync(absolutePath)) {
255
+ return {
256
+ success: false,
257
+ error: `File not found: ${absolutePath}`
258
+ };
259
+ }
260
+ const content = await fs.promises.readFile(absolutePath, "utf-8");
261
+ let data;
262
+ try {
263
+ data = JSON.parse(content);
264
+ } catch (e) {
265
+ return {
266
+ success: false,
267
+ error: `Invalid JSON in ${absolutePath}: ${e instanceof Error ? e.message : String(e)}`
268
+ };
269
+ }
270
+ const parseResult = OrbitalSchemaSchema.safeParse(data);
271
+ if (!parseResult.success) {
272
+ const errors = parseResult.error.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
273
+ return {
274
+ success: false,
275
+ error: `Invalid schema in ${absolutePath}:
276
+ ${errors}`
277
+ };
278
+ }
279
+ return { success: true, data: parseResult.data };
280
+ } catch (e) {
281
+ return {
282
+ success: false,
283
+ error: `Failed to load ${absolutePath}: ${e instanceof Error ? e.message : String(e)}`
284
+ };
285
+ }
286
+ }
287
+ /**
288
+ * Clear the cache.
289
+ */
290
+ clearCache() {
291
+ this.cache.clear();
292
+ }
293
+ /**
294
+ * Get cache statistics.
295
+ */
296
+ getCacheStats() {
297
+ return { size: this.cache.size };
298
+ }
299
+ };
300
+ function createLoader(basePath, options) {
301
+ return new ExternalOrbitalLoader({
302
+ basePath,
303
+ ...options
304
+ });
305
+ }
306
+ function parseImportPath(importPath) {
307
+ const hashIndex = importPath.indexOf("#");
308
+ if (hashIndex === -1) {
309
+ return { path: importPath };
310
+ }
311
+ return {
312
+ path: importPath.slice(0, hashIndex),
313
+ fragment: importPath.slice(hashIndex + 1)
314
+ };
315
+ }
316
+
317
+ export { ExternalOrbitalLoader, ImportChain, LoaderCache, createLoader, parseImportPath };
318
+ //# sourceMappingURL=external-loader-FJVQACFN.js.map
319
+ //# sourceMappingURL=external-loader-FJVQACFN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/loader/external-loader.ts"],"names":[],"mappings":"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","file":"external-loader-FJVQACFN.js","sourcesContent":["/**\n * External Orbital Loader\n *\n * Loads external .orb files from various sources:\n * - Local filesystem (relative paths)\n * - Standard library (std/...)\n * - Scoped packages (@name/...)\n *\n * @packageDocumentation\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { Orbital, OrbitalSchema } from \"@almadar/core\";\nimport { OrbitalSchemaSchema } from \"@almadar/core\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Result of loading an orbital.\n */\nexport interface LoadedOrbital {\n /** The loaded orbital */\n orbital: Orbital;\n\n /** Source path (resolved absolute path) */\n sourcePath: string;\n\n /** Original import path */\n importPath: string;\n}\n\n/**\n * Result of loading an orbital schema (may contain multiple orbitals).\n */\nexport interface LoadedSchema {\n /** The loaded schema */\n schema: OrbitalSchema;\n\n /** Source path (resolved absolute path) */\n sourcePath: string;\n\n /** Original import path */\n importPath: string;\n}\n\n/**\n * Loader options.\n */\nexport interface LoaderOptions {\n /** Base directory for resolving relative imports */\n basePath: string;\n\n /** Standard library root path */\n stdLibPath?: string;\n\n /** Scoped package roots (e.g., { \"@game-lib\": \"/path/to/lib\" }) */\n scopedPaths?: Record<string, string>;\n\n /** Whether to allow paths outside basePath (security) */\n allowOutsideBasePath?: boolean;\n}\n\n/**\n * Loader result with error handling.\n */\nexport type LoadResult<T> =\n | { success: true; data: T }\n | { success: false; error: string };\n\n// ============================================================================\n// Circular Import Detection\n// ============================================================================\n\n/**\n * Tracks import chains to detect circular imports.\n */\nexport class ImportChain {\n private chain: string[] = [];\n\n /**\n * Try to add a path to the chain.\n * @returns Error message if circular, null if OK\n */\n push(absolutePath: string): string | null {\n if (this.chain.includes(absolutePath)) {\n const cycle = [...this.chain.slice(this.chain.indexOf(absolutePath)), absolutePath];\n return `Circular import detected: ${cycle.join(\" -> \")}`;\n }\n this.chain.push(absolutePath);\n return null;\n }\n\n /**\n * Remove the last path from the chain.\n */\n pop(): void {\n this.chain.pop();\n }\n\n /**\n * Clone the chain for nested loading.\n */\n clone(): ImportChain {\n const newChain = new ImportChain();\n newChain.chain = [...this.chain];\n return newChain;\n }\n}\n\n// ============================================================================\n// Cache\n// ============================================================================\n\n/**\n * Cache for loaded schemas to avoid re-loading.\n */\nexport class LoaderCache {\n private cache = new Map<string, LoadedSchema>();\n\n get(absolutePath: string): LoadedSchema | undefined {\n return this.cache.get(absolutePath);\n }\n\n set(absolutePath: string, schema: LoadedSchema): void {\n this.cache.set(absolutePath, schema);\n }\n\n has(absolutePath: string): boolean {\n return this.cache.has(absolutePath);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n// ============================================================================\n// External Orbital Loader\n// ============================================================================\n\n/**\n * ExternalOrbitalLoader - Loads external .orb files with security and caching.\n *\n * Security Model:\n * - By default, only allows loading from within basePath\n * - std library has its own allowed path\n * - Scoped packages have their own allowed paths\n * - No path traversal outside allowed directories\n *\n * @example\n * ```typescript\n * const loader = new ExternalOrbitalLoader({\n * basePath: \"/project/schemas\",\n * stdLibPath: \"/project/std\",\n * });\n *\n * // Load relative .orb file\n * const result = await loader.load(\"./shared/health.orb\");\n *\n * // Load from std library\n * const stdResult = await loader.load(\"std/behaviors/game-core\");\n * ```\n */\nexport class ExternalOrbitalLoader {\n private options: Required<LoaderOptions>;\n private cache: LoaderCache;\n\n constructor(options: LoaderOptions) {\n this.options = {\n stdLibPath: options.stdLibPath ?? \"\",\n scopedPaths: options.scopedPaths ?? {},\n allowOutsideBasePath: options.allowOutsideBasePath ?? false,\n ...options,\n };\n this.cache = new LoaderCache();\n }\n\n /**\n * Load a schema from an import path.\n *\n * @param importPath - The import path (e.g., \"./health.orb\", \"std/behaviors/game-core\")\n * @param fromPath - The path of the file doing the import (for relative resolution)\n * @param chain - Import chain for circular detection\n */\n async load(\n importPath: string,\n fromPath?: string,\n chain?: ImportChain\n ): Promise<LoadResult<LoadedSchema>> {\n const importChain = chain ?? new ImportChain();\n\n // Resolve the absolute path\n const resolveResult = this.resolvePath(importPath, fromPath);\n if (!resolveResult.success) {\n return resolveResult;\n }\n const absolutePath = resolveResult.data;\n\n // Check for circular imports\n const circularError = importChain.push(absolutePath);\n if (circularError) {\n return { success: false, error: circularError };\n }\n\n try {\n // Check cache\n const cached = this.cache.get(absolutePath);\n if (cached) {\n return { success: true, data: cached };\n }\n\n // Load and parse the file\n const loadResult = await this.loadFile(absolutePath);\n if (!loadResult.success) {\n return loadResult;\n }\n\n const loaded: LoadedSchema = {\n schema: loadResult.data,\n sourcePath: absolutePath,\n importPath,\n };\n\n // Cache the result\n this.cache.set(absolutePath, loaded);\n\n return { success: true, data: loaded };\n } finally {\n importChain.pop();\n }\n }\n\n /**\n * Load a specific orbital from a schema by name.\n *\n * @param importPath - The import path\n * @param orbitalName - The orbital name (optional, defaults to first orbital)\n * @param fromPath - The path of the file doing the import\n * @param chain - Import chain for circular detection\n */\n async loadOrbital(\n importPath: string,\n orbitalName?: string,\n fromPath?: string,\n chain?: ImportChain\n ): Promise<LoadResult<LoadedOrbital>> {\n const schemaResult = await this.load(importPath, fromPath, chain);\n if (!schemaResult.success) {\n return schemaResult;\n }\n\n const schema = schemaResult.data.schema;\n let orbital: Orbital;\n\n if (orbitalName) {\n const found = schema.orbitals.find((o: Orbital) => o.name === orbitalName);\n if (!found) {\n return {\n success: false,\n error: `Orbital \"${orbitalName}\" not found in ${importPath}. Available: ${schema.orbitals.map((o: Orbital) => o.name).join(\", \")}`,\n };\n }\n orbital = found;\n } else {\n // Default to first orbital\n if (schema.orbitals.length === 0) {\n return {\n success: false,\n error: `No orbitals found in ${importPath}`,\n };\n }\n orbital = schema.orbitals[0];\n }\n\n return {\n success: true,\n data: {\n orbital,\n sourcePath: schemaResult.data.sourcePath,\n importPath,\n },\n };\n }\n\n /**\n * Resolve an import path to an absolute filesystem path.\n */\n resolvePath(importPath: string, fromPath?: string): LoadResult<string> {\n // Standard library\n if (importPath.startsWith(\"std/\")) {\n return this.resolveStdPath(importPath);\n }\n\n // Scoped packages\n if (importPath.startsWith(\"@\")) {\n return this.resolveScopedPath(importPath);\n }\n\n // Relative paths\n if (importPath.startsWith(\"./\") || importPath.startsWith(\"../\")) {\n return this.resolveRelativePath(importPath, fromPath);\n }\n\n // Absolute paths (only if allowed)\n if (path.isAbsolute(importPath)) {\n if (!this.options.allowOutsideBasePath) {\n return {\n success: false,\n error: `Absolute paths not allowed: ${importPath}`,\n };\n }\n return { success: true, data: importPath };\n }\n\n // Default: treat as relative to base path\n return this.resolveRelativePath(`./${importPath}`, fromPath);\n }\n\n /**\n * Resolve a standard library path.\n */\n private resolveStdPath(importPath: string): LoadResult<string> {\n if (!this.options.stdLibPath) {\n return {\n success: false,\n error: `Standard library path not configured. Cannot load: ${importPath}`,\n };\n }\n\n // std/behaviors/game-core -> stdLibPath/behaviors/game-core.orb\n const relativePath = importPath.slice(4); // Remove \"std/\"\n let absolutePath = path.join(this.options.stdLibPath, relativePath);\n\n // Add .orb extension if not present\n if (!absolutePath.endsWith(\".orb\")) {\n absolutePath += \".orb\";\n }\n\n // Validate it's within std library\n const normalizedPath = path.normalize(absolutePath);\n const normalizedStdLib = path.normalize(this.options.stdLibPath);\n if (!normalizedPath.startsWith(normalizedStdLib)) {\n return {\n success: false,\n error: `Path traversal outside std library: ${importPath}`,\n };\n }\n\n return { success: true, data: absolutePath };\n }\n\n /**\n * Resolve a scoped package path.\n */\n private resolveScopedPath(importPath: string): LoadResult<string> {\n // Extract scope: @game-lib/enemies.orb -> @game-lib\n const match = importPath.match(/^(@[^/]+)/);\n if (!match) {\n return {\n success: false,\n error: `Invalid scoped package path: ${importPath}`,\n };\n }\n\n const scope = match[1];\n const scopeRoot = this.options.scopedPaths[scope];\n if (!scopeRoot) {\n return {\n success: false,\n error: `Scoped package \"${scope}\" not configured. Available: ${Object.keys(this.options.scopedPaths).join(\", \") || \"none\"}`,\n };\n }\n\n // @game-lib/enemies.orb -> scopeRoot/enemies.orb\n const relativePath = importPath.slice(scope.length + 1); // Remove \"@scope/\"\n let absolutePath = path.join(scopeRoot, relativePath);\n\n // Add .orb extension if not present\n if (!absolutePath.endsWith(\".orb\")) {\n absolutePath += \".orb\";\n }\n\n // Validate it's within scope root\n const normalizedPath = path.normalize(absolutePath);\n const normalizedRoot = path.normalize(scopeRoot);\n if (!normalizedPath.startsWith(normalizedRoot)) {\n return {\n success: false,\n error: `Path traversal outside scoped package: ${importPath}`,\n };\n }\n\n return { success: true, data: absolutePath };\n }\n\n /**\n * Resolve a relative path.\n */\n private resolveRelativePath(\n importPath: string,\n fromPath?: string\n ): LoadResult<string> {\n const baseDir = fromPath\n ? path.dirname(fromPath)\n : this.options.basePath;\n\n let absolutePath = path.resolve(baseDir, importPath);\n\n // Add .orb extension if not present\n if (!absolutePath.endsWith(\".orb\")) {\n absolutePath += \".orb\";\n }\n\n // Security check: ensure within base path\n if (!this.options.allowOutsideBasePath) {\n const normalizedPath = path.normalize(absolutePath);\n const normalizedBase = path.normalize(this.options.basePath);\n if (!normalizedPath.startsWith(normalizedBase)) {\n return {\n success: false,\n error: `Path traversal outside base path: ${importPath}. Base: ${this.options.basePath}`,\n };\n }\n }\n\n return { success: true, data: absolutePath };\n }\n\n /**\n * Load and parse an .orb file.\n */\n private async loadFile(absolutePath: string): Promise<LoadResult<OrbitalSchema>> {\n try {\n // Check file exists\n if (!fs.existsSync(absolutePath)) {\n return {\n success: false,\n error: `File not found: ${absolutePath}`,\n };\n }\n\n // Read file\n const content = await fs.promises.readFile(absolutePath, \"utf-8\");\n\n // Parse JSON\n let data: unknown;\n try {\n data = JSON.parse(content);\n } catch (e) {\n return {\n success: false,\n error: `Invalid JSON in ${absolutePath}: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n\n // Validate with Zod\n const parseResult = OrbitalSchemaSchema.safeParse(data);\n if (!parseResult.success) {\n const errors = parseResult.error.errors\n .map((e) => ` - ${e.path.join(\".\")}: ${e.message}`)\n .join(\"\\n\");\n return {\n success: false,\n error: `Invalid schema in ${absolutePath}:\\n${errors}`,\n };\n }\n\n return { success: true, data: parseResult.data as OrbitalSchema };\n } catch (e) {\n return {\n success: false,\n error: `Failed to load ${absolutePath}: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n }\n\n /**\n * Clear the cache.\n */\n clearCache(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics.\n */\n getCacheStats(): { size: number } {\n return { size: this.cache.size };\n }\n}\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Create a loader with sensible defaults.\n */\nexport function createLoader(basePath: string, options?: Partial<LoaderOptions>): ExternalOrbitalLoader {\n return new ExternalOrbitalLoader({\n basePath,\n ...options,\n });\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Parse an import path with optional fragment.\n *\n * @example\n * parseImportPath(\"./health.orb\") // { path: \"./health.orb\", fragment: undefined }\n * parseImportPath(\"./game.orb#Player\") // { path: \"./game.orb\", fragment: \"Player\" }\n */\nexport function parseImportPath(importPath: string): { path: string; fragment?: string } {\n const hashIndex = importPath.indexOf(\"#\");\n if (hashIndex === -1) {\n return { path: importPath };\n }\n return {\n path: importPath.slice(0, hashIndex),\n fragment: importPath.slice(hashIndex + 1),\n };\n}\n"]}