@neon/config 0.14.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,36 +43,64 @@ A policy is split into a **static** existential set and a **dynamic** `branch` c
43
43
 
44
44
  Service toggles accept `true` / `{}` / `{ enabled: true }` (enabled) and `false` / `{ enabled: false }` (disabled). Function slugs (record keys) must match `^[a-z0-9]{1,20}$`.
45
45
 
46
- ### Unbundleable dependencies (`externalPackages`)
46
+ ### Shipping a dependency's files (`externalPackages`)
47
47
 
48
- A function's `source` is bundled with esbuild at deploy time, and some packages cannot be bundled at all: a native `.node` addon has no esbuild loader, and a library may reference an optional peer dependency on a code path the function never takes. Either one fails the deploy with a resolve or loader error naming the package, and neither is fixable from the function's own source.
48
+ A function's `source` is bundled with esbuild at deploy time, and a package backed by a native `.node` binary cannot be bundled by anything: the binary is a compiled object the platform loads from a real path. `sharp` is the common case, and it does not even fail the build it loads its binary through `createRequire`, which esbuild does not follow, so it bundles cleanly and then fails at invoke with `Could not load the "sharp" module`.
49
49
 
50
- `externalPackages` is the escape hatch, and the deploy-time counterpart of Next.js's `serverExternalPackages` every entry is passed to esbuild's `external`, so the import survives into the bundle instead of being followed:
50
+ `externalPackages` is the deploy-time counterpart of Next.js's `serverExternalPackages`. Every entry is passed to esbuild's `external`, so the import survives into the bundle instead of being followed, and the package's own files are shipped into the archive beside the bundle so that import resolves:
51
51
 
52
52
  ```ts
53
53
  export default defineConfig({
54
54
  preview: {
55
55
  functions: {
56
- agent: {
57
- name: "Agent",
58
- source: "./functions/agent.ts",
59
- externalPackages: ["microsandbox", "@mongodb-js/zstd"],
56
+ resize: {
57
+ name: "Resize",
58
+ source: "./functions/resize.ts",
59
+ externalPackages: ["sharp"],
60
60
  },
61
61
  },
62
62
  },
63
63
  });
64
64
  ```
65
65
 
66
- **An external package is not resolvable at runtime.** The deployed archive is a single `index.mjs` with no `node_modules` beside it, so anything listed here throws `Cannot find module` if the function actually reaches it. The option unblocks an import that is never evaluated; it does not make a dependency usable.
66
+ Each declared package is installed for the Functions runtime target **linux-arm64, glibc** — into a throwaway directory, traced for the files it actually reaches, and copied into the archive under `node_modules/` with its directory layout intact. The layout matters: a `.node` addon finds its sibling shared libraries relative to its own directory, so a flattened tree fails to load.
67
67
 
68
- A dependency the handler actually calls has to be bundled, and whether that is possible depends on what it is:
68
+ Your own `node_modules` is never read for those files or modified. Its binaries are built for your machine rather than the deploy target, and a cross-platform install does not survive your next plain `npm install`, so the target's packages are resolved fresh on each deploy.
69
69
 
70
- - **Pure JavaScript** — bundling is the normal case, and a failure is usually something specific and fixable in the entry.
71
- - **Backed by a native `.node` binary** — cannot be bundled by any bundler; the binary is a compiled object the platform loads from a real path. Such a package cannot work on Functions until the archive can carry files alongside the bundle. Listing it here does not help, it only moves the error from deploy to invoke.
70
+ Requirements, all checked at deploy time rather than left to fail at invoke:
72
71
 
73
- A native package may also bundle without needing this option at all. `sharp` loads its binary through `createRequire`, which esbuild does not follow, so it bundles cleanly and then fails at invoke with `Could not load the "sharp" module`.
72
+ - the package is installed in your project the deploy stages the version you have, and refuses rather than guessing one
73
+ - the package publishes a linux-arm64 glibc build (`sharp` and most `@napi-rs/*` packages do; anything compiled from source at install time does not)
74
+ - `npm` is on `PATH`
75
+ - the archive stays within the deploy size limits — native binaries are large, so a couple of them is the practical ceiling
74
76
 
75
- Entries are package names, optionally with a subpath (`pkg`, `@scope/pkg`, `pkg/sub`). A relative or absolute path is rejected at validation time. `neon dev` applies the same list, so a local run bundles like a deploy.
77
+ #### When the deploy warns about a package you did not declare
78
+
79
+ A deploy (and `neon dev`) reports a package it bundled that carries native code and is not in
80
+ `externalPackages`, because such a package deploys cleanly and then fails at invoke — `sharp`
81
+ produces no build error at all.
82
+
83
+ **The report is advisory and never fails a deploy.** It can only see that the package contains
84
+ compiled code, not whether your function reaches it. A package with a native accelerator behind
85
+ a working JavaScript fallback — `ws` with `bufferutil` installed is the common one — is reported
86
+ and is already correct; no change is needed.
87
+
88
+ Do not use `includeFiles: false` to silence it. That externalizes the package and ships nothing
89
+ for it, so an import that *is* reached then fails on every invoke.
90
+
91
+ #### Excluding a package's files
92
+
93
+ `includeFiles: false` externalizes an import without shipping anything for it. That is the escape hatch for a package that cannot be staged — no build for the runtime target, or too large — and that the function never actually reaches:
94
+
95
+ ```ts
96
+ externalPackages: ["sharp", { name: "canvas", includeFiles: false }],
97
+ ```
98
+
99
+ **An excluded package is not resolvable at runtime.** Nothing is shipped for it, so it throws `Cannot find module` if the function reaches it. It unblocks an import that is never evaluated; it does not make a dependency usable.
100
+
101
+ Entries are package names, optionally with a subpath (`pkg`, `@scope/pkg`, `pkg/sub`). A relative or absolute path is rejected at validation time. Files are staged per package, so a subpath narrows what esbuild leaves unresolved without narrowing what ships.
102
+
103
+ Under `neon dev` the list only keeps the package out of the bundle. Nothing is installed or copied, and it resolves from your own `node_modules` against your host architecture — which is what you want locally.
76
104
 
77
105
  ### Data API
78
106
 
package/dist/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- import { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConflictReport, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DurationString, DurationUnit, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput } from "./lib/types.js";
1
+ import { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConflictReport, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DurationString, DurationUnit, ExternalPackageDef, ExternalPackageEntry, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedExternalPackage, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput } from "./lib/types.js";
2
2
  import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, isPartialBranchCreateError, isPlatformError } from "./lib/errors.js";
3
3
  import { CreateBranchInput, CreateBucketInput, CreateCredentialInput, CreateProjectInput, DeployFunctionInput, EnableDataApiInput, GetConnectionUriInput, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBranchStorageSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonCredentialSecret, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, UpdateBranchInput } from "./lib/neon-api.js";
4
4
  import { createNeonApiFromOptions } from "./lib/auth.js";
5
5
  import { CredentialFeatureFlags, credentialScopesSatisfied, deriveCredentialScopes } from "./lib/credentials.js";
6
6
  import { defineConfig, resolveConfig } from "./lib/define-config.js";
7
7
  import { DiffOptions, DiffResult, PlanStep, RemotePreviewState, RemoteServiceState, RemoteState, diffConfig } from "./lib/diff.js";
8
+ import { externalPackageRoot, packagesToStage } from "./lib/external-packages.js";
8
9
  import { LoadConfigOptions, loadConfigFromFile } from "./lib/loader.js";
9
10
  import { createRealNeonApi } from "./lib/neon-api-real.js";
10
11
  import { errors, schemas } from "./v1.js";
11
- export { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConfigLoadError, ConfigValidationError, ConflictReport, CreateBranchInput, CreateBucketInput, CreateCredentialInput, CreateProjectInput, CredentialFeatureFlags, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DeployFunctionInput, DiffOptions, DiffResult, DurationString, DurationUnit, EnableDataApiInput, ErrorCode, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, GetConnectionUriInput, LoadConfigOptions, MissingContextError, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBranchStorageSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonCredentialSecret, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, PartialBranchCreateError, PlanStep, PlatformError, PostgresConfig, PreviewInput, PreviewTuning, PushAbortedError, PushConflictError, PushResult, RemotePreviewState, RemoteServiceState, RemoteState, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput, UpdateBranchInput, createNeonApiFromOptions, createRealNeonApi, credentialScopesSatisfied, defineConfig, deriveCredentialScopes, diffConfig, errors, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, resolveConfig, schemas };
12
+ export { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConfigLoadError, ConfigValidationError, ConflictReport, CreateBranchInput, CreateBucketInput, CreateCredentialInput, CreateProjectInput, CredentialFeatureFlags, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DeployFunctionInput, DiffOptions, DiffResult, DurationString, DurationUnit, EnableDataApiInput, ErrorCode, ExternalPackageDef, ExternalPackageEntry, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, GetConnectionUriInput, LoadConfigOptions, MissingContextError, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBranchStorageSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonCredentialSecret, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, PartialBranchCreateError, PlanStep, PlatformError, PostgresConfig, PreviewInput, PreviewTuning, PushAbortedError, PushConflictError, PushResult, RemotePreviewState, RemoteServiceState, RemoteState, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedExternalPackage, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput, UpdateBranchInput, createNeonApiFromOptions, createRealNeonApi, credentialScopesSatisfied, defineConfig, deriveCredentialScopes, diffConfig, errors, externalPackageRoot, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, packagesToStage, resolveConfig, schemas };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, isPartialBranchCreateError, isPlatformError } from "./lib/errors.js";
2
+ import { externalPackageRoot, packagesToStage } from "./lib/external-packages.js";
2
3
  import { createRealNeonApi } from "./lib/neon-api-real.js";
3
4
  import { createNeonApiFromOptions } from "./lib/auth.js";
4
5
  import { credentialScopesSatisfied, deriveCredentialScopes } from "./lib/credentials.js";
@@ -7,4 +8,4 @@ import { diffConfig } from "./lib/diff.js";
7
8
  import { loadConfigFromFile } from "./lib/loader.js";
8
9
  import { DATA_API_AUTH_PROVIDERS } from "./lib/types.js";
9
10
  import { errors, schemas } from "./v1.js";
10
- export { ConfigLoadError, ConfigValidationError, DATA_API_AUTH_PROVIDERS, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, credentialScopesSatisfied, defineConfig, deriveCredentialScopes, diffConfig, errors, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, resolveConfig, schemas };
11
+ export { ConfigLoadError, ConfigValidationError, DATA_API_AUTH_PROVIDERS, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, credentialScopesSatisfied, defineConfig, deriveCredentialScopes, diffConfig, errors, externalPackageRoot, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, packagesToStage, resolveConfig, schemas };
@@ -10,8 +10,8 @@ import { NeonApi } from "./neon-api.js";
10
10
  * variables and no files. Everything it needs arrives in `options`. Resolving *where* a
11
11
  * credential comes from — a flag, `NEON_API_KEY`, a credentials file on disk — is the
12
12
  * caller's job, because only the caller knows which of those its users expect. See
13
- * `packages/cli` (`ensureAuth` + `resolveApiKeyFromEnv`) and `packages/init`
14
- * (`src/lib/auth.ts`) for the two implementations in this repo.
13
+ * `packages/cli` (`ensureAuth` + `resolveApiKeyFromEnv`) and its init flow
14
+ * (`packages/cli/src/init/auth.ts`) for the two implementations in this repo.
15
15
  *
16
16
  * `apiHost` stays **optional** and defaults to production
17
17
  * (`https://console.neon.tech/api/v2`, applied by {@link createRealNeonApi}) — only pass it
