@theholocron/datapad 5.0.0-alpha.6 → 5.0.0-alpha.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,6 +51,22 @@ Loads the first `<name>.config.<ext>` found in `cwd`. Probe order is
51
51
  file"; a file that exists but cannot be parsed / loaded / has no default
52
52
  export throws `ConfigFileError`.
53
53
 
54
+ ### `loadConfigFromContent<T>({ dir, content, name, extension })`
55
+
56
+ Loads config from content that didn't come from a file already on disk —
57
+ writes it to `<dir>/<name>.config.<extension>` first, then loads it through
58
+ the exact same internals `loadConfigFile` uses for a file it discovered
59
+ itself. `dir` matters: it's what upward `node_modules` resolution sees, so
60
+ content that does `import { defineConfig } from "@theholocron/cli"` only
61
+ resolves if `dir` sits under a tree where that package is a real
62
+ dependency. Callers own creating and cleaning up `dir`. Returns
63
+ `{ config, filepath }`; throws `ConfigFileError` the same way
64
+ `loadConfigFile` does on a parse/load failure.
65
+
66
+ Built for `@theholocron/sentinel`'s `validateConfig()` — it fetches a
67
+ repo's `holocron.config.*` from GitHub's API, not a local checkout, but
68
+ still needs the fetched content to execute as a real module.
69
+
54
70
  ### `loadLayered<T>({ cwd, name, fallback?, extensions? })`
55
71
 
56
72
  `<name>.config.*` layered over the `[fallback.key]` of
