@nodetool-ai/config 0.7.0-rc.23 → 0.8.1

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 (89) hide show
  1. package/README.md +50 -0
  2. package/dist/.nodetool-build-stamp +1 -0
  3. package/dist/__tests__/credit-policy.test.d.ts +2 -0
  4. package/dist/__tests__/credit-policy.test.d.ts.map +1 -0
  5. package/dist/__tests__/credit-policy.test.js +48 -0
  6. package/dist/__tests__/credit-policy.test.js.map +1 -0
  7. package/dist/__tests__/env-file.test.d.ts +2 -0
  8. package/dist/__tests__/env-file.test.d.ts.map +1 -0
  9. package/dist/__tests__/env-file.test.js +100 -0
  10. package/dist/__tests__/env-file.test.js.map +1 -0
  11. package/dist/__tests__/google-workspace.test.d.ts +2 -0
  12. package/dist/__tests__/google-workspace.test.d.ts.map +1 -0
  13. package/dist/__tests__/google-workspace.test.js +40 -0
  14. package/dist/__tests__/google-workspace.test.js.map +1 -0
  15. package/dist/__tests__/retention.test.d.ts +2 -0
  16. package/dist/__tests__/retention.test.d.ts.map +1 -0
  17. package/dist/__tests__/retention.test.js +21 -0
  18. package/dist/__tests__/retention.test.js.map +1 -0
  19. package/dist/byte-limits.d.ts +13 -0
  20. package/dist/byte-limits.d.ts.map +1 -0
  21. package/dist/byte-limits.js +28 -0
  22. package/dist/byte-limits.js.map +1 -0
  23. package/dist/credit-policy.d.ts +25 -0
  24. package/dist/credit-policy.d.ts.map +1 -0
  25. package/dist/credit-policy.js +63 -0
  26. package/dist/credit-policy.js.map +1 -0
  27. package/dist/deployment.d.ts +8 -0
  28. package/dist/deployment.d.ts.map +1 -0
  29. package/dist/deployment.js +19 -0
  30. package/dist/deployment.js.map +1 -0
  31. package/dist/env-file.d.ts +33 -0
  32. package/dist/env-file.d.ts.map +1 -0
  33. package/dist/env-file.js +83 -0
  34. package/dist/env-file.js.map +1 -0
  35. package/dist/environment.d.ts.map +1 -1
  36. package/dist/environment.js +20 -17
  37. package/dist/environment.js.map +1 -1
  38. package/dist/google-workspace.d.ts +3 -0
  39. package/dist/google-workspace.d.ts.map +1 -0
  40. package/dist/google-workspace.js +23 -0
  41. package/dist/google-workspace.js.map +1 -0
  42. package/dist/index.d.ts +12 -3
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +12 -3
  45. package/dist/index.js.map +1 -1
  46. package/dist/logging.d.ts.map +1 -1
  47. package/dist/logging.js +46 -28
  48. package/dist/logging.js.map +1 -1
  49. package/dist/node-import.d.ts +37 -0
  50. package/dist/node-import.d.ts.map +1 -0
  51. package/dist/node-import.js +119 -0
  52. package/dist/node-import.js.map +1 -0
  53. package/dist/optional-modules.d.ts +8 -0
  54. package/dist/optional-modules.d.ts.map +1 -0
  55. package/dist/optional-modules.js +42 -0
  56. package/dist/optional-modules.js.map +1 -0
  57. package/dist/package-asset-registry.d.ts +102 -0
  58. package/dist/package-asset-registry.d.ts.map +1 -0
  59. package/dist/package-asset-registry.js +154 -0
  60. package/dist/package-asset-registry.js.map +1 -0
  61. package/dist/package-assets.d.ts +52 -0
  62. package/dist/package-assets.d.ts.map +1 -0
  63. package/dist/package-assets.js +98 -0
  64. package/dist/package-assets.js.map +1 -0
  65. package/dist/paths.d.ts +52 -23
  66. package/dist/paths.d.ts.map +1 -1
  67. package/dist/paths.js +166 -47
  68. package/dist/paths.js.map +1 -1
  69. package/dist/retention.d.ts +3 -0
  70. package/dist/retention.d.ts.map +1 -0
  71. package/dist/retention.js +19 -0
  72. package/dist/retention.js.map +1 -0
  73. package/dist/setting-catalog.d.ts +28 -0
  74. package/dist/setting-catalog.d.ts.map +1 -0
  75. package/dist/setting-catalog.js +161 -0
  76. package/dist/setting-catalog.js.map +1 -0
  77. package/dist/storage-config.d.ts +45 -0
  78. package/dist/storage-config.d.ts.map +1 -0
  79. package/dist/storage-config.js +62 -0
  80. package/dist/storage-config.js.map +1 -0
  81. package/package.json +23 -8
  82. package/dist/diagnostics.d.ts +0 -27
  83. package/dist/diagnostics.d.ts.map +0 -1
  84. package/dist/diagnostics.js +0 -42
  85. package/dist/diagnostics.js.map +0 -1
  86. package/dist/settings.d.ts +0 -29
  87. package/dist/settings.d.ts.map +0 -1
  88. package/dist/settings.js +0 -29
  89. package/dist/settings.js.map +0 -1
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @nodetool-ai/config
2
+
3
+ Configuration and environment loading for [NodeTool](https://nodetool.ai) — settings, paths, logging, byte limits, and cross-runtime module loading.
4
+
5
+ This is the base dependency for the backend: it loads and reads environment variables, resolves data/asset/database paths, registers typed settings, configures logging, and diagnoses the running environment. It also provides cross-runtime helpers for importing Node built-ins and optional modules from code that also runs in the browser.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @nodetool-ai/config
11
+ ```
12
+
13
+ ## Exported symbols
14
+
15
+ | Symbol | Kind | Description |
16
+ | --- | --- | --- |
17
+ | `loadEnvironment` / `resetEnvironment` | function | Load and reset the process environment (dotenv) |
18
+ | `getEnv` / `requireEnv` | function | Read an env var, optionally throwing if missing |
19
+ | `getByteLimitEnv` | function | Parse a byte-size limit from an env var |
20
+ | `settingCatalog` / `settingDefinition` | function | Read the catalog of every configuration knob NodeTool ships |
21
+ | `SettingCatalogEntry` | type | One catalog definition: env var, group, description, `isSecret` |
22
+ | `configureLogging` / `getLogLevel` / `createLogger` | function | Configure logging and create named loggers |
23
+ | `LogLevel` / `LoggingOptions` / `Logger` | type | Logging types |
24
+ | `getNodetoolDataDir` / `getDefaultDbPath` / `getDefaultAssetsPath` | function | Resolve standard data, database, and asset paths |
25
+ | `getPostgresDatabaseUrl` / `getDefaultVectorstoreDbPath` | function | Resolve Postgres and vectorstore locations |
26
+ | `getAssetFilePath` / `buildAssetUrl` | function | Map asset keys to file paths and URLs |
27
+ | `loadAssetStorageConfig` / `loadTempStorageConfig` | function | Load storage configuration from the environment |
28
+ | `StorageConfig` / `SIGNED_URL_TTL` | type / const | Storage config shape and signed-URL lifetime |
29
+ | `IS_NODE` / `importNodeBuiltin` / `importHidden` / `importOptionalModule` | const / function | Cross-runtime module loading helpers |
30
+
31
+ ## Usage
32
+
33
+ ```ts
34
+ import {
35
+ loadEnvironment,
36
+ requireEnv,
37
+ createLogger,
38
+ getDefaultDbPath
39
+ } from "@nodetool-ai/config";
40
+
41
+ loadEnvironment();
42
+ const log = createLogger("server");
43
+ log.info("db path", getDefaultDbPath());
44
+ const token = requireEnv("NODETOOL_TOKEN");
45
+ ```
46
+
47
+ ## Links
48
+
49
+ - [NodeTool](https://nodetool.ai)
50
+ - [GitHub](https://github.com/nodetool-ai/nodetool)
@@ -0,0 +1 @@
1
+ 2026-09-28T09:19:01.424Z
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=credit-policy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credit-policy.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/credit-policy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { CREDIT_MODELS_ENV, DEFAULT_SIGNUP_CREDITS, SIGNUP_CREDITS_ENV, creditModelAllowlist, isCreditModelAllowed, signupGrantCredits } from "../credit-policy.js";
3
+ const env = (value) => value;
4
+ describe("creditModelAllowlist", () => {
5
+ it("is null when the operator restricted nothing — the whole catalog", () => {
6
+ expect(creditModelAllowlist({})).toBeNull();
7
+ expect(creditModelAllowlist(env({ [CREDIT_MODELS_ENV]: " " }))).toBeNull();
8
+ expect(creditModelAllowlist(env({ [CREDIT_MODELS_ENV]: ", ," }))).toBeNull();
9
+ });
10
+ it("splits on commas, spaces, and newlines", () => {
11
+ const allowed = creditModelAllowlist(env({
12
+ [CREDIT_MODELS_ENV]: "nodetool/flux-schnell, nodetool/kokoro\n nodetool/hailuo-fast"
13
+ }));
14
+ expect([...allowed].sort()).toEqual([
15
+ "nodetool/flux-schnell",
16
+ "nodetool/hailuo-fast",
17
+ "nodetool/kokoro"
18
+ ]);
19
+ });
20
+ });
21
+ describe("isCreditModelAllowed", () => {
22
+ it("allows everything when nothing is whitelisted", () => {
23
+ expect(isCreditModelAllowed("nodetool/seedream", {})).toBe(true);
24
+ });
25
+ it("allows only what the whitelist names", () => {
26
+ const configured = env({
27
+ [CREDIT_MODELS_ENV]: "nodetool/flux-schnell,nodetool/kokoro"
28
+ });
29
+ expect(isCreditModelAllowed("nodetool/flux-schnell", configured)).toBe(true);
30
+ expect(isCreditModelAllowed("nodetool/seedream", configured)).toBe(false);
31
+ });
32
+ });
33
+ describe("signupGrantCredits", () => {
34
+ it("defaults when unset or blank", () => {
35
+ expect(signupGrantCredits({})).toBe(DEFAULT_SIGNUP_CREDITS);
36
+ expect(signupGrantCredits(env({ [SIGNUP_CREDITS_ENV]: " " }))).toBe(DEFAULT_SIGNUP_CREDITS);
37
+ });
38
+ it("reads a configured amount, floored to whole credits", () => {
39
+ expect(signupGrantCredits(env({ [SIGNUP_CREDITS_ENV]: "1200" }))).toBe(1200);
40
+ expect(signupGrantCredits(env({ [SIGNUP_CREDITS_ENV]: "12.9" }))).toBe(12);
41
+ });
42
+ it("treats zero, negatives, and junk as no welcome grant", () => {
43
+ for (const raw of ["0", "-5", "lots"]) {
44
+ expect(signupGrantCredits(env({ [SIGNUP_CREDITS_ENV]: raw }))).toBe(0);
45
+ }
46
+ });
47
+ });
48
+ //# sourceMappingURL=credit-policy.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credit-policy.test.js","sourceRoot":"","sources":["../../src/__tests__/credit-policy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,GAAG,GAAG,CAAC,KAA6B,EAAE,EAAE,CAAC,KAAK,CAAC;AAErD,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5C,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5E,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC,iBAAiB,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,OAAO,GAAG,oBAAoB,CAClC,GAAG,CAAC;YACF,CAAC,iBAAiB,CAAC,EACjB,+DAA+D;SAClE,CAAC,CACH,CAAC;QACF,MAAM,CAAC,CAAC,GAAG,OAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC;YACnC,uBAAuB;YACvB,sBAAsB;YACtB,iBAAiB;SAClB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,oBAAoB,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,UAAU,GAAG,GAAG,CAAC;YACrB,CAAC,iBAAiB,CAAC,EAAE,uCAAuC;SAC7D,CAAC,CAAC;QACH,MAAM,CAAC,oBAAoB,CAAC,uBAAuB,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,MAAM,CAAC,oBAAoB,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC5D,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAClE,sBAAsB,CACvB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YACtC,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,kBAAkB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=env-file.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/env-file.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import * as fs from "node:fs";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { loadEnvFile, parseEnvFile } from "../env-file.js";
7
+ describe("parseEnvFile", () => {
8
+ it("parses simple KEY=VALUE pairs", () => {
9
+ expect(parseEnvFile("A=1\nB=two")).toEqual({ A: "1", B: "two" });
10
+ });
11
+ it("skips blank lines and # comments", () => {
12
+ const input = "\n# a comment\nA=1\n\n # indented comment\nB=2\n";
13
+ expect(parseEnvFile(input)).toEqual({ A: "1", B: "2" });
14
+ });
15
+ it("strips an optional export prefix", () => {
16
+ expect(parseEnvFile("export A=1\nexport B=2")).toEqual({ A: "1", B: "2" });
17
+ });
18
+ it("trims unquoted values", () => {
19
+ expect(parseEnvFile("A= spaced ")).toEqual({ A: "spaced" });
20
+ });
21
+ it("keeps single-quoted values literal", () => {
22
+ expect(parseEnvFile("A='line\\n still'")).toEqual({ A: "line\\n still" });
23
+ });
24
+ it("expands \\n \\r \\t in double-quoted values", () => {
25
+ expect(parseEnvFile('A="line1\\nline2\\tend"')).toEqual({
26
+ A: "line1\nline2\tend"
27
+ });
28
+ });
29
+ it("preserves surrounding whitespace inside quotes", () => {
30
+ expect(parseEnvFile('A=" padded "')).toEqual({ A: " padded " });
31
+ });
32
+ it("handles values containing =", () => {
33
+ expect(parseEnvFile("URL=postgres://u:p@h/db?a=b&c=d")).toEqual({
34
+ URL: "postgres://u:p@h/db?a=b&c=d"
35
+ });
36
+ });
37
+ it("strips inline comments from unquoted values", () => {
38
+ expect(parseEnvFile("A=bar # explanation\nB=bar#explanation")).toEqual({
39
+ A: "bar",
40
+ B: "bar"
41
+ });
42
+ });
43
+ it("preserves hashes inside quoted values before inline comments", () => {
44
+ expect(parseEnvFile(`A="double # value" # comment\nB='single # value'#comment`)).toEqual({
45
+ A: "double # value",
46
+ B: "single # value"
47
+ });
48
+ });
49
+ it("preserves hashes after escaped quotes inside quoted values", () => {
50
+ expect(parseEnvFile('A="quoted \\"text\\" # value" # comment')).toEqual({
51
+ A: 'quoted \\"text\\" # value'
52
+ });
53
+ });
54
+ it("treats hashes after backslashes in unquoted values as comments", () => {
55
+ expect(parseEnvFile("A=bar\\#comment")).toEqual({ A: "bar\\" });
56
+ });
57
+ it("ignores lines without = and empty keys", () => {
58
+ expect(parseEnvFile("NOTAKEY\n=value\nA=1")).toEqual({ A: "1" });
59
+ });
60
+ });
61
+ describe("loadEnvFile", () => {
62
+ let dir;
63
+ const created = [];
64
+ beforeEach(() => {
65
+ dir = mkdtempSync(join(tmpdir(), "envfile-"));
66
+ });
67
+ afterEach(() => {
68
+ for (const key of created)
69
+ delete process.env[key];
70
+ created.length = 0;
71
+ rmSync(dir, { recursive: true, force: true });
72
+ });
73
+ const write = (name, content) => {
74
+ const file = join(dir, name);
75
+ writeFileSync(file, content);
76
+ return file;
77
+ };
78
+ it("applies pairs to process.env", () => {
79
+ created.push("ENVFILE_TEST_A");
80
+ const file = write(".env", "ENVFILE_TEST_A=hello");
81
+ const parsed = loadEnvFile(fs, file);
82
+ expect(parsed).toEqual({ ENVFILE_TEST_A: "hello" });
83
+ expect(process.env.ENVFILE_TEST_A).toBe("hello");
84
+ });
85
+ it("overrides existing process.env values", () => {
86
+ created.push("ENVFILE_TEST_B");
87
+ process.env.ENVFILE_TEST_B = "first";
88
+ const first = write(".env", "ENVFILE_TEST_B=first-file");
89
+ loadEnvFile(fs, first);
90
+ expect(process.env.ENVFILE_TEST_B).toBe("first-file");
91
+ const second = write(".env.local", "ENVFILE_TEST_B=second-file");
92
+ loadEnvFile(fs, second);
93
+ expect(process.env.ENVFILE_TEST_B).toBe("second-file");
94
+ });
95
+ it("tolerates a missing file silently", () => {
96
+ const parsed = loadEnvFile(fs, join(dir, "does-not-exist.env"));
97
+ expect(parsed).toEqual({});
98
+ });
99
+ });
100
+ //# sourceMappingURL=env-file.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.test.js","sourceRoot":"","sources":["../../src/__tests__/env-file.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=google-workspace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-workspace.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/google-workspace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { isGoogleWorkspaceEnabled } from "../google-workspace.js";
3
+ const KEYS = ["SUPABASE_URL", "SUPABASE_KEY", "NODETOOL_GOOGLE_WORKSPACE"];
4
+ describe("isGoogleWorkspaceEnabled", () => {
5
+ let saved;
6
+ beforeEach(() => {
7
+ saved = Object.fromEntries(KEYS.map((k) => [k, process.env[k]]));
8
+ for (const key of KEYS)
9
+ delete process.env[key];
10
+ });
11
+ afterEach(() => {
12
+ for (const key of KEYS) {
13
+ if (saved[key] === undefined)
14
+ delete process.env[key];
15
+ else
16
+ process.env[key] = saved[key];
17
+ }
18
+ });
19
+ it("is off in local mode", () => {
20
+ expect(isGoogleWorkspaceEnabled()).toBe(false);
21
+ });
22
+ it("is on when Supabase auth is configured", () => {
23
+ process.env.SUPABASE_URL = "https://project.supabase.co";
24
+ process.env.SUPABASE_KEY = "service-role-key";
25
+ expect(isGoogleWorkspaceEnabled()).toBe(true);
26
+ });
27
+ it("stays off when only one Supabase variable is set", () => {
28
+ process.env.SUPABASE_URL = "https://project.supabase.co";
29
+ expect(isGoogleWorkspaceEnabled()).toBe(false);
30
+ });
31
+ it("honours the explicit override in both directions", () => {
32
+ process.env.NODETOOL_GOOGLE_WORKSPACE = "1";
33
+ expect(isGoogleWorkspaceEnabled()).toBe(true);
34
+ process.env.SUPABASE_URL = "https://project.supabase.co";
35
+ process.env.SUPABASE_KEY = "service-role-key";
36
+ process.env.NODETOOL_GOOGLE_WORKSPACE = "0";
37
+ expect(isGoogleWorkspaceEnabled()).toBe(false);
38
+ });
39
+ });
40
+ //# sourceMappingURL=google-workspace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-workspace.test.js","sourceRoot":"","sources":["../../src/__tests__/google-workspace.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAElE,MAAM,IAAI,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,2BAA2B,CAAC,CAAC;AAE3E,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,IAAI,KAAyC,CAAC;IAE9C,UAAU,CAAC,GAAG,EAAE;QACd,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,SAAS;gBAAE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;gBACjD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,6BAA6B,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,kBAAkB,CAAC;QAC9C,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,6BAA6B,CAAC;QACzD,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,GAAG,CAAC;QAC5C,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE9C,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,6BAA6B,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,kBAAkB,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,GAAG,CAAC;QAC5C,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=retention.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/retention.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { isAutomaticStorageCleanupEnabled } from "../retention.js";
3
+ describe("isAutomaticStorageCleanupEnabled", () => {
4
+ it("is off when the variable is unset", () => {
5
+ expect(isAutomaticStorageCleanupEnabled({})).toBe(false);
6
+ });
7
+ it("is on for 1 and true", () => {
8
+ expect(isAutomaticStorageCleanupEnabled({ NODETOOL_STORAGE_AUTO_CLEANUP: "1" })).toBe(true);
9
+ expect(isAutomaticStorageCleanupEnabled({
10
+ NODETOOL_STORAGE_AUTO_CLEANUP: " TRUE "
11
+ })).toBe(true);
12
+ });
13
+ it("is off for 0, false, and anything else", () => {
14
+ for (const value of ["0", "false", "", "yes please"]) {
15
+ expect(isAutomaticStorageCleanupEnabled({
16
+ NODETOOL_STORAGE_AUTO_CLEANUP: value
17
+ })).toBe(false);
18
+ }
19
+ });
20
+ });
21
+ //# sourceMappingURL=retention.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.test.js","sourceRoot":"","sources":["../../src/__tests__/retention.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gCAAgC,EAAE,MAAM,iBAAiB,CAAC;AAEnE,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,gCAAgC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CACJ,gCAAgC,CAAC,EAAE,6BAA6B,EAAE,GAAG,EAAE,CAAC,CACzE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,CACJ,gCAAgC,CAAC;YAC/B,6BAA6B,EAAE,QAAQ;SACxC,CAAC,CACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,CAAC;YACrD,MAAM,CACJ,gCAAgC,CAAC;gBAC/B,6BAA6B,EAAE,KAAK;aACrC,CAAC,CACH,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Resolve a byte-size limit from an environment variable.
3
+ *
4
+ * Returns `fallback` when the variable is unset, blank, non-numeric, or not a
5
+ * positive finite number. This is deliberately strict: a bad value (e.g.
6
+ * `"unlimited"`, `"-1"`, `"0"`) falls back to the safe default rather than
7
+ * resolving to `NaN`/`0` and turning the cap off.
8
+ *
9
+ * @param key Environment variable name (e.g. `NODETOOL_MAX_UPLOAD_BYTES`).
10
+ * @param fallback Default limit in bytes, used when the env var is invalid.
11
+ */
12
+ export declare function getByteLimitEnv(key: string, fallback: number): number;
13
+ //# sourceMappingURL=byte-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byte-limits.d.ts","sourceRoot":"","sources":["../src/byte-limits.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMrE"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Byte-size limit helpers.
3
+ *
4
+ * Centralizes parsing of configurable size caps (upload size, message size,
5
+ * IPC frame size) so a malformed override can never silently disable a guard.
6
+ */
7
+ import { getEnv } from "./environment.js";
8
+ /**
9
+ * Resolve a byte-size limit from an environment variable.
10
+ *
11
+ * Returns `fallback` when the variable is unset, blank, non-numeric, or not a
12
+ * positive finite number. This is deliberately strict: a bad value (e.g.
13
+ * `"unlimited"`, `"-1"`, `"0"`) falls back to the safe default rather than
14
+ * resolving to `NaN`/`0` and turning the cap off.
15
+ *
16
+ * @param key Environment variable name (e.g. `NODETOOL_MAX_UPLOAD_BYTES`).
17
+ * @param fallback Default limit in bytes, used when the env var is invalid.
18
+ */
19
+ export function getByteLimitEnv(key, fallback) {
20
+ const raw = getEnv(key);
21
+ if (raw === undefined || raw.trim() === "")
22
+ return fallback;
23
+ const parsed = Number(raw);
24
+ if (!Number.isFinite(parsed) || parsed <= 0)
25
+ return fallback;
26
+ return Math.floor(parsed);
27
+ }
28
+ //# sourceMappingURL=byte-limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byte-limits.js","sourceRoot":"","sources":["../src/byte-limits.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,QAAgB;IAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7D,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,25 @@
1
+ /** Names the managed models credits may be spent on. */
2
+ export declare const CREDIT_MODELS_ENV = "NODETOOL_CREDIT_MODELS";
3
+ /** One-time welcome grant, in credits, for a user seen for the first time. */
4
+ export declare const SIGNUP_CREDITS_ENV = "NODETOOL_SIGNUP_CREDITS";
5
+ /** Credits a new user is granted when no override is configured. */
6
+ export declare const DEFAULT_SIGNUP_CREDITS = 500;
7
+ type Env = Record<string, string | undefined>;
8
+ /**
9
+ * The configured allowlist of managed model ids, or `null` when the operator
10
+ * set none — `null` is "the whole catalog", not "nothing".
11
+ *
12
+ * Entries are separated by commas, whitespace, or newlines, so the variable
13
+ * reads the same in a `.env` file, a `fly secrets` line, and a Compose file.
14
+ */
15
+ export declare function creditModelAllowlist(env?: Env): ReadonlySet<string> | null;
16
+ /** Whether `modelId` may be served by the managed provider on this server. */
17
+ export declare function isCreditModelAllowed(modelId: string, env?: Env): boolean;
18
+ /**
19
+ * Credits granted once, the first time a user is seen. `0` (or any negative
20
+ * or unparseable value) means no welcome grant — the monthly plan grant is
21
+ * then all a new user gets.
22
+ */
23
+ export declare function signupGrantCredits(env?: Env): number;
24
+ export {};
25
+ //# sourceMappingURL=credit-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credit-policy.d.ts","sourceRoot":"","sources":["../src/credit-policy.ts"],"names":[],"mappings":"AAqBA,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAC1D,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,4BAA4B,CAAC;AAE5D,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,GAAsB,GAC1B,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAQ5B;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,GAAsB,GAC1B,OAAO,CAGT;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,GAAsB,GAAG,MAAM,CAMtE"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Which managed models credits may be spent on, and what a new user starts
3
+ * with — the two knobs a production operator turns.
4
+ *
5
+ * NodeTool's `nodetool` provider runs curated delegates on platform-owned
6
+ * keys, so every call through it spends the operator's money. The operator
7
+ * therefore decides which of the catalog's entries are open for business:
8
+ * `NODETOOL_CREDIT_MODELS` names them, and anything else is refused before a
9
+ * key is used. Unset means the whole catalog — the right default for a
10
+ * staging or dev server that holds platform keys.
11
+ *
12
+ * None of this reaches a local install: `nodetool` is a cloud-only provider
13
+ * (`CLOUD_ONLY_PROVIDER_IDS`), unregistered off the cloud profile, so a
14
+ * desktop or self-hosted server has no managed provider to whitelist.
15
+ *
16
+ * Parsing lives here rather than next to the catalog because the catalog
17
+ * (`@nodetool-ai/protocol`) is browser-safe pure data with no environment to
18
+ * read. Callers resolve names against the catalog themselves.
19
+ */
20
+ import { safeProcessEnv } from "./node-import.js";
21
+ /** Names the managed models credits may be spent on. */
22
+ export const CREDIT_MODELS_ENV = "NODETOOL_CREDIT_MODELS";
23
+ /** One-time welcome grant, in credits, for a user seen for the first time. */
24
+ export const SIGNUP_CREDITS_ENV = "NODETOOL_SIGNUP_CREDITS";
25
+ /** Credits a new user is granted when no override is configured. */
26
+ export const DEFAULT_SIGNUP_CREDITS = 500;
27
+ /**
28
+ * The configured allowlist of managed model ids, or `null` when the operator
29
+ * set none — `null` is "the whole catalog", not "nothing".
30
+ *
31
+ * Entries are separated by commas, whitespace, or newlines, so the variable
32
+ * reads the same in a `.env` file, a `fly secrets` line, and a Compose file.
33
+ */
34
+ export function creditModelAllowlist(env = safeProcessEnv()) {
35
+ const raw = env[CREDIT_MODELS_ENV]?.trim();
36
+ if (!raw)
37
+ return null;
38
+ const ids = raw
39
+ .split(/[\s,]+/)
40
+ .map((id) => id.trim())
41
+ .filter((id) => id.length > 0);
42
+ return ids.length > 0 ? new Set(ids) : null;
43
+ }
44
+ /** Whether `modelId` may be served by the managed provider on this server. */
45
+ export function isCreditModelAllowed(modelId, env = safeProcessEnv()) {
46
+ const allowed = creditModelAllowlist(env);
47
+ return allowed === null || allowed.has(modelId);
48
+ }
49
+ /**
50
+ * Credits granted once, the first time a user is seen. `0` (or any negative
51
+ * or unparseable value) means no welcome grant — the monthly plan grant is
52
+ * then all a new user gets.
53
+ */
54
+ export function signupGrantCredits(env = safeProcessEnv()) {
55
+ const raw = env[SIGNUP_CREDITS_ENV]?.trim();
56
+ if (raw === undefined || raw === "")
57
+ return DEFAULT_SIGNUP_CREDITS;
58
+ const parsed = Number(raw);
59
+ if (!Number.isFinite(parsed) || parsed <= 0)
60
+ return 0;
61
+ return Math.floor(parsed);
62
+ }
63
+ //# sourceMappingURL=credit-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credit-policy.js","sourceRoot":"","sources":["../src/credit-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,wDAAwD;AACxD,MAAM,CAAC,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAC1D,8EAA8E;AAC9E,MAAM,CAAC,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;AAE5D,oEAAoE;AACpE,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAI1C;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAG,GAAQ,cAAc,EAAE;IAE3B,MAAM,GAAG,GAAG,GAAG,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,GAAG,GAAG,GAAG;SACZ,KAAK,CAAC,QAAQ,CAAC;SACf,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;SACtB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAClC,OAAe,EACf,GAAG,GAAQ,cAAc,EAAE;IAE3B,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAG,GAAQ,cAAc,EAAE;IAC5D,MAAM,GAAG,GAAG,GAAG,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,CAAC;IAC5C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,sBAAsB,CAAC;IACnE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * True when the server enforces authentication — a remote identity provider
3
+ * (`SUPABASE_URL` + `SUPABASE_KEY`) is configured, so every request carries a
4
+ * user token and the host is shared. False for a local install, where loopback
5
+ * is trusted and the machine belongs to the person using it.
6
+ */
7
+ export declare function isAuthEnforced(env?: Record<string, string | undefined>): boolean;
8
+ //# sourceMappingURL=deployment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../src/deployment.ts"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAoB,GACzD,OAAO,CAET"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * How this server is deployed — one answer, read from the environment.
3
+ *
4
+ * `NODE_ENV` does not answer it: the desktop backend (`electron/src/server.ts`)
5
+ * and the Docker image both set `NODE_ENV=production` while serving one user on
6
+ * their own machine. What separates a shared deployment from a local install is
7
+ * whether a remote identity provider is configured, so that is what this asks.
8
+ */
9
+ import { safeProcessEnv } from "./node-import.js";
10
+ /**
11
+ * True when the server enforces authentication — a remote identity provider
12
+ * (`SUPABASE_URL` + `SUPABASE_KEY`) is configured, so every request carries a
13
+ * user token and the host is shared. False for a local install, where loopback
14
+ * is trusted and the machine belongs to the person using it.
15
+ */
16
+ export function isAuthEnforced(env = safeProcessEnv()) {
17
+ return Boolean(env["SUPABASE_URL"]?.trim() && env["SUPABASE_KEY"]?.trim());
18
+ }
19
+ //# sourceMappingURL=deployment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.js","sourceRoot":"","sources":["../src/deployment.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,GAAG,GAAuC,cAAc,EAAE;IAE1D,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Minimal `.env` file parser — a drop-in for the small subset of `dotenv`
3
+ * we relied on. Node 22's `process.loadEnvFile()` can't override existing
4
+ * `process.env` values, so we parse by hand.
5
+ *
6
+ * Supported syntax (matching dotenv where it matters in practice):
7
+ * - `KEY=VALUE` pairs, one per line.
8
+ * - Blank lines and `#` comment lines are skipped.
9
+ * - An optional `export ` prefix on the key is stripped.
10
+ * - Single- or double-quoted values; double-quoted values expand `\n`,
11
+ * `\r`, and `\t` escapes. Single-quoted values are taken literally.
12
+ * - Unquoted values are trimmed, may contain `=`, and end at `#` comments.
13
+ * Hashes inside quoted values are preserved.
14
+ *
15
+ * Not supported (and not needed by callers): `dotenv-expand`-style
16
+ * `${VAR}` interpolation.
17
+ */
18
+ /** Environment variable names to their values, as a `.env` file spells them. */
19
+ export type EnvPairs = Record<string, string>;
20
+ /** Parse the text of a `.env` file into key/value pairs. */
21
+ export declare function parseEnvFile(content: string): EnvPairs;
22
+ type FsApi = {
23
+ existsSync: (path: string) => boolean;
24
+ readFileSync: (path: string, encoding: "utf8") => string;
25
+ };
26
+ /**
27
+ * Read a `.env` file and apply its pairs to `process.env`, overriding any
28
+ * existing values. Missing files are tolerated silently. Returns the parsed
29
+ * pairs (empty when the file is absent).
30
+ */
31
+ export declare function loadEnvFile(fs: FsApi, file: string): EnvPairs;
32
+ export {};
33
+ //# sourceMappingURL=env-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.d.ts","sourceRoot":"","sources":["../src/env-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,gFAAgF;AAChF,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAoBtD;AAoCD,KAAK,KAAK,GAAG;IACX,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAC1D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAO7D"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Minimal `.env` file parser — a drop-in for the small subset of `dotenv`
3
+ * we relied on. Node 22's `process.loadEnvFile()` can't override existing
4
+ * `process.env` values, so we parse by hand.
5
+ *
6
+ * Supported syntax (matching dotenv where it matters in practice):
7
+ * - `KEY=VALUE` pairs, one per line.
8
+ * - Blank lines and `#` comment lines are skipped.
9
+ * - An optional `export ` prefix on the key is stripped.
10
+ * - Single- or double-quoted values; double-quoted values expand `\n`,
11
+ * `\r`, and `\t` escapes. Single-quoted values are taken literally.
12
+ * - Unquoted values are trimmed, may contain `=`, and end at `#` comments.
13
+ * Hashes inside quoted values are preserved.
14
+ *
15
+ * Not supported (and not needed by callers): `dotenv-expand`-style
16
+ * `${VAR}` interpolation.
17
+ */
18
+ /** Parse the text of a `.env` file into key/value pairs. */
19
+ export function parseEnvFile(content) {
20
+ const pairs = [];
21
+ for (const rawLine of content.split(/\r?\n/)) {
22
+ const line = rawLine.trim();
23
+ if (line === "" || line.startsWith("#"))
24
+ continue;
25
+ const withoutExport = line.startsWith("export ")
26
+ ? line.slice("export ".length)
27
+ : line;
28
+ const eq = withoutExport.indexOf("=");
29
+ if (eq === -1)
30
+ continue;
31
+ const key = withoutExport.slice(0, eq).trim();
32
+ if (key === "")
33
+ continue;
34
+ const rawValue = stripInlineComment(withoutExport.slice(eq + 1));
35
+ pairs.push([key, unquoteValue(rawValue)]);
36
+ }
37
+ return Object.fromEntries(pairs);
38
+ }
39
+ function stripInlineComment(raw) {
40
+ const value = raw.trim();
41
+ const quote = value[0];
42
+ if (quote === "'" || quote === '"') {
43
+ for (let index = 1; index < value.length; index += 1) {
44
+ if (value[index] === quote && value[index - 1] !== "\\") {
45
+ const comment = value.indexOf("#", index + 1);
46
+ return comment === -1 ? value : value.slice(0, comment).trim();
47
+ }
48
+ }
49
+ }
50
+ const comment = value.indexOf("#");
51
+ return comment === -1 ? value : value.slice(0, comment).trim();
52
+ }
53
+ function unquoteValue(raw) {
54
+ if (raw.length >= 2) {
55
+ const quote = raw[0];
56
+ if ((quote === '"' || quote === "'") && raw[raw.length - 1] === quote) {
57
+ const inner = raw.slice(1, -1);
58
+ if (quote === '"') {
59
+ return inner
60
+ .replace(/\\n/g, "\n")
61
+ .replace(/\\r/g, "\r")
62
+ .replace(/\\t/g, "\t");
63
+ }
64
+ return inner;
65
+ }
66
+ }
67
+ return raw;
68
+ }
69
+ /**
70
+ * Read a `.env` file and apply its pairs to `process.env`, overriding any
71
+ * existing values. Missing files are tolerated silently. Returns the parsed
72
+ * pairs (empty when the file is absent).
73
+ */
74
+ export function loadEnvFile(fs, file) {
75
+ if (!fs.existsSync(file))
76
+ return {};
77
+ const parsed = parseEnvFile(fs.readFileSync(file, "utf8"));
78
+ for (const [key, value] of Object.entries(parsed)) {
79
+ process.env[key] = value;
80
+ }
81
+ return parsed;
82
+ }
83
+ //# sourceMappingURL=env-file.js.map