package/dist/lib/auth.js CHANGED
@@ -14,8 +14,8 @@ function normalizeApiHost(url) {
14
14
  * variables and no files. Everything it needs arrives in `options`. Resolving *where* a
15
15
  * credential comes from — a flag, `NEON_API_KEY`, a credentials file on disk — is the
16
16
  * caller's job, because only the caller knows which of those its users expect. See
17
- * `packages/cli` (`ensureAuth` + `resolveApiKeyFromEnv`) and `packages/init`
18
- * (`src/lib/auth.ts`) for the two implementations in this repo.
17
+ * `packages/cli` (`ensureAuth` + `resolveApiKeyFromEnv`) and its init flow
18
+ * (`packages/cli/src/init/auth.ts`) for the two implementations in this repo.
19
19
  *
20
20
  * `apiHost` stays **optional** and defaults to production
21
21
  * (`https://console.neon.tech/api/v2`, applied by {@link createRealNeonApi}) — only pass it
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { ErrorCode, PlatformError } from \"./errors.js\";\nimport type { NeonApi } from \"./neon-api.js\";\nimport { createRealNeonApi } from \"./neon-api-real.js\";\n\n/** Trim trailing slashes and surrounding whitespace; treat empty as unset. */\nfunction normalizeApiHost(url: string | undefined): string | undefined {\n\tconst trimmed = url?.trim().replace(/\\/+$/, \"\");\n\treturn trimmed ? trimmed : undefined;\n}\n\n/**\n * Build a real {@link NeonApi} adapter from an explicit API key, or throw a uniform\n * `PLATFORM_MISSING_API_KEY` error when the caller didn't supply one.\n *\n * **This function is pure with respect to its environment**: it reads no environment\n * variables and no files. Everything it needs arrives in `options`. Resolving *where* a\n * credential comes from — a flag, `NEON_API_KEY`, a credentials file on disk — is the\n * caller's job, because only the caller knows which of those its users expect. See\n * `packages/cli` (`ensureAuth` + `resolveApiKeyFromEnv`) and `packages/init`\n * (`src/lib/auth.ts`) for the two implementations in this repo.\n *\n * `apiHost` stays **optional** and defaults to production\n * (`https://console.neon.tech/api/v2`, applied by {@link createRealNeonApi}) — only pass it\n * to target a non-production API. It is the *ambient* `NEON_API_HOST` lookup that's gone,\n * not the default.\n *\n * Used by `pullConfig`, `pushConfig`, `fetchEnv`, and `branch` to build their default\n * adapter when the caller doesn't inject one. `operation` is the calling function's name\n * (e.g. `\"pushConfig\"`, `\"branch\"`) — it's prepended to the error message so users can tell\n * which call surfaced the missing key.\n */\nexport function createNeonApiFromOptions(\n\toperation: string,\n\toptions: {\n\t\tapiKey?: string;\n\t\tapiHost?: string;\n\t} = {},\n): NeonApi {\n\tconst apiKey = options.apiKey?.trim();\n\tif (!apiKey) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.MissingApiKey,\n\t\t\t[\n\t\t\t\t`${operation} was not given a Neon API key.`,\n\t\t\t\t\"Pass `apiKey` explicitly, or inject your own `api` adapter (e.g. an in-memory fake for tests).\",\n\t\t\t\t\"This package never reads NEON_API_KEY or a credentials file on your behalf — resolve the key in your own application or CLI and pass it in.\",\n\t\t\t\t\"Generate a key at https://console.neon.tech/app/settings/api-keys.\",\n\t\t\t].join(\" \"),\n\t\t);\n\t}\n\n\tconst baseUrl = normalizeApiHost(options.apiHost);\n\treturn createRealNeonApi({\n\t\tapiKey,\n\t\t...(baseUrl ? { baseUrl } : {}),\n\t});\n}\n"],"mappings":";;;;AAKA,SAAS,iBAAiB,KAA6C;CACtE,MAAM,UAAU,KAAK,KAAK,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAC9C,OAAO,UAAU,UAAU,KAAA;AAC5B;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,yBACf,WACA,UAGI,CAAC,GACK;CACV,MAAM,SAAS,QAAQ,QAAQ,KAAK;CACpC,IAAI,CAAC,QACJ,MAAM,IAAI,cACT,UAAU,eACV;EACC,GAAG,UAAU;EACb;EACA;EACA;CACD,CAAC,CAAC,KAAK,GAAG,CACX;CAGD,MAAM,UAAU,iBAAiB,QAAQ,OAAO;CAChD,OAAO,kBAAkB;EACxB;EACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;CAC9B,CAAC;AACF"}
1
+ {"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { ErrorCode, PlatformError } from \"./errors.js\";\nimport type { NeonApi } from \"./neon-api.js\";\nimport { createRealNeonApi } from \"./neon-api-real.js\";\n\n/** Trim trailing slashes and surrounding whitespace; treat empty as unset. */\nfunction normalizeApiHost(url: string | undefined): string | undefined {\n\tconst trimmed = url?.trim().replace(/\\/+$/, \"\");\n\treturn trimmed ? trimmed : undefined;\n}\n\n/**\n * Build a real {@link NeonApi} adapter from an explicit API key, or throw a uniform\n * `PLATFORM_MISSING_API_KEY` error when the caller didn't supply one.\n *\n * **This function is pure with respect to its environment**: it reads no environment\n * variables and no files. Everything it needs arrives in `options`. Resolving *where* a\n * credential comes from — a flag, `NEON_API_KEY`, a credentials file on disk — is the\n * caller's job, because only the caller knows which of those its users expect. See\n * `packages/cli` (`ensureAuth` + `resolveApiKeyFromEnv`) and its init flow\n * (`packages/cli/src/init/auth.ts`) for the two implementations in this repo.\n *\n * `apiHost` stays **optional** and defaults to production\n * (`https://console.neon.tech/api/v2`, applied by {@link createRealNeonApi}) — only pass it\n * to target a non-production API. It is the *ambient* `NEON_API_HOST` lookup that's gone,\n * not the default.\n *\n * Used by `pullConfig`, `pushConfig`, `fetchEnv`, and `branch` to build their default\n * adapter when the caller doesn't inject one. `operation` is the calling function's name\n * (e.g. `\"pushConfig\"`, `\"branch\"`) — it's prepended to the error message so users can tell\n * which call surfaced the missing key.\n */\nexport function createNeonApiFromOptions(\n\toperation: string,\n\toptions: {\n\t\tapiKey?: string;\n\t\tapiHost?: string;\n\t} = {},\n): NeonApi {\n\tconst apiKey = options.apiKey?.trim();\n\tif (!apiKey) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.MissingApiKey,\n\t\t\t[\n\t\t\t\t`${operation} was not given a Neon API key.`,\n\t\t\t\t\"Pass `apiKey` explicitly, or inject your own `api` adapter (e.g. an in-memory fake for tests).\",\n\t\t\t\t\"This package never reads NEON_API_KEY or a credentials file on your behalf — resolve the key in your own application or CLI and pass it in.\",\n\t\t\t\t\"Generate a key at https://console.neon.tech/app/settings/api-keys.\",\n\t\t\t].join(\" \"),\n\t\t);\n\t}\n\n\tconst baseUrl = normalizeApiHost(options.apiHost);\n\treturn createRealNeonApi({\n\t\tapiKey,\n\t\t...(baseUrl ? { baseUrl } : {}),\n\t});\n}\n"],"mappings":";;;;AAKA,SAAS,iBAAiB,KAA6C;CACtE,MAAM,UAAU,KAAK,KAAK,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAC9C,OAAO,UAAU,UAAU,KAAA;AAC5B;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,yBACf,WACA,UAGI,CAAC,GACK;CACV,MAAM,SAAS,QAAQ,QAAQ,KAAK;CACpC,IAAI,CAAC,QACJ,MAAM,IAAI,cACT,UAAU,eACV;EACC,GAAG,UAAU;EACb;EACA;EACA;CACD,CAAC,CAAC,KAAK,GAAG,CACX;CAGD,MAAM,UAAU,iBAAiB,QAAQ,OAAO;CAChD,OAAO,kBAAkB;EACxB;EACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;CAC9B,CAAC;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"define-config.d.ts","names":[],"sources":["../../src/lib/define-config.ts"],"mappings":";;;;;;AAwBoB;AAYI;AACR;AAAf,KADI,mBACJ,CAAA,OAAA,CAAA,GAAA,cAAA,CAAe,OAAf,CAAA,SAAA,IAAA,GAAA,CACI,OADJ,CAAA,SAAA,CAAA;EACI,YAAA,EAAA,UAAA;AAAO,CAAA,CAAA,GAAA,KAAA,GAAA,IAAA,GAAA,KAAA;AAiBZ;AAqBA;AAAwB;AACH;AAApB;AACkB;AAAf;AACC;AAAU;AACV;AACD,KA1BQ,oBAAA,GA0BR,+QAAA;AAAU;AAAY;AAAC;AAoBH;AAAa;AACJ;AAAI;AAEnC;AAC8B;AAAI;AAAS;AAoC7C;AAA4B;AACR;AACG;AACA;AAQf,KA5EI,YA4EJ,CAAA,IAAA,EAAA,OAAA,CAAA,GA3EP,mBA2EO,CA3Ea,OA2Eb,CAAA,SAAA,IAAA,GA1EJ,cA0EI,CA1EW,IA0EX,CAAA,SAAA,IAAA,GAzEH,OAyEG,GAzEO,YAyEP,GAxEH,oBAwEG,GAvEJ,OAuEI,GAvEM,YAuEN;AAAO;AAIS;AAAM;AAAnB;AAIA;AAAU;AAAmC;AAApB;AACX;AAAf;AACC;AAAM;AAAS;AAAtB;AAAM;AA4BV;AAA6B;AACpB;AACA,KA3FJ,mBA2FI,CAAA,OAAA,CAAA,GAAA,CA3F4B,OA2F5B,SAAA;EACN,SAAA,EAAA,KAAA,EAAA;AAAoB,CAAA,GAAA;EAqKP,SAAA,EAAA,iBAhQiB,IAAI;gBAEnC;;;4BAC8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCpB,gCACI,kEACG,4DACA;SAQf,OAAO;YAIJ,aAAa,MAAM;YAInB,UAAU,eAAe,oBAAoB;WAC9C,eAAe;IACrB,OAAO,MAAM,SAAS;;;;;;;;iBA4BV,aAAA,SACP,gBACA,eACN;;;;;;iBAqKa,eAAA"}
1
+ {"version":3,"file":"define-config.d.ts","names":[],"sources":["../../src/lib/define-config.ts"],"mappings":";;;;;;AAyBoB;AAYI;AACR;AAAf,KADI,mBACJ,CAAA,OAAA,CAAA,GAAA,cAAA,CAAe,OAAf,CAAA,SAAA,IAAA,GAAA,CACI,OADJ,CAAA,SAAA,CAAA;EACI,YAAA,EAAA,UAAA;AAAO,CAAA,CAAA,GAAA,KAAA,GAAA,IAAA,GAAA,KAAA;AAiBZ;AAqBA;AAAwB;AACH;AAApB;AACkB;AAAf;AACC;AAAU;AACV;AACD,KA1BQ,oBAAA,GA0BR,+QAAA;AAAU;AAAY;AAAC;AAoBH;AAAa;AACJ;AAAI;AAEnC;AAC8B;AAAI;AAAS;AAoC7C;AAA4B;AACR;AACG;AACA;AAQf,KA5EI,YA4EJ,CAAA,IAAA,EAAA,OAAA,CAAA,GA3EP,mBA2EO,CA3Ea,OA2Eb,CAAA,SAAA,IAAA,GA1EJ,cA0EI,CA1EW,IA0EX,CAAA,SAAA,IAAA,GAzEH,OAyEG,GAzEO,YAyEP,GAxEH,oBAwEG,GAvEJ,OAuEI,GAvEM,YAuEN;AAAO;AAIS;AAAM;AAAnB;AAIA;AAAU;AAAmC;AAApB;AACX;AAAf;AACC;AAAM;AAAS;AAAtB;AAAM;AA4BV;AAA6B;AACpB;AACA,KA3FJ,mBA2FI,CAAA,OAAA,CAAA,GAAA,CA3F4B,OA2F5B,SAAA;EACN,SAAA,EAAA,KAAA,EAAA;AAAoB,CAAA,GAAA;EA0KP,SAAA,EAAA,iBArQiB,IAAI;gBAEnC;;;4BAC8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCpB,gCACI,kEACG,4DACA;SAQf,OAAO;YAIJ,aAAa,MAAM;YAInB,UAAU,eAAe,oBAAoB;WAC9C,eAAe;IACrB,OAAO,MAAM,SAAS;;;;;;;;iBA4BV,aAAA,SACP,gBACA,eACN;;;;;;iBA0Ka,eAAA"}
@@ -1,5 +1,6 @@
1
1
  import { ConfigValidationError } from "./errors.js";