package/dist/index.d.mts CHANGED
@@ -31,8 +31,10 @@ declare class ConfigFileError extends Error {
31
31
  //#endregion
32
32
  //#region src/load.d.ts
33
33
  /**
34
- * Discover and load `<name>.config.*` files. Holocron-agnostic — no
35
- * schema, no validation, no defaults. Consumers layer those on top.
34
+ * Discover and load `<name>.config.*` files, or load one from content that
35
+ * didn't come from a file at all ({@link loadConfigFromContent}).
36
+ * Holocron-agnostic — no schema, no validation, no defaults. Consumers
37
+ * layer those on top.
36
38
  *
37
39
  * Probe order is **TS-first**: `.ts` → `.js` → `.mjs` → `.cjs` → `.json`.
38
40
  * TS is loaded through `tsx`'s `tsImport` (a runtime dependency) so a
@@ -84,6 +86,34 @@ interface LayeredResult<T> {
84
86
  * when neither source resolves.
85
87
  */
86
88
  declare function loadLayered<T>(opts: LoadLayeredOptions): Promise<LayeredResult<T> | null>;
89
+ interface LoadConfigFromContentOptions {
90
+ /**
91
+ * Directory to write `<name>.config.<extension>` into before loading —
92
+ * this is what upward `node_modules` resolution sees, so a config that
93
+ * does `import { defineConfig } from "@theholocron/cli"` only resolves
94
+ * if `dir` sits under a tree where that package is a real dependency.
95
+ * Callers own creating and cleaning up `dir` — this function only
96
+ * writes the one file into it.
97
+ */
98
+ dir: string;
99
+ /** Raw file content — not necessarily sourced from a local file at all (a network fetch, a git blob, …). */
100
+ content: string;
101
+ /** Base name — `"holocron"` writes `holocron.config.<extension>`. */
102
+ name: string;
103
+ /** Which of `DEFAULT_EXTENSIONS` `content` is — determines how it's interpreted (ts/js/mjs/cjs parsed as a module, json as `JSON.parse`). */
104
+ extension: string;
105
+ }
106
+ /**
107
+ * Load config from content that didn't come from a file already on disk —
108
+ * writes it to `<dir>/<name>.config.<extension>` first, then loads it
109
+ * through the exact same path {@link loadConfigFile} uses for a file it
110
+ * discovered itself. Exists because sourcing config content from somewhere
111
+ * other than the local filesystem (a GitHub API fetch, for one — see
112
+ * `@theholocron/sentinel`) doesn't change what loading it correctly means:
113
+ * a `.ts` config still needs real module resolution, `defineConfig` import
114
+ * included, not a re-implemented parser.
115
+ */
116
+ declare function loadConfigFromContent<T>(opts: LoadConfigFromContentOptions): Promise<Loaded<T>>;
87
117
  //#endregion
88
118
  //#region src/merge.d.ts
89
119
  /**
@@ -98,4 +128,4 @@ declare function loadLayered<T>(opts: LoadLayeredOptions): Promise<LayeredResult
98
128
  */
99
129
  declare function mergeConfig<T>(base: T, override: unknown): T;
100
130
  //#endregion
101
- export { ConfigFileError, DEFAULT_EXTENSIONS, type LayeredResult, type LoadConfigFileOptions, type LoadLayeredOptions, type Loaded, createDefineConfig, loadConfigFile, loadLayered, mergeConfig };
131
+ export { ConfigFileError, DEFAULT_EXTENSIONS, type LayeredResult, type LoadConfigFileOptions, type LoadConfigFromContentOptions, type LoadLayeredOptions, type Loaded, createDefineConfig, loadConfigFile, loadConfigFromContent, loadLayered, mergeConfig };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { readFile, stat } from "node:fs/promises";
1
+ import { readFile, stat, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  //#region src/define.ts
@@ -65,8 +65,10 @@ function isPlainObject(value) {
65
65
  //#endregion
66
66
  //#region src/load.ts
67
67
  /**
68
- * Discover and load `<name>.config.*` files. Holocron-agnostic — no
69
- * schema, no validation, no defaults. Consumers layer those on top.
68
+ * Discover and load `<name>.config.*` files, or load one from content that
69
+ * didn't come from a file at all ({@link loadConfigFromContent}).
70
+ * Holocron-agnostic — no schema, no validation, no defaults. Consumers
71
+ * layer those on top.
70
72
  *
71
73
  * Probe order is **TS-first**: `.ts` → `.js` → `.mjs` → `.cjs` → `.json`.
72
74
  * TS is loaded through `tsx`'s `tsImport` (a runtime dependency) so a
@@ -127,6 +129,24 @@ async function loadLayered(opts) {
127
129
  sources
128
130
  };
129
131
  }
132
+ /**
133
+ * Load config from content that didn't come from a file already on disk —
134
+ * writes it to `<dir>/<name>.config.<extension>` first, then loads it
135
+ * through the exact same path {@link loadConfigFile} uses for a file it
136
+ * discovered itself. Exists because sourcing config content from somewhere
137
+ * other than the local filesystem (a GitHub API fetch, for one — see
138
+ * `@theholocron/sentinel`) doesn't change what loading it correctly means:
139
+ * a `.ts` config still needs real module resolution, `defineConfig` import
140
+ * included, not a re-implemented parser.
141
+ */
142
+ async function loadConfigFromContent(opts) {
143
+ const filepath = join(opts.dir, `${opts.name}.config.${opts.extension}`);
144
+ await writeFile(filepath, opts.content, "utf8");
145
+ return {
146
+ config: await loadFile(filepath, opts.extension),
147
+ filepath
148
+ };
149
+ }
130
150
  async function loadFile(filepath, ext) {
131
151
  if (ext === "json") return loadJson(filepath);
132
152
  return extractDefault(filepath, ext === "ts" ? await importTs(filepath) : await importModule(filepath));
@@ -188,4 +208,4 @@ async function isFile(path) {
188
208
  }
189
209
  const message = (err) => err instanceof Error ? err.message : String(err);
190
210
  //#endregion
191
- export { ConfigFileError, DEFAULT_EXTENSIONS, createDefineConfig, loadConfigFile, loadLayered, mergeConfig };
211
+ export { ConfigFileError, DEFAULT_EXTENSIONS, createDefineConfig, loadConfigFile, loadConfigFromContent, loadLayered, mergeConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/datapad",
3
- "version": "5.0.0-alpha.6",
3
+ "version": "5.0.0-alpha.60",
4
4
  "description": "Generic config-file loading for Holocron — discover, load (JSON/JS/TS/ESM/CJS), and merge <name>.config.* files with a typed defineConfig.",
5
5
  "keywords": [
6
6
  "config",
@@ -35,10 +35,10 @@
35
35
  "tsx": "4.23.12"
36
36
  },
37
37
  "devDependencies": {
38
- "@theholocron/eslint-config": "^8.0.0",
39
- "@theholocron/tsconfig": "^8.0.0",
40
- "@theholocron/tsdown-config": "^8.0.0",
41
- "@theholocron/vitest-config": "^8.0.0",
38
+ "@theholocron/eslint-config": "^8.4.5",
39
+ "@theholocron/tsconfig": "^8.4.5",
40
+ "@theholocron/tsdown-config": "^8.4.5",
41
+ "@theholocron/vitest-config": "^8.4.5",
42
42
  "@types/node": "^26",
43
43
  "@vitest/coverage-v8": "^4.1.11",
44
44
  "@vitest/eslint-plugin": "^1.6.27",