2
2
  import { parseBranchTtl } from "./duration.js";
3
+ import { normalizeExternalPackage } from "./external-packages.js";
3
4
  import { branchTuningSchema, configInputSchema, formatZodIssues } from "./schema.js";
4
5
  //#region src/lib/define-config.ts
5
6
  /** Default deploy parameters applied to functions that omit them in `neon.ts`. */
@@ -150,7 +151,7 @@ function resolveFunctionConfig(slug, def, tuning) {
150
151
  source: def.source,
151
152
  env: { ...def.env ?? {} },
152
153
  runtime: tuning.runtime ?? DEFAULT_FUNCTION_RUNTIME,
153
- ...def.externalPackages ? { externalPackages: [...def.externalPackages] } : {},
154
+ ...def.externalPackages ? { externalPackages: def.externalPackages.map(normalizeExternalPackage) } : {},
154
155
  ...def.dev ? { dev: def.dev } : {}
155
156
  };
156
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"define-config.js","names":[],"sources":["../../src/lib/define-config.ts"],"sourcesContent":["import { parseBranchTtl } from \"./duration.js\";\nimport { ConfigValidationError } from \"./errors.js\";\nimport {\n\tbranchTuningSchema,\n\tconfigInputSchema,\n\tformatZodIssues,\n} from \"./schema.js\";\nimport type {\n\tBranchTarget,\n\tBranchTuning,\n\tBranchTuningFn,\n\tBucketDef,\n\tConfig,\n\tDataApiInput,\n\tDataApiSettings,\n\tFunctionDef,\n\tFunctionTuning,\n\tPreviewInput,\n\tResolvedBranchConfig,\n\tResolvedDataApiConfig,\n\tResolvedFunctionConfig,\n\tResolvedPreviewConfig,\n\tServiceEnabled,\n\tServiceToggleInput,\n} from \"./types.js\";\n\n/** Default deploy parameters applied to functions that omit them in `neon.ts`. */\nconst DEFAULT_FUNCTION_RUNTIME = \"nodejs24\" as const;\n\nconst REGION_PREFIX = /^(aws|azure|gcp)-/;\n\n/**\n * Whether a `dataApi` toggle is **enabled and verified by Neon Auth** at the type level: it is\n * on (see {@link ServiceEnabled}) and not the explicit `authProvider: \"external\"` variant\n * (so the default / `\"neon\"` provider). This is the case that requires top-level Neon Auth.\n */\ntype DataApiUsesNeonAuth<DataApi> =\n\tServiceEnabled<DataApi> extends true\n\t\t? [DataApi] extends [{ authProvider: \"external\" }]\n\t\t\t? false\n\t\t\t: true\n\t\t: false;\n\n/**\n * Human-readable hint surfaced as the **expected type** of `dataApi` when a Neon-Auth Data\n * API is declared without Neon Auth enabled (see {@link DataApiField}). TypeScript prints the\n * offending value against this string literal — `Type 'true' is not assignable to type\n * '…requires `auth: true`…'` — which points straight at the fix, instead of the opaque\n * `Type 'true' is not assignable to type 'never'` an intersection guard produces.\n *\n * It documents **both** fixes: enabling Neon Auth (`auth: true`), and running the Data API\n * *without* Neon Auth by verifying a third-party IdP (`authProvider: 'external'` + `jwksUrl`).\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type NeonAuthRequiredHint =\n\t\"`dataApi` with Neon Auth (the default `authProvider: 'neon'`) requires Neon Auth, so add `auth: true`. To enable the Data API WITHOUT Neon Auth, verify a third-party IdP instead: `dataApi: { authProvider: 'external', jwksUrl: 'https://your-idp/.well-known/jwks.json' }`\";\n\n/**\n * Static cross-field guard for {@link defineConfig}, expressed as the **type of the `dataApi`\n * field** rather than an intersected requirement on `auth`.\n *\n * - A Neon-Auth Data API (`authProvider: \"neon\"`, the default) with top-level `auth` enabled,\n * or any external Data API: the field keeps its normal `DataApi & DataApiInput` type (the\n * `& DataApiInput` preserves member autocomplete; the `const DataApi` still types the\n * returned {@link Config}).\n * - A Neon-Auth Data API **without** `auth` enabled: the field's expected type collapses to\n * the {@link NeonAuthRequiredHint} message, so the author sees the rule (and the two fixes)\n * right on the `dataApi` value.\n *\n * The runtime `superRefine` in {@link configInputSchema} enforces the same invariant for\n * non-typed (plain-JS) callers, so the behavior is identical — only the type-level message\n * changes.\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type DataApiField<Auth, DataApi> =\n\tDataApiUsesNeonAuth<DataApi> extends true\n\t\t? ServiceEnabled<Auth> extends true\n\t\t\t? DataApi & DataApiInput\n\t\t\t: NeonAuthRequiredHint\n\t\t: DataApi & DataApiInput;\n\n/**\n * Autocomplete bridge for the nested `preview.functions` / `preview.buckets` slug objects.\n *\n * {@link PreviewInput} types those records with a string index signature\n * (`Record<string, FunctionDef>` / `Record<string, BucketDef>`). When `defineConfig` infers\n * `const Preview`, every authored slug becomes a **named** property on the inferred literal\n * (e.g. `{ hello: { name; source } }`), and a named property **shadows** the index signature\n * when the editor computes the contextual type of that slug's value — so the rest of\n * {@link FunctionDef} / {@link BucketDef} (`env`, `dev`, `access`, …) never surfaces as\n * completions inside `hello: { … }` / `uploads: { … }`.\n *\n * Re-declaring each inferred slug's value as `FunctionDef` / `BucketDef` (a *named* member, via\n * a mapped type over the already-inferred keys) puts those members back onto the contextual\n * type without going through an index signature, which restores autocomplete. Intersected with\n * `Preview & PreviewInput` it neither widens what is accepted (the values were already\n * `FunctionDef` / `BucketDef`) nor perturbs the inferred `const Preview` — so slug inference for\n * `BranchTuningFn<Preview>` and the returned {@link Config} is unchanged.\n */\ntype PreviewAutocomplete<Preview> = (Preview extends { functions: infer F }\n\t? { functions: { [Slug in keyof F]: FunctionDef } }\n\t: unknown) &\n\t(Preview extends { buckets: infer B }\n\t\t? { buckets: { [Name in keyof B]: BucketDef } }\n\t\t: unknown);\n\n/**\n * Validate and freeze a Neon branch policy.\n *\n * Used at the top of `neon.ts`:\n * ```ts\n * import { defineConfig } from \"@neon/config/v1\";\n *\n * export default defineConfig({\n * auth: true,\n * preview: {\n * functions: {\n * hello: { name: \"Hello\", source: \"./functions/hello.ts\", dev: { port: 8787 } },\n * },\n * },\n * branch: (branch) => ({ protected: branch.name === \"main\" }),\n * });\n * ```\n *\n * The policy is split into a **static** existential set (top-level `auth` / `dataApi`\n * toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The\n * static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`\n * are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function\n * deploy settings), never change what exists.\n *\n * The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch\n * being decided for (not a live handle); switch on its facts (`branch.name`,\n * `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two\n * modes: against an existing branch (fields populated from Neon) and during pre-create\n * evaluation (`exists: false`, `id` undefined).\n *\n * Pure: no I/O, no side effects. The static parts are validated here; the closure's output\n * is validated every time it is evaluated so errors point at the concrete branch target.\n */\nexport function defineConfig<\n\tconst Auth extends ServiceToggleInput | undefined = undefined,\n\tconst DataApi extends DataApiInput | undefined = undefined,\n\tconst Preview extends PreviewInput | undefined = undefined,\n>(input: {\n\t// Each field is intersected with its concrete interface (not just typed as the bare\n\t// generic). The generic alone — e.g. `preview?: Preview` — gives editors no members to\n\t// complete against in the object-literal position (they see `{} | undefined`), so you\n\t// lose hints for `aiGateway` / `functions` / `buckets`. `& PreviewInput` restores the\n\t// full shape for autocomplete while still inferring the `const` literal that types the\n\t// `branch` closure's slugs (BranchTuningFn<Preview>) and the returned Config.\n\tauth?: Auth & ServiceToggleInput;\n\t// The `dataApi` field carries the Neon-Auth cross-field guard at the type level (see\n\t// `DataApiField`): a Neon-Auth Data API without `auth` enabled surfaces a readable hint\n\t// as the field's expected type instead of collapsing the value to `never`.\n\tdataApi?: DataApiField<Auth, DataApi>;\n\t// `& PreviewInput` restores top-level member hints (aiGateway/functions/buckets);\n\t// `& PreviewAutocomplete<Preview>` restores hints *inside* each function/bucket slug\n\t// object (see `PreviewAutocomplete`), which the bare index signature otherwise hides.\n\tpreview?: Preview & PreviewInput & PreviewAutocomplete<Preview>;\n\tbranch?: BranchTuningFn<Preview>;\n}): Config<Auth, DataApi, Preview> {\n\tif (typeof input === \"function\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig now expects an object, not a function: `export default defineConfig({ auth: true, preview: { … }, branch: (branch) => ({ … }) })`.\",\n\t\t\t\"The static services/preview set moved to the top level; per-branch logic moved into the `branch` closure.\",\n\t\t]);\n\t}\n\tif (input === null || typeof input !== \"object\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig expects a configuration object: `export default defineConfig({ … })`.\",\n\t\t]);\n\t}\n\n\tconst parsed = configInputSchema.safeParse(input);\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\n\treturn Object.freeze({ ...input }) as Config<Auth, DataApi, Preview>;\n}\n\n/**\n * Evaluate a branch policy for a specific branch target and return a normalized config.\n *\n * Merges the static existential set (services + preview functions/buckets) with the\n * per-branch tuning returned by the `branch` closure into the same {@link\n * ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.\n */\nexport function resolveConfig(\n\tconfig: Config,\n\tbranch: BranchTarget,\n): ResolvedBranchConfig {\n\tconst tuning = evaluateBranchTuning(config.branch, branch);\n\n\tconst resolved: ResolvedBranchConfig = {\n\t\tauthEnabled: isServiceEnabled(config.auth),\n\t\tdataApiEnabled: isDataApiEnabled(config.dataApi),\n\t};\n\tconst dataApi = resolveDataApi(config.dataApi);\n\tif (dataApi) resolved.dataApi = dataApi;\n\tif (tuning.parent !== undefined) resolved.parent = tuning.parent;\n\tif (tuning.ttl !== undefined) {\n\t\t// `branchTuningSchema` already validated `ttl` with the same `parseBranchTtl`, so\n\t\t// this only converts the validated value to seconds — it cannot fail here.\n\t\tconst parsedTtl = parseBranchTtl(tuning.ttl);\n\t\tif (!(\"error\" in parsedTtl)) resolved.ttlSeconds = parsedTtl.seconds;\n\t}\n\tif (tuning.protected !== undefined) resolved.protected = tuning.protected;\n\tif (tuning.postgres?.computeSettings) {\n\t\tresolved.postgres = {\n\t\t\tcomputeSettings: { ...tuning.postgres.computeSettings },\n\t\t};\n\t}\n\n\tconst preview = resolvePreviewConfig(config.preview, tuning);\n\tif (preview) resolved.preview = preview;\n\n\treturn resolved;\n}\n\n/**\n * Run the `branch` closure (when present) for the target and validate its output. The\n * closure is optional — a fully static policy resolves with empty tuning.\n */\nfunction evaluateBranchTuning(\n\tbranchFn: BranchTuningFn | undefined,\n\ttarget: BranchTarget,\n): BranchTuning {\n\tif (!branchFn) return {};\n\tlet raw: unknown;\n\ttry {\n\t\traw = branchFn(Object.freeze({ ...target }));\n\t} catch (cause) {\n\t\tthrow new ConfigValidationError([\n\t\t\t`Branch policy threw while evaluating branch \"${target.name}\".`,\n\t\t\t(cause as Error)?.message ?? String(cause),\n\t\t]);\n\t}\n\tconst parsed = branchTuningSchema.safeParse(raw ?? {});\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\treturn parsed.data as BranchTuning;\n}\n\nfunction isServiceEnabled(toggle: ServiceToggleInput | undefined): boolean {\n\tif (toggle === undefined) return false;\n\tif (typeof toggle === \"boolean\") return toggle;\n\treturn toggle.enabled !== false;\n}\n\n/** Whether a {@link DataApiInput} is enabled (present object/`true` unless `enabled: false`). */\nfunction isDataApiEnabled(input: DataApiInput | undefined): boolean {\n\tif (input === undefined) return false;\n\tif (typeof input === \"boolean\") return input;\n\treturn input.enabled !== false;\n}\n\n/**\n * Normalize a {@link DataApiInput} into a {@link ResolvedDataApiConfig}, or `undefined` when\n * the Data API is not enabled. `authProvider` defaults to `\"neon\"`; the external-IdP wiring\n * is carried through only for the `\"external\"` provider; `settings` is copied with its\n * `undefined` entries dropped so diffing only considers fields the policy actually set.\n */\nfunction resolveDataApi(\n\tinput: DataApiInput | undefined,\n): ResolvedDataApiConfig | undefined {\n\tif (!isDataApiEnabled(input)) return undefined;\n\tif (typeof input !== \"object\") {\n\t\t// Bare `true`: enabled with Neon Auth and all-default settings.\n\t\treturn { authProvider: \"neon\" };\n\t}\n\tconst authProvider = input.authProvider ?? \"neon\";\n\tconst resolved: ResolvedDataApiConfig = { authProvider };\n\tif (authProvider === \"external\") {\n\t\tif (input.jwksUrl !== undefined) resolved.jwksUrl = input.jwksUrl;\n\t\tif (input.providerName !== undefined)\n\t\t\tresolved.providerName = input.providerName;\n\t\tif (input.jwtAudience !== undefined)\n\t\t\tresolved.jwtAudience = input.jwtAudience;\n\t}\n\tconst settings = normalizeDataApiSettings(input.settings);\n\tif (settings) resolved.settings = settings;\n\treturn resolved;\n}\n\n/** Copy a {@link DataApiSettings}, dropping `undefined` entries; `undefined` when empty. */\nfunction normalizeDataApiSettings(\n\tsettings: DataApiSettings | undefined,\n): DataApiSettings | undefined {\n\tif (!settings) return undefined;\n\tconst out: DataApiSettings = {};\n\tfor (const [key, value] of Object.entries(settings)) {\n\t\tif (value !== undefined) {\n\t\t\t(out as Record<string, unknown>)[key] = value;\n\t\t}\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * Normalize the static {@link PreviewInput} (merged with per-branch function tuning) into a\n * {@link ResolvedPreviewConfig}. Returns `undefined` when the policy declares no `preview`\n * block so the field can be omitted entirely. Function slugs / bucket names come from the\n * record keys.\n */\nfunction resolvePreviewConfig(\n\tpreview: PreviewInput | undefined,\n\ttuning: BranchTuning,\n): ResolvedPreviewConfig | undefined {\n\tif (!preview) return undefined;\n\tconst fnTuning = tuning.preview?.functions ?? {};\n\tconst functions: ResolvedFunctionConfig[] = Object.entries(\n\t\tpreview.functions ?? {},\n\t).map(([slug, def]) =>\n\t\tresolveFunctionConfig(slug, def, fnTuning[slug] ?? {}),\n\t);\n\tconst buckets = Object.entries(preview.buckets ?? {}).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\taccess: def.access ?? \"private\",\n\t\t}),\n\t);\n\treturn {\n\t\tfunctions,\n\t\tbuckets,\n\t\taiGatewayEnabled: isServiceEnabled(preview.aiGateway),\n\t};\n}\n\nfunction resolveFunctionConfig(\n\tslug: string,\n\tdef: FunctionDef,\n\ttuning: FunctionTuning,\n): ResolvedFunctionConfig {\n\treturn {\n\t\tslug,\n\t\tname: def.name,\n\t\tsource: def.source,\n\t\tenv: { ...(def.env ?? {}) },\n\t\truntime: tuning.runtime ?? DEFAULT_FUNCTION_RUNTIME,\n\t\t// Copied only when declared, so a policy without it resolves unchanged. Both\n\t\t// bundlers read it; `neon dev` mirrors it so a local run bundles like a deploy.\n\t\t...(def.externalPackages\n\t\t\t? { externalPackages: [...def.externalPackages] }\n\t\t\t: {}),\n\t\t// Passed through untouched (no defaults); only `neon dev` reads it.\n\t\t...(def.dev ? { dev: def.dev } : {}),\n\t};\n}\n\n/**\n * Normalize a region identifier to Neon's `<cloud>-<region>` format. When the user writes\n * `us-east-1` we assume `aws-us-east-1`. Pure helper used by both the validator and the\n * NeonApi adapter.\n */\nexport function normalizeRegion(region: string): string {\n\tif (REGION_PREFIX.test(region)) return region;\n\treturn `aws-${region}`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"define-config.js","names":[],"sources":["../../src/lib/define-config.ts"],"sourcesContent":["import { parseBranchTtl } from \"./duration.js\";\nimport { ConfigValidationError } from \"./errors.js\";\nimport { normalizeExternalPackage } from \"./external-packages.js\";\nimport {\n\tbranchTuningSchema,\n\tconfigInputSchema,\n\tformatZodIssues,\n} from \"./schema.js\";\nimport type {\n\tBranchTarget,\n\tBranchTuning,\n\tBranchTuningFn,\n\tBucketDef,\n\tConfig,\n\tDataApiInput,\n\tDataApiSettings,\n\tFunctionDef,\n\tFunctionTuning,\n\tPreviewInput,\n\tResolvedBranchConfig,\n\tResolvedDataApiConfig,\n\tResolvedFunctionConfig,\n\tResolvedPreviewConfig,\n\tServiceEnabled,\n\tServiceToggleInput,\n} from \"./types.js\";\n\n/** Default deploy parameters applied to functions that omit them in `neon.ts`. */\nconst DEFAULT_FUNCTION_RUNTIME = \"nodejs24\" as const;\n\nconst REGION_PREFIX = /^(aws|azure|gcp)-/;\n\n/**\n * Whether a `dataApi` toggle is **enabled and verified by Neon Auth** at the type level: it is\n * on (see {@link ServiceEnabled}) and not the explicit `authProvider: \"external\"` variant\n * (so the default / `\"neon\"` provider). This is the case that requires top-level Neon Auth.\n */\ntype DataApiUsesNeonAuth<DataApi> =\n\tServiceEnabled<DataApi> extends true\n\t\t? [DataApi] extends [{ authProvider: \"external\" }]\n\t\t\t? false\n\t\t\t: true\n\t\t: false;\n\n/**\n * Human-readable hint surfaced as the **expected type** of `dataApi` when a Neon-Auth Data\n * API is declared without Neon Auth enabled (see {@link DataApiField}). TypeScript prints the\n * offending value against this string literal — `Type 'true' is not assignable to type\n * '…requires `auth: true`…'` — which points straight at the fix, instead of the opaque\n * `Type 'true' is not assignable to type 'never'` an intersection guard produces.\n *\n * It documents **both** fixes: enabling Neon Auth (`auth: true`), and running the Data API\n * *without* Neon Auth by verifying a third-party IdP (`authProvider: 'external'` + `jwksUrl`).\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type NeonAuthRequiredHint =\n\t\"`dataApi` with Neon Auth (the default `authProvider: 'neon'`) requires Neon Auth, so add `auth: true`. To enable the Data API WITHOUT Neon Auth, verify a third-party IdP instead: `dataApi: { authProvider: 'external', jwksUrl: 'https://your-idp/.well-known/jwks.json' }`\";\n\n/**\n * Static cross-field guard for {@link defineConfig}, expressed as the **type of the `dataApi`\n * field** rather than an intersected requirement on `auth`.\n *\n * - A Neon-Auth Data API (`authProvider: \"neon\"`, the default) with top-level `auth` enabled,\n * or any external Data API: the field keeps its normal `DataApi & DataApiInput` type (the\n * `& DataApiInput` preserves member autocomplete; the `const DataApi` still types the\n * returned {@link Config}).\n * - A Neon-Auth Data API **without** `auth` enabled: the field's expected type collapses to\n * the {@link NeonAuthRequiredHint} message, so the author sees the rule (and the two fixes)\n * right on the `dataApi` value.\n *\n * The runtime `superRefine` in {@link configInputSchema} enforces the same invariant for\n * non-typed (plain-JS) callers, so the behavior is identical — only the type-level message\n * changes.\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type DataApiField<Auth, DataApi> =\n\tDataApiUsesNeonAuth<DataApi> extends true\n\t\t? ServiceEnabled<Auth> extends true\n\t\t\t? DataApi & DataApiInput\n\t\t\t: NeonAuthRequiredHint\n\t\t: DataApi & DataApiInput;\n\n/**\n * Autocomplete bridge for the nested `preview.functions` / `preview.buckets` slug objects.\n *\n * {@link PreviewInput} types those records with a string index signature\n * (`Record<string, FunctionDef>` / `Record<string, BucketDef>`). When `defineConfig` infers\n * `const Preview`, every authored slug becomes a **named** property on the inferred literal\n * (e.g. `{ hello: { name; source } }`), and a named property **shadows** the index signature\n * when the editor computes the contextual type of that slug's value — so the rest of\n * {@link FunctionDef} / {@link BucketDef} (`env`, `dev`, `access`, …) never surfaces as\n * completions inside `hello: { … }` / `uploads: { … }`.\n *\n * Re-declaring each inferred slug's value as `FunctionDef` / `BucketDef` (a *named* member, via\n * a mapped type over the already-inferred keys) puts those members back onto the contextual\n * type without going through an index signature, which restores autocomplete. Intersected with\n * `Preview & PreviewInput` it neither widens what is accepted (the values were already\n * `FunctionDef` / `BucketDef`) nor perturbs the inferred `const Preview` — so slug inference for\n * `BranchTuningFn<Preview>` and the returned {@link Config} is unchanged.\n */\ntype PreviewAutocomplete<Preview> = (Preview extends { functions: infer F }\n\t? { functions: { [Slug in keyof F]: FunctionDef } }\n\t: unknown) &\n\t(Preview extends { buckets: infer B }\n\t\t? { buckets: { [Name in keyof B]: BucketDef } }\n\t\t: unknown);\n\n/**\n * Validate and freeze a Neon branch policy.\n *\n * Used at the top of `neon.ts`:\n * ```ts\n * import { defineConfig } from \"@neon/config/v1\";\n *\n * export default defineConfig({\n * auth: true,\n * preview: {\n * functions: {\n * hello: { name: \"Hello\", source: \"./functions/hello.ts\", dev: { port: 8787 } },\n * },\n * },\n * branch: (branch) => ({ protected: branch.name === \"main\" }),\n * });\n * ```\n *\n * The policy is split into a **static** existential set (top-level `auth` / `dataApi`\n * toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The\n * static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`\n * are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function\n * deploy settings), never change what exists.\n *\n * The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch\n * being decided for (not a live handle); switch on its facts (`branch.name`,\n * `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two\n * modes: against an existing branch (fields populated from Neon) and during pre-create\n * evaluation (`exists: false`, `id` undefined).\n *\n * Pure: no I/O, no side effects. The static parts are validated here; the closure's output\n * is validated every time it is evaluated so errors point at the concrete branch target.\n */\nexport function defineConfig<\n\tconst Auth extends ServiceToggleInput | undefined = undefined,\n\tconst DataApi extends DataApiInput | undefined = undefined,\n\tconst Preview extends PreviewInput | undefined = undefined,\n>(input: {\n\t// Each field is intersected with its concrete interface (not just typed as the bare\n\t// generic). The generic alone — e.g. `preview?: Preview` — gives editors no members to\n\t// complete against in the object-literal position (they see `{} | undefined`), so you\n\t// lose hints for `aiGateway` / `functions` / `buckets`. `& PreviewInput` restores the\n\t// full shape for autocomplete while still inferring the `const` literal that types the\n\t// `branch` closure's slugs (BranchTuningFn<Preview>) and the returned Config.\n\tauth?: Auth & ServiceToggleInput;\n\t// The `dataApi` field carries the Neon-Auth cross-field guard at the type level (see\n\t// `DataApiField`): a Neon-Auth Data API without `auth` enabled surfaces a readable hint\n\t// as the field's expected type instead of collapsing the value to `never`.\n\tdataApi?: DataApiField<Auth, DataApi>;\n\t// `& PreviewInput` restores top-level member hints (aiGateway/functions/buckets);\n\t// `& PreviewAutocomplete<Preview>` restores hints *inside* each function/bucket slug\n\t// object (see `PreviewAutocomplete`), which the bare index signature otherwise hides.\n\tpreview?: Preview & PreviewInput & PreviewAutocomplete<Preview>;\n\tbranch?: BranchTuningFn<Preview>;\n}): Config<Auth, DataApi, Preview> {\n\tif (typeof input === \"function\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig now expects an object, not a function: `export default defineConfig({ auth: true, preview: { … }, branch: (branch) => ({ … }) })`.\",\n\t\t\t\"The static services/preview set moved to the top level; per-branch logic moved into the `branch` closure.\",\n\t\t]);\n\t}\n\tif (input === null || typeof input !== \"object\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig expects a configuration object: `export default defineConfig({ … })`.\",\n\t\t]);\n\t}\n\n\tconst parsed = configInputSchema.safeParse(input);\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\n\treturn Object.freeze({ ...input }) as Config<Auth, DataApi, Preview>;\n}\n\n/**\n * Evaluate a branch policy for a specific branch target and return a normalized config.\n *\n * Merges the static existential set (services + preview functions/buckets) with the\n * per-branch tuning returned by the `branch` closure into the same {@link\n * ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.\n */\nexport function resolveConfig(\n\tconfig: Config,\n\tbranch: BranchTarget,\n): ResolvedBranchConfig {\n\tconst tuning = evaluateBranchTuning(config.branch, branch);\n\n\tconst resolved: ResolvedBranchConfig = {\n\t\tauthEnabled: isServiceEnabled(config.auth),\n\t\tdataApiEnabled: isDataApiEnabled(config.dataApi),\n\t};\n\tconst dataApi = resolveDataApi(config.dataApi);\n\tif (dataApi) resolved.dataApi = dataApi;\n\tif (tuning.parent !== undefined) resolved.parent = tuning.parent;\n\tif (tuning.ttl !== undefined) {\n\t\t// `branchTuningSchema` already validated `ttl` with the same `parseBranchTtl`, so\n\t\t// this only converts the validated value to seconds — it cannot fail here.\n\t\tconst parsedTtl = parseBranchTtl(tuning.ttl);\n\t\tif (!(\"error\" in parsedTtl)) resolved.ttlSeconds = parsedTtl.seconds;\n\t}\n\tif (tuning.protected !== undefined) resolved.protected = tuning.protected;\n\tif (tuning.postgres?.computeSettings) {\n\t\tresolved.postgres = {\n\t\t\tcomputeSettings: { ...tuning.postgres.computeSettings },\n\t\t};\n\t}\n\n\tconst preview = resolvePreviewConfig(config.preview, tuning);\n\tif (preview) resolved.preview = preview;\n\n\treturn resolved;\n}\n\n/**\n * Run the `branch` closure (when present) for the target and validate its output. The\n * closure is optional — a fully static policy resolves with empty tuning.\n */\nfunction evaluateBranchTuning(\n\tbranchFn: BranchTuningFn | undefined,\n\ttarget: BranchTarget,\n): BranchTuning {\n\tif (!branchFn) return {};\n\tlet raw: unknown;\n\ttry {\n\t\traw = branchFn(Object.freeze({ ...target }));\n\t} catch (cause) {\n\t\tthrow new ConfigValidationError([\n\t\t\t`Branch policy threw while evaluating branch \"${target.name}\".`,\n\t\t\t(cause as Error)?.message ?? String(cause),\n\t\t]);\n\t}\n\tconst parsed = branchTuningSchema.safeParse(raw ?? {});\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\treturn parsed.data as BranchTuning;\n}\n\nfunction isServiceEnabled(toggle: ServiceToggleInput | undefined): boolean {\n\tif (toggle === undefined) return false;\n\tif (typeof toggle === \"boolean\") return toggle;\n\treturn toggle.enabled !== false;\n}\n\n/** Whether a {@link DataApiInput} is enabled (present object/`true` unless `enabled: false`). */\nfunction isDataApiEnabled(input: DataApiInput | undefined): boolean {\n\tif (input === undefined) return false;\n\tif (typeof input === \"boolean\") return input;\n\treturn input.enabled !== false;\n}\n\n/**\n * Normalize a {@link DataApiInput} into a {@link ResolvedDataApiConfig}, or `undefined` when\n * the Data API is not enabled. `authProvider` defaults to `\"neon\"`; the external-IdP wiring\n * is carried through only for the `\"external\"` provider; `settings` is copied with its\n * `undefined` entries dropped so diffing only considers fields the policy actually set.\n */\nfunction resolveDataApi(\n\tinput: DataApiInput | undefined,\n): ResolvedDataApiConfig | undefined {\n\tif (!isDataApiEnabled(input)) return undefined;\n\tif (typeof input !== \"object\") {\n\t\t// Bare `true`: enabled with Neon Auth and all-default settings.\n\t\treturn { authProvider: \"neon\" };\n\t}\n\tconst authProvider = input.authProvider ?? \"neon\";\n\tconst resolved: ResolvedDataApiConfig = { authProvider };\n\tif (authProvider === \"external\") {\n\t\tif (input.jwksUrl !== undefined) resolved.jwksUrl = input.jwksUrl;\n\t\tif (input.providerName !== undefined)\n\t\t\tresolved.providerName = input.providerName;\n\t\tif (input.jwtAudience !== undefined)\n\t\t\tresolved.jwtAudience = input.jwtAudience;\n\t}\n\tconst settings = normalizeDataApiSettings(input.settings);\n\tif (settings) resolved.settings = settings;\n\treturn resolved;\n}\n\n/** Copy a {@link DataApiSettings}, dropping `undefined` entries; `undefined` when empty. */\nfunction normalizeDataApiSettings(\n\tsettings: DataApiSettings | undefined,\n): DataApiSettings | undefined {\n\tif (!settings) return undefined;\n\tconst out: DataApiSettings = {};\n\tfor (const [key, value] of Object.entries(settings)) {\n\t\tif (value !== undefined) {\n\t\t\t(out as Record<string, unknown>)[key] = value;\n\t\t}\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * Normalize the static {@link PreviewInput} (merged with per-branch function tuning) into a\n * {@link ResolvedPreviewConfig}. Returns `undefined` when the policy declares no `preview`\n * block so the field can be omitted entirely. Function slugs / bucket names come from the\n * record keys.\n */\nfunction resolvePreviewConfig(\n\tpreview: PreviewInput | undefined,\n\ttuning: BranchTuning,\n): ResolvedPreviewConfig | undefined {\n\tif (!preview) return undefined;\n\tconst fnTuning = tuning.preview?.functions ?? {};\n\tconst functions: ResolvedFunctionConfig[] = Object.entries(\n\t\tpreview.functions ?? {},\n\t).map(([slug, def]) =>\n\t\tresolveFunctionConfig(slug, def, fnTuning[slug] ?? {}),\n\t);\n\tconst buckets = Object.entries(preview.buckets ?? {}).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\taccess: def.access ?? \"private\",\n\t\t}),\n\t);\n\treturn {\n\t\tfunctions,\n\t\tbuckets,\n\t\taiGatewayEnabled: isServiceEnabled(preview.aiGateway),\n\t};\n}\n\nfunction resolveFunctionConfig(\n\tslug: string,\n\tdef: FunctionDef,\n\ttuning: FunctionTuning,\n): ResolvedFunctionConfig {\n\treturn {\n\t\tslug,\n\t\tname: def.name,\n\t\tsource: def.source,\n\t\tenv: { ...(def.env ?? {}) },\n\t\truntime: tuning.runtime ?? DEFAULT_FUNCTION_RUNTIME,\n\t\t// Normalized only when declared, so a policy without it resolves unchanged and takes\n\t\t// the pre-existing bundling path. Both bundlers read it; `neon dev` mirrors it so a\n\t\t// local run leaves the same packages unbundled as a deploy.\n\t\t...(def.externalPackages\n\t\t\t? {\n\t\t\t\t\texternalPackages: def.externalPackages.map(\n\t\t\t\t\t\tnormalizeExternalPackage,\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: {}),\n\t\t// Passed through untouched (no defaults); only `neon dev` reads it.\n\t\t...(def.dev ? { dev: def.dev } : {}),\n\t};\n}\n\n/**\n * Normalize a region identifier to Neon's `<cloud>-<region>` format. When the user writes\n * `us-east-1` we assume `aws-us-east-1`. Pure helper used by both the validator and the\n * NeonApi adapter.\n */\nexport function normalizeRegion(region: string): string {\n\tif (REGION_PREFIX.test(region)) return region;\n\treturn `aws-${region}`;\n}\n"],"mappings":"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"}
@@ -34,8 +34,8 @@ const UNIT_SECONDS = {
34
34
  s: 1,
35
35
  m: 60,
36
36
  h: 3600,
37
- d: 1440 * 60,
38
- w: 10080 * 60
37
+ d: 86400,
38
+ w: 604800
39
39
  };
40
40
  /** Neon's branch-expiration ceiling: the API rejects an `expires_at` more than 30 days out. */
41
41
  const MAX_BRANCH_TTL_SECONDS = 30 * UNIT_SECONDS.d;
@@ -1 +1 @@
1
- {"version":3,"file":"duration.js","names":[],"sources":["../../src/lib/duration.ts"],"sourcesContent":["import type { DurationString } from \"./types.js\";\n\n/**\n * Parse a duration value into whole seconds.\n *\n * Accepted formats:\n * - a positive finite **number** → interpreted as seconds (must be an integer)\n * - a **string** of the form `<integer><unit>` where unit is one of `s`, `m`, `h`, `d`, `w`\n * (e.g. `30s`, `5m`, `1h`, `7d`, `2w`)\n *\n * A **unit is required** on strings: a bare numeric string like `\"7\"` is rejected — pass a\n * `number` (`7`) for raw seconds instead. This removes the ambiguity where `\"7\"` silently\n * meant 7 seconds rather than, say, `\"7d\"`.\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseDuration(\n\tinput: string | number,\n): { seconds: number } | { error: string } {\n\tif (typeof input === \"number\") {\n\t\tif (!Number.isFinite(input))\n\t\t\treturn { error: `not a finite number: ${input}` };\n\t\tif (!Number.isInteger(input))\n\t\t\treturn {\n\t\t\t\terror: `must be an integer when passed as number: ${input}`,\n\t\t\t};\n\t\tif (input <= 0) return { error: `must be > 0, got ${input}` };\n\t\treturn { seconds: input };\n\t}\n\n\tconst trimmed = input.trim();\n\tif (trimmed === \"\") return { error: \"duration string is empty\" };\n\n\t// A bare numeric string is rejected on purpose: pass a number for raw seconds, or add a\n\t// unit (e.g. \"7d\"). Detected explicitly so we can give a targeted hint instead of the\n\t// generic \"invalid duration\" message.\n\tif (/^\\d+$/.test(trimmed)) {\n\t\treturn {\n\t\t\terror: `duration string \"${input}\" is missing a unit; add one of s, m, h, d, w (e.g. \"${trimmed}d\") or pass ${trimmed} as a number for seconds`,\n\t\t};\n\t}\n\n\tconst unitMatch = /^(\\d+)([smhdw])$/i.exec(trimmed);\n\tif (!unitMatch) {\n\t\treturn {\n\t\t\terror: `invalid duration \"${input}\"; expected an integer followed by one of: s, m, h, d, w (e.g. \"30s\", \"1h\", \"7d\")`,\n\t\t};\n\t}\n\n\tconst value = Number(unitMatch[1]);\n\tconst unit = unitMatch[2].toLowerCase() as \"s\" | \"m\" | \"h\" | \"d\" | \"w\";\n\tif (value <= 0) return { error: `must be > 0, got \"${trimmed}\"` };\n\n\tconst seconds = value * UNIT_SECONDS[unit];\n\treturn { seconds };\n}\n\nconst UNIT_SECONDS = {\n\ts: 1,\n\tm: 60,\n\th: 60 * 60,\n\td: 24 * 60 * 60,\n\tw: 7 * 24 * 60 * 60,\n} as const;\n\n/** Neon's branch-expiration ceiling: the API rejects an `expires_at` more than 30 days out. */\nexport const MAX_BRANCH_TTL_SECONDS = 30 * UNIT_SECONDS.d;\n\n/**\n * Parse a branch TTL into seconds, enforcing Neon's branch-expiration limit on top of the\n * shared {@link parseDuration} rules: the result must be `> 0` and at most 30 days\n * ({@link MAX_BRANCH_TTL_SECONDS}), since the API caps `expires_at` at 30 days from now.\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseBranchTtl(\n\tinput: string | number,\n): { seconds: number } | { error: string } {\n\tconst result = parseDuration(input);\n\tif (\"error\" in result) return result;\n\tif (result.seconds > MAX_BRANCH_TTL_SECONDS) {\n\t\treturn {\n\t\t\terror: `branch TTL must be at most 30 days (${MAX_BRANCH_TTL_SECONDS}s), got ${result.seconds}s`,\n\t\t};\n\t}\n\treturn result;\n}\n\n/**\n * Render a TTL in seconds back to the canonical \"<n><unit>\" form. Used for round-trip\n * serialization when {@link pullConfig} emits a TTL value (it always falls back to seconds\n * when no clean unit boundary matches). The output always carries a unit, so it is a valid\n * {@link DurationString}.\n */\nexport function formatDurationSeconds(totalSeconds: number): DurationString {\n\tif (!Number.isFinite(totalSeconds) || totalSeconds <= 0) {\n\t\tthrow new RangeError(\n\t\t\t`formatDurationSeconds expected a positive finite number, got ${totalSeconds}`,\n\t\t);\n\t}\n\tconst candidates = [\n\t\t[\"w\", UNIT_SECONDS.w],\n\t\t[\"d\", UNIT_SECONDS.d],\n\t\t[\"h\", UNIT_SECONDS.h],\n\t\t[\"m\", UNIT_SECONDS.m],\n\t] as const;\n\tfor (const [unit, perUnit] of candidates) {\n\t\tif (totalSeconds % perUnit === 0) {\n\t\t\treturn `${totalSeconds / perUnit}${unit}`;\n\t\t}\n\t}\n\treturn `${totalSeconds}s`;\n}\n\n/**\n * Parse a suspend timeout value into seconds for the Neon API.\n *\n * Accepted formats:\n * - `false` → -1 (never suspend)\n * - `undefined` → 0 (use platform default)\n * - duration string → parsed seconds (\"5m\", \"1h\", \"7d\")\n * - number → validated seconds (must be 60-604800 or -1/0)\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseSuspendTimeout(\n\tinput: false | string | number | undefined,\n): { seconds: number } | { error: string } {\n\t// false means \"never suspend\"\n\tif (input === false) return { seconds: -1 };\n\n\t// undefined means \"use platform default\"\n\tif (input === undefined) return { seconds: 0 };\n\n\t// If it's a number, validate the range\n\tif (typeof input === \"number\") {\n\t\tif (!Number.isFinite(input))\n\t\t\treturn { error: `not a finite number: ${input}` };\n\t\tif (!Number.isInteger(input))\n\t\t\treturn { error: `must be an integer: ${input}` };\n\n\t\t// Allow special values: -1 (never), 0 (default)\n\t\tif (input === -1 || input === 0) return { seconds: input };\n\n\t\t// Validate range for custom timeout: 60s (1 min) to 604800s (1 week)\n\t\tif (input < 60 || input > 604_800) {\n\t\t\treturn {\n\t\t\t\terror: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), got ${input}`,\n\t\t\t};\n\t\t}\n\t\treturn { seconds: input };\n\t}\n\n\t// Parse duration string\n\tconst result = parseDuration(input);\n\tif (\"error\" in result) return result;\n\n\t// Validate the parsed duration is in the valid range\n\tconst { seconds } = result;\n\tif (seconds < 60 || seconds > 604_800) {\n\t\treturn {\n\t\t\terror: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), \"${input}\" = ${seconds}s`,\n\t\t};\n\t}\n\n\treturn { seconds };\n}\n\n/**\n * Format a suspend timeout value from API seconds back to the user-facing type.\n * Returns `false` for -1 (never suspend), `undefined` for 0 (default), or a duration string.\n */\nexport function formatSuspendTimeout(\n\tseconds: number,\n): false | DurationString | undefined {\n\tif (seconds === -1) return false; // never suspend\n\tif (seconds === 0) return undefined; // platform default\n\treturn formatDurationSeconds(seconds);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"duration.js","names":[],"sources":["../../src/lib/duration.ts"],"sourcesContent":["import type { DurationString } from \"./types.js\";\n\n/**\n * Parse a duration value into whole seconds.\n *\n * Accepted formats:\n * - a positive finite **number** → interpreted as seconds (must be an integer)\n * - a **string** of the form `<integer><unit>` where unit is one of `s`, `m`, `h`, `d`, `w`\n * (e.g. `30s`, `5m`, `1h`, `7d`, `2w`)\n *\n * A **unit is required** on strings: a bare numeric string like `\"7\"` is rejected — pass a\n * `number` (`7`) for raw seconds instead. This removes the ambiguity where `\"7\"` silently\n * meant 7 seconds rather than, say, `\"7d\"`.\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseDuration(\n\tinput: string | number,\n): { seconds: number } | { error: string } {\n\tif (typeof input === \"number\") {\n\t\tif (!Number.isFinite(input))\n\t\t\treturn { error: `not a finite number: ${input}` };\n\t\tif (!Number.isInteger(input))\n\t\t\treturn {\n\t\t\t\terror: `must be an integer when passed as number: ${input}`,\n\t\t\t};\n\t\tif (input <= 0) return { error: `must be > 0, got ${input}` };\n\t\treturn { seconds: input };\n\t}\n\n\tconst trimmed = input.trim();\n\tif (trimmed === \"\") return { error: \"duration string is empty\" };\n\n\t// A bare numeric string is rejected on purpose: pass a number for raw seconds, or add a\n\t// unit (e.g. \"7d\"). Detected explicitly so we can give a targeted hint instead of the\n\t// generic \"invalid duration\" message.\n\tif (/^\\d+$/.test(trimmed)) {\n\t\treturn {\n\t\t\terror: `duration string \"${input}\" is missing a unit; add one of s, m, h, d, w (e.g. \"${trimmed}d\") or pass ${trimmed} as a number for seconds`,\n\t\t};\n\t}\n\n\tconst unitMatch = /^(\\d+)([smhdw])$/i.exec(trimmed);\n\tif (!unitMatch) {\n\t\treturn {\n\t\t\terror: `invalid duration \"${input}\"; expected an integer followed by one of: s, m, h, d, w (e.g. \"30s\", \"1h\", \"7d\")`,\n\t\t};\n\t}\n\n\tconst value = Number(unitMatch[1]);\n\tconst unit = unitMatch[2].toLowerCase() as \"s\" | \"m\" | \"h\" | \"d\" | \"w\";\n\tif (value <= 0) return { error: `must be > 0, got \"${trimmed}\"` };\n\n\tconst seconds = value * UNIT_SECONDS[unit];\n\treturn { seconds };\n}\n\nconst UNIT_SECONDS = {\n\ts: 1,\n\tm: 60,\n\th: 60 * 60,\n\td: 24 * 60 * 60,\n\tw: 7 * 24 * 60 * 60,\n} as const;\n\n/** Neon's branch-expiration ceiling: the API rejects an `expires_at` more than 30 days out. */\nexport const MAX_BRANCH_TTL_SECONDS = 30 * UNIT_SECONDS.d;\n\n/**\n * Parse a branch TTL into seconds, enforcing Neon's branch-expiration limit on top of the\n * shared {@link parseDuration} rules: the result must be `> 0` and at most 30 days\n * ({@link MAX_BRANCH_TTL_SECONDS}), since the API caps `expires_at` at 30 days from now.\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseBranchTtl(\n\tinput: string | number,\n): { seconds: number } | { error: string } {\n\tconst result = parseDuration(input);\n\tif (\"error\" in result) return result;\n\tif (result.seconds > MAX_BRANCH_TTL_SECONDS) {\n\t\treturn {\n\t\t\terror: `branch TTL must be at most 30 days (${MAX_BRANCH_TTL_SECONDS}s), got ${result.seconds}s`,\n\t\t};\n\t}\n\treturn result;\n}\n\n/**\n * Render a TTL in seconds back to the canonical \"<n><unit>\" form. Used for round-trip\n * serialization when {@link pullConfig} emits a TTL value (it always falls back to seconds\n * when no clean unit boundary matches). The output always carries a unit, so it is a valid\n * {@link DurationString}.\n */\nexport function formatDurationSeconds(totalSeconds: number): DurationString {\n\tif (!Number.isFinite(totalSeconds) || totalSeconds <= 0) {\n\t\tthrow new RangeError(\n\t\t\t`formatDurationSeconds expected a positive finite number, got ${totalSeconds}`,\n\t\t);\n\t}\n\tconst candidates = [\n\t\t[\"w\", UNIT_SECONDS.w],\n\t\t[\"d\", UNIT_SECONDS.d],\n\t\t[\"h\", UNIT_SECONDS.h],\n\t\t[\"m\", UNIT_SECONDS.m],\n\t] as const;\n\tfor (const [unit, perUnit] of candidates) {\n\t\tif (totalSeconds % perUnit === 0) {\n\t\t\treturn `${totalSeconds / perUnit}${unit}`;\n\t\t}\n\t}\n\treturn `${totalSeconds}s`;\n}\n\n/**\n * Parse a suspend timeout value into seconds for the Neon API.\n *\n * Accepted formats:\n * - `false` → -1 (never suspend)\n * - `undefined` → 0 (use platform default)\n * - duration string → parsed seconds (\"5m\", \"1h\", \"7d\")\n * - number → validated seconds (must be 60-604800 or -1/0)\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseSuspendTimeout(\n\tinput: false | string | number | undefined,\n): { seconds: number } | { error: string } {\n\t// false means \"never suspend\"\n\tif (input === false) return { seconds: -1 };\n\n\t// undefined means \"use platform default\"\n\tif (input === undefined) return { seconds: 0 };\n\n\t// If it's a number, validate the range\n\tif (typeof input === \"number\") {\n\t\tif (!Number.isFinite(input))\n\t\t\treturn { error: `not a finite number: ${input}` };\n\t\tif (!Number.isInteger(input))\n\t\t\treturn { error: `must be an integer: ${input}` };\n\n\t\t// Allow special values: -1 (never), 0 (default)\n\t\tif (input === -1 || input === 0) return { seconds: input };\n\n\t\t// Validate range for custom timeout: 60s (1 min) to 604800s (1 week)\n\t\tif (input < 60 || input > 604_800) {\n\t\t\treturn {\n\t\t\t\terror: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), got ${input}`,\n\t\t\t};\n\t\t}\n\t\treturn { seconds: input };\n\t}\n\n\t// Parse duration string\n\tconst result = parseDuration(input);\n\tif (\"error\" in result) return result;\n\n\t// Validate the parsed duration is in the valid range\n\tconst { seconds } = result;\n\tif (seconds < 60 || seconds > 604_800) {\n\t\treturn {\n\t\t\terror: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), \"${input}\" = ${seconds}s`,\n\t\t};\n\t}\n\n\treturn { seconds };\n}\n\n/**\n * Format a suspend timeout value from API seconds back to the user-facing type.\n * Returns `false` for -1 (never suspend), `undefined` for 0 (default), or a duration string.\n */\nexport function formatSuspendTimeout(\n\tseconds: number,\n): false | DurationString | undefined {\n\tif (seconds === -1) return false; // never suspend\n\tif (seconds === 0) return undefined; // platform default\n\treturn formatDurationSeconds(seconds);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,SAAgB,cACf,OAC0C;CAC1C,IAAI,OAAO,UAAU,UAAU;EAC9B,IAAI,CAAC,OAAO,SAAS,KAAK,GACzB,OAAO,EAAE,OAAO,wBAAwB,QAAQ;EACjD,IAAI,CAAC,OAAO,UAAU,KAAK,GAC1B,OAAO,EACN,OAAO,6CAA6C,QACrD;EACD,IAAI,SAAS,GAAG,OAAO,EAAE,OAAO,oBAAoB,QAAQ;EAC5D,OAAO,EAAE,SAAS,MAAM;CACzB;CAEA,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,YAAY,IAAI,OAAO,EAAE,OAAO,2BAA2B;CAK/D,IAAI,QAAQ,KAAK,OAAO,GACvB,OAAO,EACN,OAAO,oBAAoB,MAAM,uDAAuD,QAAQ,cAAc,QAAQ,0BACvH;CAGD,MAAM,YAAY,oBAAoB,KAAK,OAAO;CAClD,IAAI,CAAC,WACJ,OAAO,EACN,OAAO,qBAAqB,MAAM,mFACnC;CAGD,MAAM,QAAQ,OAAO,UAAU,EAAE;CACjC,MAAM,OAAO,UAAU,EAAE,CAAC,YAAY;CACtC,IAAI,SAAS,GAAG,OAAO,EAAE,OAAO,qBAAqB,QAAQ,GAAG;CAGhE,OAAO,EAAE,SADO,QAAQ,aAAa,MACpB;AAClB;AAEA,MAAM,eAAe;CACpB,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;AACJ;;AAGA,MAAa,yBAAyB,KAAK,aAAa;;;;;;;;AASxD,SAAgB,eACf,OAC0C;CAC1C,MAAM,SAAS,cAAc,KAAK;CAClC,IAAI,WAAW,QAAQ,OAAO;CAC9B,IAAI,OAAO,UAAU,wBACpB,OAAO,EACN,OAAO,uCAAuC,uBAAuB,UAAU,OAAO,QAAQ,GAC/F;CAED,OAAO;AACR;;;;;;;AAQA,SAAgB,sBAAsB,cAAsC;CAC3E,IAAI,CAAC,OAAO,SAAS,YAAY,KAAK,gBAAgB,GACrD,MAAM,IAAI,WACT,gEAAgE,cACjE;CAED,MAAM,aAAa;EAClB,CAAC,KAAK,aAAa,CAAC;EACpB,CAAC,KAAK,aAAa,CAAC;EACpB,CAAC,KAAK,aAAa,CAAC;EACpB,CAAC,KAAK,aAAa,CAAC;CACrB;CACA,KAAK,MAAM,CAAC,MAAM,YAAY,YAC7B,IAAI,eAAe,YAAY,GAC9B,OAAO,GAAG,eAAe,UAAU;CAGrC,OAAO,GAAG,aAAa;AACxB;;;;;;;;;;;;AAaA,SAAgB,oBACf,OAC0C;CAE1C,IAAI,UAAU,OAAO,OAAO,EAAE,SAAS,GAAG;CAG1C,IAAI,UAAU,KAAA,GAAW,OAAO,EAAE,SAAS,EAAE;CAG7C,IAAI,OAAO,UAAU,UAAU;EAC9B,IAAI,CAAC,OAAO,SAAS,KAAK,GACzB,OAAO,EAAE,OAAO,wBAAwB,QAAQ;EACjD,IAAI,CAAC,OAAO,UAAU,KAAK,GAC1B,OAAO,EAAE,OAAO,uBAAuB,QAAQ;EAGhD,IAAI,UAAU,MAAM,UAAU,GAAG,OAAO,EAAE,SAAS,MAAM;EAGzD,IAAI,QAAQ,MAAM,QAAQ,QACzB,OAAO,EACN,OAAO,mFAAmF,QAC3F;EAED,OAAO,EAAE,SAAS,MAAM;CACzB;CAGA,MAAM,SAAS,cAAc,KAAK;CAClC,IAAI,WAAW,QAAQ,OAAO;CAG9B,MAAM,EAAE,YAAY;CACpB,IAAI,UAAU,MAAM,UAAU,QAC7B,OAAO,EACN,OAAO,gFAAgF,MAAM,MAAM,QAAQ,GAC5G;CAGD,OAAO,EAAE,QAAQ;AAClB;;;;;AAMA,SAAgB,qBACf,SACqC;CACrC,IAAI,YAAY,IAAI,OAAO;CAC3B,IAAI,YAAY,GAAG,OAAO,KAAA;CAC1B,OAAO,sBAAsB,OAAO;AACrC"}
@@ -0,0 +1,32 @@
1
+ import { ExternalPackageEntry, ResolvedExternalPackage } from "./types.js";
2
+
3
+ //#region src/lib/external-packages.d.ts
4
+
5
+ /**
6
+ * The package a specifier belongs to, dropping any subpath: `sharp` from `sharp/lib/x`,
7
+ * `@scope/pkg` from `@scope/pkg/sub`.
8
+ *
9
+ * Files are installed and traced one package at a time, so this is the unit the deploy
10
+ * stages. A subpath narrows what esbuild leaves unresolved; it does not narrow what ships.
11
+ */
12
+ declare const externalPackageRoot: (specifier: string) => string;
13
+ /**
14
+ * Normalize one authored entry into the shape every consumer reads.
15
+ *
16
+ * `includeFiles` defaults to **true**: the bare-string form is the one users reach for, and
17
+ * it should produce a function that works. Turning it off is the deliberate gesture.
18
+ */
19
+ declare const normalizeExternalPackage: (entry: ExternalPackageEntry) => ResolvedExternalPackage;
20
+ /**
21
+ * The specifiers whose files a function ships, in declaration order and deduplicated.
22
+ *
23
+ * These are the specifiers **as authored**, subpath included, because that is what the trace
24
+ * has to import. A package may export only a subpath — `exports: { "./native": … }` with no
25
+ * `.` — in which case importing the root throws `ERR_PACKAGE_PATH_NOT_EXPORTED` and the
26
+ * trace finds nothing. Use {@link externalPackageRoot} on these to get what to *install*,
27
+ * which is always the whole package.
28
+ */
29
+ declare const packagesToStage: (entries: readonly ResolvedExternalPackage[]) => string[];
30
+ //#endregion
31
+ export { externalPackageRoot, normalizeExternalPackage, packagesToStage };
32
+ //# sourceMappingURL=external-packages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-packages.d.ts","names":[],"sources":["../../src/lib/external-packages.ts"],"mappings":";;;;;;AASA;AAYA;AAKoE;AAJ5D;AACL;AAGiE,cAjBvD,mBAiBuD,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GAAA,MAAA;AAWpE;;;;;;cAhBa,kCACL,yBACL;;;;;;;;;;cAcU,oCACM"}
@@ -0,0 +1,47 @@
1
+ //#region src/lib/external-packages.ts
2
+ /**
3
+ * The package a specifier belongs to, dropping any subpath: `sharp` from `sharp/lib/x`,
4
+ * `@scope/pkg` from `@scope/pkg/sub`.
5
+ *
6
+ * Files are installed and traced one package at a time, so this is the unit the deploy
7
+ * stages. A subpath narrows what esbuild leaves unresolved; it does not narrow what ships.
8
+ */
9
+ const externalPackageRoot = (specifier) => {
10
+ const segments = specifier.split("/");
11
+ const keep = specifier.startsWith("@") ? 2 : 1;
12
+ return segments.slice(0, keep).join("/");
13
+ };
14
+ /**
15
+ * Normalize one authored entry into the shape every consumer reads.
16
+ *
17
+ * `includeFiles` defaults to **true**: the bare-string form is the one users reach for, and
18
+ * it should produce a function that works. Turning it off is the deliberate gesture.
19
+ */
20
+ const normalizeExternalPackage = (entry) => typeof entry === "string" ? {
21
+ name: entry,
22
+ includeFiles: true
23
+ } : {
24
+ name: entry.name,
25
+ includeFiles: entry.includeFiles !== false
26
+ };
27
+ /**
28
+ * The specifiers whose files a function ships, in declaration order and deduplicated.
29
+ *
30
+ * These are the specifiers **as authored**, subpath included, because that is what the trace
31
+ * has to import. A package may export only a subpath — `exports: { "./native": … }` with no
32
+ * `.` — in which case importing the root throws `ERR_PACKAGE_PATH_NOT_EXPORTED` and the
33
+ * trace finds nothing. Use {@link externalPackageRoot} on these to get what to *install*,
34
+ * which is always the whole package.
35
+ */
36
+ const packagesToStage = (entries) => {
37
+ const specifiers = [];
38
+ for (const entry of entries) {
39
+ if (!entry.includeFiles) continue;
40
+ if (!specifiers.includes(entry.name)) specifiers.push(entry.name);
41
+ }
42
+ return specifiers;
43
+ };
44
+ //#endregion
45
+ export { externalPackageRoot, normalizeExternalPackage, packagesToStage };
46
+
47
+ //# sourceMappingURL=external-packages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-packages.js","names":[],"sources":["../../src/lib/external-packages.ts"],"sourcesContent":["import type { ExternalPackageEntry, ResolvedExternalPackage } from \"./types.js\";\n\n/**\n * The package a specifier belongs to, dropping any subpath: `sharp` from `sharp/lib/x`,\n * `@scope/pkg` from `@scope/pkg/sub`.\n *\n * Files are installed and traced one package at a time, so this is the unit the deploy\n * stages. A subpath narrows what esbuild leaves unresolved; it does not narrow what ships.\n */\nexport const externalPackageRoot = (specifier: string): string => {\n\tconst segments = specifier.split(\"/\");\n\tconst keep = specifier.startsWith(\"@\") ? 2 : 1;\n\treturn segments.slice(0, keep).join(\"/\");\n};\n\n/**\n * Normalize one authored entry into the shape every consumer reads.\n *\n * `includeFiles` defaults to **true**: the bare-string form is the one users reach for, and\n * it should produce a function that works. Turning it off is the deliberate gesture.\n */\nexport const normalizeExternalPackage = (\n\tentry: ExternalPackageEntry,\n): ResolvedExternalPackage =>\n\ttypeof entry === \"string\"\n\t\t? { name: entry, includeFiles: true }\n\t\t: { name: entry.name, includeFiles: entry.includeFiles !== false };\n\n/**\n * The specifiers whose files a function ships, in declaration order and deduplicated.\n *\n * These are the specifiers **as authored**, subpath included, because that is what the trace\n * has to import. A package may export only a subpath — `exports: { \"./native\": … }` with no\n * `.` — in which case importing the root throws `ERR_PACKAGE_PATH_NOT_EXPORTED` and the\n * trace finds nothing. Use {@link externalPackageRoot} on these to get what to *install*,\n * which is always the whole package.\n */\nexport const packagesToStage = (\n\tentries: readonly ResolvedExternalPackage[],\n): string[] => {\n\tconst specifiers: string[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.includeFiles) continue;\n\t\tif (!specifiers.includes(entry.name)) specifiers.push(entry.name);\n\t}\n\treturn specifiers;\n};\n"],"mappings":";;;;;;;;AASA,MAAa,uBAAuB,cAA8B;CACjE,MAAM,WAAW,UAAU,MAAM,GAAG;CACpC,MAAM,OAAO,UAAU,WAAW,GAAG,IAAI,IAAI;CAC7C,OAAO,SAAS,MAAM,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG;AACxC;;;;;;;AAQA,MAAa,4BACZ,UAEA,OAAO,UAAU,WACd;CAAE,MAAM;CAAO,cAAc;AAAK,IAClC;CAAE,MAAM,MAAM;CAAM,cAAc,MAAM,iBAAiB;AAAM;;;;;;;;;;AAWnE,MAAa,mBACZ,YACc;CACd,MAAM,aAAuB,CAAC;CAC9B,KAAK,MAAM,SAAS,SAAS;EAC5B,IAAI,CAAC,MAAM,cAAc;EACzB,IAAI,CAAC,WAAW,SAAS,MAAM,IAAI,GAAG,WAAW,KAAK,MAAM,IAAI;CACjE;CACA,OAAO;AACR"}
@@ -99,12 +99,20 @@ declare const postgresConfigSchema: z.ZodObject<{
99
99
  * Static definition of a function (existence). The slug is the record key (validated by
100
100
  * {@link functionSlugSchema}), so it is not a field here. Deploy tuning (`runtime`) lives
101
101
  * in the `branch` closure, not here.
102
+ *
103
+ * `externalPackages` entries are checked for contradictions: the same package named twice,
104
+ * or named once bare and once through a subpath with a different `includeFiles`. Both state
105
+ * two intents for one package, and files are staged per package rather than per subpath, so
106
+ * neither can be honoured as written.
102
107
  */
103
108
  declare const functionDefSchema: z.ZodObject<{
104
109
  name: z.ZodString;
105
110
  source: z.ZodString;
106
111
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
107
- externalPackages: z.ZodOptional<z.ZodArray<z.ZodString>>;
112
+ externalPackages: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
113
+ name: z.ZodString;
114
+ includeFiles: z.ZodOptional<z.ZodBoolean>;
115
+ }, z.core.$strict>]>>>;
108
116
  dev: z.ZodOptional<z.ZodObject<{
109
117
  port: z.ZodOptional<z.ZodNumber>;
110
118
  }, z.core.$strict>>;
@@ -122,7 +130,10 @@ declare const previewInputSchema: z.ZodObject<{
122
130
  name: z.ZodString;
123
131
  source: z.ZodString;
124
132
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
125
- externalPackages: z.ZodOptional<z.ZodArray<z.ZodString>>;
133
+ externalPackages: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
134
+ name: z.ZodString;
135
+ includeFiles: z.ZodOptional<z.ZodBoolean>;
136
+ }, z.core.$strict>]>>>;
126
137
  dev: z.ZodOptional<z.ZodObject<{
127
138
  port: z.ZodOptional<z.ZodNumber>;
128
139
  }, z.core.$strict>>;
@@ -192,7 +203,10 @@ declare const configInputSchema: z.ZodObject<{
192
203
  name: z.ZodString;
193
204
  source: z.ZodString;
194
205
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
195
- externalPackages: z.ZodOptional<z.ZodArray<z.ZodString>>;
206
+ externalPackages: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
207
+ name: z.ZodString;
208
+ includeFiles: z.ZodOptional<z.ZodBoolean>;
209
+ }, z.core.$strict>]>>>;
196
210
  dev: z.ZodOptional<z.ZodObject<{
197
211
  port: z.ZodOptional<z.ZodNumber>;
198
212
  }, z.core.$strict>>;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","names":[],"sources":["../../src/lib/schema.ts"],"mappings":";;;;;;AAgBA;AA8CG;;;;;;;;;cA9CU,uBAAqB,CAAA,CAAA;;;;GA8C/B,CAAA,CAAA,IAAA,CAAA;;cAGU,qBAAmB,CAAA,CAAA;;GAE9B,CAAA,CAAA,IAAA,CAAA;;cAGW,0BAAwB,CAAA,CAAA,mBAAA,CAAA,CAAA,YAAA,CAAA,CAAA;;GAGnC,CAAA,CAAA,IAAA,CAAA;AAXC;AA9C+B;AAAA;AAiDlC;AAEE,cAYW,qBAZX,EAYgC,CAAA,CAAA,SAZhC,CAAA;;;EAAA,iBAAA,eAAA,YAAA,CAAA;EAF8B,SAAA,eAAA,YAAA,CAAA;EAAA,SAAA,eAAA,WAAA,YAAA,CAAA,CAAA;EAKnB,eAAA,eAGX,YAAA,CAAA;EAAA,mBAAA,eAAA,YAAA,CAAA;EAHmC,WAAA,eAAA,WAAA,CAAA,SAAA,aAAA,CAAA,mBAAA,CAAA,cAAA,CAAA,UAAA,CAAA,CAAA,CAAA,CAAA;;;AAGnC,CAAA,EAmBA,CAAA,CAAA,IAAA,CAAA,OAnBA,CAAA;AAHmC;AAAA;AAAA;AASrC;AAaE;;cAeW,qBAAmB,CAAA,CAAA;;;;;;;;;;;;;;;;;;GAuB7B,CAAA,CAAA,IAAA,CAAA;;cAGU,oBAAkB,CAAA,CAAA,mBAAA,CAAA,CAAA,YAAA,CAAA,CAAA;;EAzC7B,YAAA,eAAA,WAAA,CAAA,SAAA,aAAA,CAAA,MAAA,CAAA,cAAA,CAAA,UAAA,CAAA,CAAA,CAAA,CAAA;EAbgC,OAAA,eAAA,YAAA,CAAA;EAAA,YAAA,eAAA,YAAA,CAAA;EA4BrB,WAAA,eAuBV,YAAA,CAAA;EAAA,QAAA,eAAA,YAAA,CAAA;;;;;;;;;;;;GAG0E,CAAA,CAAA,IAAA,CAAA;cAEhE,sBAAoB,CAAA,CAAA;;;;;;GAE/B,CAAA,CAAA,IAAA,CAAA;;;;;;cAgGW,mBAAiB,CAAA,CAAA;;;;;;;;GAM5B,CAAA,CAAA,IAAA,CAAA;;cAGW,iBAAe,CAAA,CAAA;;GAI1B,CAAA,CAAA,IAAA,CAAA;;AApHC,cAuHU,kBAvHV,EAuH4B,CAAA,CAAA,SAvH5B,CAAA;EAvB6B,SAAA,eAAA,WAAA,CAAA,SAAA,aAAA,aAAA,CAAA;IAAA,OAAA,eAAA,aAAA,CAAA;EA0BnB,CAAA,gBAAA,CAAA,CAAA,CAAA,CAAgE;EAAA,SAAA,eAAA,YAAA,YAAA,aAAA,CAAA;IAA9C,IAAA,aAAA;;;;;;;;;;;GAwH7B,CAAA,CAAA,IAAA,CAAA;;cAGW,sBAAoB,CAAA,CAAA;;GAE/B,CAAA,CAAA,IAAA,CAAA;;;;;cAWW,oBAAkB,CAAA,CAAA;;;;;;;;;;;;;;;;GAuB5B,CAAA,CAAA,IAAA,CAAA;;;AA/J0E;AAA9C;AAAA;AAAA,cAsKlB,iBAtKkB,EAsKD,CAAA,CAAA,SAtKC,CAAA;EAElB,IAAA,eAAA,WAEX,CAAA,SAAA,aAAA,aAAA,CAAA;IAAA,OAAA,eAAA,aAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;IAAA,SAAA,eAAA,YAAA,YAAA,aAAA,CAAA;MAF+B,IAAA,aAAA;MAAA,MAAA,aAAA;MAkGpB,GAAA,eAMX,YAAA,YAAA,aAAA,CAAA,CAAA;MAAA,gBAAA,eAAA,WAAA,YAAA,CAAA,CAAA;;;;;;;;;UAkEmB,CAAA,CAAA,YAAA,CAAA,CAAA;GAuBlB,CAAA,CAAA,IAAA,CAAA;;;;;AAzFD;AAN4B;AAAA;AAS9B;AAIE;iBAwIc,eAAA,QAAuB,CAAA,CAAE"}
1
+ {"version":3,"file":"schema.d.ts","names":[],"sources":["../../src/lib/schema.ts"],"mappings":"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"}