@neondatabase/config 0.4.2 → 0.7.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/dist/index.d.ts +5 -4
- package/dist/index.js +3 -2
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/credentials.d.ts +37 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +30 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/define-config.js.map +1 -1
- package/dist/lib/diff.d.ts +7 -16
- package/dist/lib/diff.d.ts.map +1 -1
- package/dist/lib/diff.js +17 -22
- package/dist/lib/diff.js.map +1 -1
- package/dist/lib/duration.js.map +1 -1
- package/dist/lib/errors.d.ts +11 -1
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +17 -1
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/loader.js +3 -2
- package/dist/lib/loader.js.map +1 -1
- package/dist/lib/neon-api-real.d.ts +6 -0
- package/dist/lib/neon-api-real.d.ts.map +1 -1
- package/dist/lib/neon-api-real.js +175 -39
- package/dist/lib/neon-api-real.js.map +1 -1
- package/dist/lib/neon-api.d.ts +101 -19
- package/dist/lib/neon-api.d.ts.map +1 -1
- package/dist/lib/patterns.js.map +1 -1
- package/dist/lib/schema.d.ts +0 -3
- package/dist/lib/schema.d.ts.map +1 -1
- package/dist/lib/schema.js +7 -8
- package/dist/lib/schema.js.map +1 -1
- package/dist/lib/types.d.ts +22 -16
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/wrap-neon-error.js.map +1 -1
- package/dist/v1.d.ts +6 -7
- package/dist/v1.d.ts.map +1 -1
- package/dist/v1.js +4 -2
- package/dist/v1.js.map +1 -1
- package/package.json +1 -1
package/dist/lib/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","names":[],"sources":["../../src/lib/loader.ts"],"sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { defineConfig } from \"./define-config.js\";\nimport { ConfigLoadError } from \"./errors.js\";\nimport type { Config } from \"./types.js\";\n\n/**\n * Default file names tried (in order) when {@link loadConfigFromFile} is called without an\n * explicit path. We accept `.ts` first because that's the documented format; `.mjs` and `.js`\n * fall out for free since jiti handles all of them.\n */\nexport const DEFAULT_CONFIG_FILENAMES = [\n\t\"neon.ts\",\n\t\"neon.mts\",\n\t\"neon.js\",\n\t\"neon.mjs\",\n] as const;\n\nexport interface LoadConfigOptions {\n\t/** Explicit absolute or cwd-relative path to a config file. Takes precedence over the search. */\n\tpath?: string;\n\t/** Starting directory for the upward search. Defaults to `process.cwd()`. */\n\tcwd?: string;\n\t/**\n\t * Hard ceiling for the upward walk — once `current === stopAt` the search returns\n\t * `null` even if no `.git` boundary was hit. Defaults to the OS home directory so\n\t * stray runs from outside any repo never leak into the user's `~` files.\n\t */\n\tstopAt?: string;\n}\n\n/**\n * Load a `neon.ts` (or any other supported extension) and return the validated {@link Config}.\n *\n * Behavior:\n * - When `path` is set, that file is loaded directly. The file must exist and must default-export\n * a value produced by `defineConfig()`.\n * - When `path` is omitted, we walk up from `cwd` picking the **closest** file matching\n * {@link DEFAULT_CONFIG_FILENAMES}. The walk is monorepo-friendly: intermediate\n * `package.json` files do **not** stop it, so a single `neon.ts` lifted to the workspace\n * root keeps working when invoked from inside any sub-package. The walk terminates at the\n * first directory containing `.git`, at `stopAt`, or at the filesystem root.\n *\n * jiti is loaded lazily so that callers who pass an already-resolved `Config` to `pushConfig`\n * never pay the import cost.\n */\nexport async function loadConfigFromFile(\n\toptions: LoadConfigOptions = {},\n): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\tconst resolvedPath = options.path\n\t\t? resolveExplicitPath(options.path, options.cwd)\n\t\t: findDefaultConfig(options.cwd, options.stopAt);\n\n\tif (!resolvedPath) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Could not find a Neon config file while walking up from ${resolve(options.cwd ?? process.cwd())}.`,\n\t\t\t\t`Looked for: ${DEFAULT_CONFIG_FILENAMES.join(\", \")} (stopping at the first directory with a \\`.git\\`).`,\n\t\t\t\t`Create one at your repository root (or anywhere on the path from cwd up to .git), or pass an explicit \\`configPath\\` (SDK) / \\`--config <path>\\` (CLI).`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\tlet mod: unknown;\n\ttry {\n\t\tmod = await importModule(resolvedPath);\n\t} catch (cause) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Failed to evaluate ${resolvedPath}.`,\n\t\t\t\t`Underlying error: ${(cause as Error)?.message ?? String(cause)}`,\n\t\t\t\t\"This is usually a TypeScript syntax error, a missing dependency, or a runtime exception inside the config file. Run the file directly (e.g. `npx tsx neon.ts`) to reproduce.\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tconst exported = extractDefaultExport(mod);\n\tif (exported === undefined) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`${resolvedPath} loaded successfully but did not default-export a config.`,\n\t\t\t\t\"Add `export default defineConfig({ ... })` at the bottom of the file. (Named exports are ignored.)\",\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\t// Run through defineConfig to validate any function the user might have constructed manually.\n\tconst config = defineConfig(exported as Config);\n\treturn { config, resolvedPath };\n}\n\nfunction resolveExplicitPath(input: string, cwd?: string): string {\n\tconst base = resolve(cwd ?? process.cwd());\n\tconst abs = isAbsolute(input) ? input : resolve(base, input);\n\tif (!existsSync(abs)) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config file not found at ${abs}. The path was resolved from \\`${input}\\` against ${base}.`,\n\t\t);\n\t}\n\tconst s = statSync(abs);\n\tif (!s.isFile()) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config path ${abs} is a directory, not a file. Pass a path to the file itself (e.g. ./neon.ts).`,\n\t\t);\n\t}\n\treturn abs;\n}\n\nfunction findDefaultConfig(\n\tcwd: string | undefined,\n\tstopAt: string | undefined,\n): string | null {\n\tlet current = resolve(cwd ?? process.cwd());\n\tconst stop = resolve(stopAt ?? homedir());\n\tlet lastSeen: string | null = null;\n\n\twhile (true) {\n\t\tfor (const name of DEFAULT_CONFIG_FILENAMES) {\n\t\t\tconst candidate = resolve(current, name);\n\t\t\tif (existsSync(candidate) && safeIsFile(candidate))\n\t\t\t\treturn candidate;\n\t\t}\n\n\t\t// `.git` is the canonical repo-root marker. `package.json` is deliberately *not*\n\t\t// a stop: monorepos lift `neon.ts` above sub-package package.jsons.\n\t\tif (existsSync(resolve(current, \".git\"))) return null;\n\t\tif (current === stop) return null;\n\n\t\tconst parent = dirname(current);\n\t\tif (parent === current || parent === lastSeen) return null;\n\t\tlastSeen = current;\n\t\tcurrent = parent;\n\t}\n}\n\nasync function importModule(absPath: string): Promise<unknown> {\n\tconst lower = absPath.toLowerCase();\n\tconst needsJiti =\n\t\tlower.endsWith(\".ts\") ||\n\t\tlower.endsWith(\".mts\") ||\n\t\tlower.endsWith(\".cts\");\n\n\tif (!needsJiti) {\n\t\treturn import(pathToFileURL(absPath).href);\n\t}\n\n\tconst jitiModule: unknown = await import(\"jiti\");\n\tconst createJiti = extractCreateJiti(jitiModule);\n\tif (!createJiti) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t\"jiti is required to load TypeScript config files but could not be initialised.\",\n\t\t\t\t\"Reinstall the package dependencies (`pnpm install` / `npm install`) — jiti is a runtime dependency of @neondatabase/config.\",\n\t\t\t].join(\" \"),\n\t\t);\n\t}\n\tconst jiti = createJiti(pathToFileURL(absPath).href, {\n\t\tinteropDefault: true,\n\t\tmoduleCache: false,\n\t});\n\treturn jiti.import(absPath);\n}\n\nfunction extractCreateJiti(\n\tmod: unknown,\n): ((id: string, options?: unknown) => JitiInstance) | null {\n\tif (mod === null || typeof mod !== \"object\") return null;\n\tconst obj = mod as Record<string, unknown>;\n\tif (typeof obj.createJiti === \"function\") {\n\t\treturn obj.createJiti as (\n\t\t\tid: string,\n\t\t\toptions?: unknown,\n\t\t) => JitiInstance;\n\t}\n\tconst def = obj.default;\n\tif (def !== null && typeof def === \"object\") {\n\t\tconst defObj = def as Record<string, unknown>;\n\t\tif (typeof defObj.createJiti === \"function\") {\n\t\t\treturn defObj.createJiti as (\n\t\t\t\tid: string,\n\t\t\t\toptions?: unknown,\n\t\t\t) => JitiInstance;\n\t\t}\n\t}\n\treturn null;\n}\n\ninterface JitiInstance {\n\timport(id: string): Promise<unknown>;\n}\n\nfunction extractDefaultExport(mod: unknown): unknown {\n\tif (mod === null || typeof mod !== \"object\") return mod;\n\tconst obj = mod as Record<string, unknown>;\n\tif (\"default\" in obj && obj.default !== undefined) return obj.default;\n\t// No `default` export. If the module itself is a function, treat it as the config —\n\t// that lets tests and advanced users skip the wrapper.\n\t// Otherwise, return `undefined` so the caller surfaces a clear ConfigLoadError.\n\tif (typeof mod === \"function\") return mod;\n\treturn undefined;\n}\n\nfunction safeIsFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loader.js","names":[],"sources":["../../src/lib/loader.ts"],"sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { defineConfig } from \"./define-config.js\";\nimport { ConfigLoadError, isPlatformError } from \"./errors.js\";\nimport type { Config } from \"./types.js\";\n\n/**\n * Default file names tried (in order) when {@link loadConfigFromFile} is called without an\n * explicit path. We accept `.ts` first because that's the documented format; `.mjs` and `.js`\n * fall out for free since jiti handles all of them.\n */\nexport const DEFAULT_CONFIG_FILENAMES = [\n\t\"neon.ts\",\n\t\"neon.mts\",\n\t\"neon.js\",\n\t\"neon.mjs\",\n] as const;\n\nexport interface LoadConfigOptions {\n\t/** Explicit absolute or cwd-relative path to a config file. Takes precedence over the search. */\n\tpath?: string;\n\t/** Starting directory for the upward search. Defaults to `process.cwd()`. */\n\tcwd?: string;\n\t/**\n\t * Hard ceiling for the upward walk — once `current === stopAt` the search returns\n\t * `null` even if no `.git` boundary was hit. Defaults to the OS home directory so\n\t * stray runs from outside any repo never leak into the user's `~` files.\n\t */\n\tstopAt?: string;\n}\n\n/**\n * Load a `neon.ts` (or any other supported extension) and return the validated {@link Config}.\n *\n * Behavior:\n * - When `path` is set, that file is loaded directly. The file must exist and must default-export\n * a value produced by `defineConfig()`.\n * - When `path` is omitted, we walk up from `cwd` picking the **closest** file matching\n * {@link DEFAULT_CONFIG_FILENAMES}. The walk is monorepo-friendly: intermediate\n * `package.json` files do **not** stop it, so a single `neon.ts` lifted to the workspace\n * root keeps working when invoked from inside any sub-package. The walk terminates at the\n * first directory containing `.git`, at `stopAt`, or at the filesystem root.\n *\n * jiti is loaded lazily so that callers who pass an already-resolved `Config` to `pushConfig`\n * never pay the import cost.\n */\nexport async function loadConfigFromFile(\n\toptions: LoadConfigOptions = {},\n): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\tconst resolvedPath = options.path\n\t\t? resolveExplicitPath(options.path, options.cwd)\n\t\t: findDefaultConfig(options.cwd, options.stopAt);\n\n\tif (!resolvedPath) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Could not find a Neon config file while walking up from ${resolve(options.cwd ?? process.cwd())}.`,\n\t\t\t\t`Looked for: ${DEFAULT_CONFIG_FILENAMES.join(\", \")} (stopping at the first directory with a \\`.git\\`).`,\n\t\t\t\t`Create one at your repository root (or anywhere on the path from cwd up to .git), or pass an explicit \\`configPath\\` (SDK) / \\`--config <path>\\` (CLI).`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\tlet mod: unknown;\n\ttry {\n\t\tmod = await importModule(resolvedPath);\n\t} catch (cause) {\n\t\t// `defineConfig()` runs at module-eval time, so a config the user got *wrong*\n\t\t// (a bad function slug, an unknown key, an invalid duration, …) throws a\n\t\t// PlatformError from inside this import. That error already pinpoints the exact\n\t\t// field and reason, so surface it verbatim — burying it under the generic\n\t\t// \"this is usually a TypeScript syntax error\" hint sent users hunting for a\n\t\t// syntax bug that isn't there. The hint is reserved for genuine evaluation\n\t\t// failures (syntax errors, missing deps, thrown runtime exceptions).\n\t\tif (isPlatformError(cause)) {\n\t\t\tthrow cause;\n\t\t}\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Failed to evaluate ${resolvedPath}.`,\n\t\t\t\t`Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t\t\"This is usually a TypeScript syntax error, a missing dependency, or a runtime exception inside the config file. Run the file directly (e.g. `npx tsx neon.ts`) to reproduce.\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tconst exported = extractDefaultExport(mod);\n\tif (exported === undefined) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`${resolvedPath} loaded successfully but did not default-export a config.`,\n\t\t\t\t\"Add `export default defineConfig({ ... })` at the bottom of the file. (Named exports are ignored.)\",\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\t// Run through defineConfig to validate any function the user might have constructed manually.\n\tconst config = defineConfig(exported as Config);\n\treturn { config, resolvedPath };\n}\n\nfunction resolveExplicitPath(input: string, cwd?: string): string {\n\tconst base = resolve(cwd ?? process.cwd());\n\tconst abs = isAbsolute(input) ? input : resolve(base, input);\n\tif (!existsSync(abs)) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config file not found at ${abs}. The path was resolved from \\`${input}\\` against ${base}.`,\n\t\t);\n\t}\n\tconst s = statSync(abs);\n\tif (!s.isFile()) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config path ${abs} is a directory, not a file. Pass a path to the file itself (e.g. ./neon.ts).`,\n\t\t);\n\t}\n\treturn abs;\n}\n\nfunction findDefaultConfig(\n\tcwd: string | undefined,\n\tstopAt: string | undefined,\n): string | null {\n\tlet current = resolve(cwd ?? process.cwd());\n\tconst stop = resolve(stopAt ?? homedir());\n\tlet lastSeen: string | null = null;\n\n\twhile (true) {\n\t\tfor (const name of DEFAULT_CONFIG_FILENAMES) {\n\t\t\tconst candidate = resolve(current, name);\n\t\t\tif (existsSync(candidate) && safeIsFile(candidate))\n\t\t\t\treturn candidate;\n\t\t}\n\n\t\t// `.git` is the canonical repo-root marker. `package.json` is deliberately *not*\n\t\t// a stop: monorepos lift `neon.ts` above sub-package package.jsons.\n\t\tif (existsSync(resolve(current, \".git\"))) return null;\n\t\tif (current === stop) return null;\n\n\t\tconst parent = dirname(current);\n\t\tif (parent === current || parent === lastSeen) return null;\n\t\tlastSeen = current;\n\t\tcurrent = parent;\n\t}\n}\n\nasync function importModule(absPath: string): Promise<unknown> {\n\tconst lower = absPath.toLowerCase();\n\tconst needsJiti =\n\t\tlower.endsWith(\".ts\") ||\n\t\tlower.endsWith(\".mts\") ||\n\t\tlower.endsWith(\".cts\");\n\n\tif (!needsJiti) {\n\t\treturn import(pathToFileURL(absPath).href);\n\t}\n\n\tconst jitiModule: unknown = await import(\"jiti\");\n\tconst createJiti = extractCreateJiti(jitiModule);\n\tif (!createJiti) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t\"jiti is required to load TypeScript config files but could not be initialised.\",\n\t\t\t\t\"Reinstall the package dependencies (`pnpm install` / `npm install`) — jiti is a runtime dependency of @neondatabase/config.\",\n\t\t\t].join(\" \"),\n\t\t);\n\t}\n\tconst jiti = createJiti(pathToFileURL(absPath).href, {\n\t\tinteropDefault: true,\n\t\tmoduleCache: false,\n\t});\n\treturn jiti.import(absPath);\n}\n\nfunction extractCreateJiti(\n\tmod: unknown,\n): ((id: string, options?: unknown) => JitiInstance) | null {\n\tif (mod === null || typeof mod !== \"object\") return null;\n\tconst obj = mod as Record<string, unknown>;\n\tif (typeof obj.createJiti === \"function\") {\n\t\treturn obj.createJiti as (\n\t\t\tid: string,\n\t\t\toptions?: unknown,\n\t\t) => JitiInstance;\n\t}\n\tconst def = obj.default;\n\tif (def !== null && typeof def === \"object\") {\n\t\tconst defObj = def as Record<string, unknown>;\n\t\tif (typeof defObj.createJiti === \"function\") {\n\t\t\treturn defObj.createJiti as (\n\t\t\t\tid: string,\n\t\t\t\toptions?: unknown,\n\t\t\t) => JitiInstance;\n\t\t}\n\t}\n\treturn null;\n}\n\ninterface JitiInstance {\n\timport(id: string): Promise<unknown>;\n}\n\nfunction extractDefaultExport(mod: unknown): unknown {\n\tif (mod === null || typeof mod !== \"object\") return mod;\n\tconst obj = mod as Record<string, unknown>;\n\tif (\"default\" in obj && obj.default !== undefined) return obj.default;\n\t// No `default` export. If the module itself is a function, treat it as the config —\n\t// that lets tests and advanced users skip the wrapper.\n\t// Otherwise, return `undefined` so the caller surfaces a clear ConfigLoadError.\n\tif (typeof mod === \"function\") return mod;\n\treturn undefined;\n}\n\nfunction safeIsFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -53,6 +53,12 @@ declare function isPreviewFeatureUnavailable(err: unknown): boolean;
|
|
|
53
53
|
* Convert a Preview-feature error into a clear {@link PlatformError} when the feature is
|
|
54
54
|
* unavailable for the project; otherwise pass the original error through unchanged so a
|
|
55
55
|
* genuine failure (auth, transient 5xx, …) keeps its specific code and message.
|
|
56
|
+
*
|
|
57
|
+
* The message names the failing feature, summarizes the response in one short
|
|
58
|
+
* `HTTP <status> <reason>` line, includes the raw Neon API message + request id (valuable
|
|
59
|
+
* signal while the feature is in preview), gives status-specific guidance (see
|
|
60
|
+
* {@link previewUnavailableHint}), and offers removing the feature from the policy as an
|
|
61
|
+
* escape hatch. `status`/`requestId` are also kept on `details` for programmatic consumers.
|
|
56
62
|
*/
|
|
57
63
|
declare function previewUnavailableError(err: unknown, featureLabel: string): unknown;
|
|
58
64
|
declare function createNeonAuthRestInput(input: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-api-real.d.ts","names":[],"sources":["../../src/lib/neon-api-real.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"neon-api-real.d.ts","names":[],"sources":["../../src/lib/neon-api-real.ts"],"mappings":";;;UAiIU,uBAAA;;EAAA,aAAA,CAAA,EAAA,MAAA;AAeV;AAyCC;AAeD;;;;AAES,iBA1DO,iBAAA,CA0DP,OAAA,EAAA;QACE,EAAA,MAAA;SAAR,CAAA,EAAA,MAAA;EAAO;AA+2BV;AAiFA;AA6DA;AAoBA;EAAuC,aAAA,CAAA,EAAA;IAAQ,WAAA,CAAA,EAAA,MAAA;IAAsB,cAAA,CAAA,EAAA,MAAA;IAAQ,UAAA,CAAA,EAAA,MAAA;EAwBvD,CAAA;CAAY,CAAA,EAvlC9B,OAulC8B;UAzjCxB,WAAA,CAyjC8B;aAAW,EAAA,MAAA;EAAO,cAAA,EAAA,MAAA;;;;;;;;;;iBA5iCpC,2BACX,QAAQ,YACV,cACN,QAAQ;;;;;;;;;;;;iBA+2BK,2BAAA;;;;;;;;;;;;iBAiFA,uBAAA;iBA6DA,uBAAA;;IAEZ;;;;;;;;;;;;iBAkBY,uBAAA,QAA+B,sBAAsB;;;;;;;;;;;iBAwB/C,YAAA,MAAkB,WAAW"}
|
|
@@ -18,6 +18,12 @@ const bucketSchema = z.object({
|
|
|
18
18
|
});
|
|
19
19
|
const bucketResponseSchema = z.object({ bucket: bucketSchema });
|
|
20
20
|
const bucketsListResponseSchema = z.object({ buckets: z.array(bucketSchema) });
|
|
21
|
+
const branchStorageSchema = z.object({
|
|
22
|
+
enabled: z.boolean().optional(),
|
|
23
|
+
s3_endpoint: z.string(),
|
|
24
|
+
region: z.string(),
|
|
25
|
+
force_path_style: z.boolean()
|
|
26
|
+
});
|
|
21
27
|
const functionDeploymentSchema = z.object({
|
|
22
28
|
id: z.number(),
|
|
23
29
|
status: z.string()
|
|
@@ -29,9 +35,39 @@ const neonFunctionSchema = z.object({
|
|
|
29
35
|
invocation_url: z.string(),
|
|
30
36
|
active_deployment: functionDeploymentSchema.optional()
|
|
31
37
|
});
|
|
32
|
-
const functionResponseSchema = z.object({ function: neonFunctionSchema });
|
|
33
38
|
const functionsListResponseSchema = z.object({ functions: z.array(neonFunctionSchema) });
|
|
34
39
|
const functionDeploymentResponseSchema = z.object({ deployment: functionDeploymentSchema });
|
|
40
|
+
const credentialScopeSchema = z.enum([
|
|
41
|
+
"storage:read",
|
|
42
|
+
"storage:write",
|
|
43
|
+
"ai_gateway:invoke",
|
|
44
|
+
"functions:invoke"
|
|
45
|
+
]);
|
|
46
|
+
const createCredentialResponseSchema = z.object({
|
|
47
|
+
token_id: z.string(),
|
|
48
|
+
token_id_short: z.string(),
|
|
49
|
+
name: z.string().optional(),
|
|
50
|
+
api_token: z.string(),
|
|
51
|
+
s3_secret_access_key: z.string(),
|
|
52
|
+
scopes: z.array(credentialScopeSchema),
|
|
53
|
+
branch_id: z.string(),
|
|
54
|
+
created_at: z.string(),
|
|
55
|
+
expires_at: z.string().optional()
|
|
56
|
+
});
|
|
57
|
+
const credentialMetaSchema = z.object({
|
|
58
|
+
token_id: z.string(),
|
|
59
|
+
token_id_short: z.string(),
|
|
60
|
+
name: z.string().optional(),
|
|
61
|
+
scopes: z.array(credentialScopeSchema),
|
|
62
|
+
principal_type: z.enum(["user", "function"]),
|
|
63
|
+
function_id: z.string().optional(),
|
|
64
|
+
branch_id: z.string().optional(),
|
|
65
|
+
created_at: z.string(),
|
|
66
|
+
last_used_at: z.string().optional(),
|
|
67
|
+
revoked_at: z.string().optional(),
|
|
68
|
+
expires_at: z.string().optional()
|
|
69
|
+
});
|
|
70
|
+
const listCredentialsResponseSchema = z.object({ credentials: z.array(credentialMetaSchema) });
|
|
35
71
|
/**
|
|
36
72
|
* Adapt `@neondatabase/api-client` to the narrow {@link NeonApi} façade used by the rest of
|
|
37
73
|
* this package. Constructs are restricted to whole-object read/write of just the fields we
|
|
@@ -364,6 +400,22 @@ var RealNeonApi = class {
|
|
|
364
400
|
mutating: true
|
|
365
401
|
});
|
|
366
402
|
}
|
|
403
|
+
async getProjectBranchStorage(projectId, branchId) {
|
|
404
|
+
try {
|
|
405
|
+
return await this.call(`getProjectBranchStorage(${projectId}/${branchId})`, async () => {
|
|
406
|
+
const data = await this.getJson(`/projects/${encodeURIComponent(projectId)}/branches/${encodeURIComponent(branchId)}/storage`);
|
|
407
|
+
const parsed = branchStorageSchema.parse(data);
|
|
408
|
+
return {
|
|
409
|
+
s3Endpoint: parsed.s3_endpoint,
|
|
410
|
+
region: parsed.region,
|
|
411
|
+
forcePathStyle: parsed.force_path_style
|
|
412
|
+
};
|
|
413
|
+
}, { projectId });
|
|
414
|
+
} catch (err) {
|
|
415
|
+
if (err instanceof PlatformError && err.code === ErrorCode.NotFound) return null;
|
|
416
|
+
throw previewUnavailableError(err, "Object storage");
|
|
417
|
+
}
|
|
418
|
+
}
|
|
367
419
|
async listBranchFunctions(projectId, branchId) {
|
|
368
420
|
try {
|
|
369
421
|
return await this.call(`listBranchFunctions(${projectId}/${branchId})`, async () => {
|
|
@@ -374,18 +426,6 @@ var RealNeonApi = class {
|
|
|
374
426
|
throw previewUnavailableError(err, "Functions");
|
|
375
427
|
}
|
|
376
428
|
}
|
|
377
|
-
async createBranchFunction(projectId, branchId, input) {
|
|
378
|
-
return this.call(`createBranchFunction(${projectId}/${branchId}/${input.slug})`, async () => {
|
|
379
|
-
const data = await this.postJson(branchPreviewPath(projectId, branchId, "functions"), {
|
|
380
|
-
slug: input.slug,
|
|
381
|
-
name: input.name
|
|
382
|
-
});
|
|
383
|
-
return functionToSnapshot(functionResponseSchema.parse(data).function);
|
|
384
|
-
}, {
|
|
385
|
-
projectId,
|
|
386
|
-
mutating: true
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
429
|
async deleteBranchFunction(projectId, branchId, slug) {
|
|
390
430
|
await this.call(`deleteBranchFunction(${projectId}/${branchId}/${slug})`, async () => {
|
|
391
431
|
await this.deleteJson(`${branchPreviewPath(projectId, branchId, "functions")}/${encodeURIComponent(slug)}`);
|
|
@@ -403,28 +443,37 @@ var RealNeonApi = class {
|
|
|
403
443
|
mutating: true
|
|
404
444
|
});
|
|
405
445
|
}
|
|
406
|
-
async
|
|
446
|
+
async createCredential(projectId, branchId, input) {
|
|
407
447
|
try {
|
|
408
|
-
return await this.call(`
|
|
409
|
-
|
|
410
|
-
|
|
448
|
+
return await this.call(`createCredential(${projectId}/${branchId})`, async () => {
|
|
449
|
+
const data = await this.postJson(credentialsPath(projectId, branchId), {
|
|
450
|
+
scopes: input.scopes,
|
|
451
|
+
principal_type: input.principalType,
|
|
452
|
+
...input.functionId ? { function_id: input.functionId } : {},
|
|
453
|
+
...input.name ? { name: input.name } : {}
|
|
454
|
+
});
|
|
455
|
+
return createCredentialToSnapshot(createCredentialResponseSchema.parse(data));
|
|
456
|
+
}, {
|
|
457
|
+
projectId,
|
|
458
|
+
mutating: true
|
|
459
|
+
});
|
|
411
460
|
} catch (err) {
|
|
412
|
-
|
|
413
|
-
if (err instanceof PlatformError && err.code === ErrorCode.NotFound) return false;
|
|
414
|
-
throw err;
|
|
461
|
+
throw previewUnavailableError(err, "Branch credentials");
|
|
415
462
|
}
|
|
416
463
|
}
|
|
417
|
-
async
|
|
418
|
-
|
|
419
|
-
await this.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
})
|
|
464
|
+
async listCredentials(projectId, branchId) {
|
|
465
|
+
try {
|
|
466
|
+
return await this.call(`listCredentials(${projectId}/${branchId})`, async () => {
|
|
467
|
+
const data = await this.getJson(credentialsPath(projectId, branchId));
|
|
468
|
+
return listCredentialsResponseSchema.parse(data).credentials.map(credentialMetaToSnapshot);
|
|
469
|
+
}, { projectId });
|
|
470
|
+
} catch (err) {
|
|
471
|
+
throw previewUnavailableError(err, "Branch credentials");
|
|
472
|
+
}
|
|
424
473
|
}
|
|
425
|
-
async
|
|
426
|
-
await this.call(`
|
|
427
|
-
await this.deleteJson(
|
|
474
|
+
async revokeCredential(projectId, branchId, tokenId) {
|
|
475
|
+
await this.call(`revokeCredential(${projectId}/${branchId}/${tokenId})`, async () => {
|
|
476
|
+
await this.deleteJson(`${credentialsPath(projectId, branchId)}/${encodeURIComponent(tokenId)}`);
|
|
428
477
|
}, {
|
|
429
478
|
projectId,
|
|
430
479
|
mutating: true
|
|
@@ -434,8 +483,38 @@ var RealNeonApi = class {
|
|
|
434
483
|
function branchPreviewPath(projectId, branchId, resource) {
|
|
435
484
|
return `/projects/${encodeURIComponent(projectId)}/branches/${encodeURIComponent(branchId)}/${resource}`;
|
|
436
485
|
}
|
|
437
|
-
function
|
|
438
|
-
return `/projects/${encodeURIComponent(projectId)}/branches/${encodeURIComponent(branchId)}/
|
|
486
|
+
function credentialsPath(projectId, branchId) {
|
|
487
|
+
return `/projects/${encodeURIComponent(projectId)}/branches/${encodeURIComponent(branchId)}/credentials`;
|
|
488
|
+
}
|
|
489
|
+
function createCredentialToSnapshot(data) {
|
|
490
|
+
const snapshot = {
|
|
491
|
+
tokenId: data.token_id,
|
|
492
|
+
tokenIdShort: data.token_id_short,
|
|
493
|
+
apiToken: data.api_token,
|
|
494
|
+
s3SecretAccessKey: data.s3_secret_access_key,
|
|
495
|
+
scopes: data.scopes,
|
|
496
|
+
branchId: data.branch_id,
|
|
497
|
+
createdAt: data.created_at
|
|
498
|
+
};
|
|
499
|
+
if (data.name !== void 0) snapshot.name = data.name;
|
|
500
|
+
if (data.expires_at !== void 0) snapshot.expiresAt = data.expires_at;
|
|
501
|
+
return snapshot;
|
|
502
|
+
}
|
|
503
|
+
function credentialMetaToSnapshot(data) {
|
|
504
|
+
const snapshot = {
|
|
505
|
+
tokenId: data.token_id,
|
|
506
|
+
tokenIdShort: data.token_id_short,
|
|
507
|
+
scopes: data.scopes,
|
|
508
|
+
principalType: data.principal_type,
|
|
509
|
+
createdAt: data.created_at
|
|
510
|
+
};
|
|
511
|
+
if (data.name !== void 0) snapshot.name = data.name;
|
|
512
|
+
if (data.function_id !== void 0) snapshot.functionId = data.function_id;
|
|
513
|
+
if (data.branch_id !== void 0) snapshot.branchId = data.branch_id;
|
|
514
|
+
if (data.last_used_at !== void 0) snapshot.lastUsedAt = data.last_used_at;
|
|
515
|
+
if (data.revoked_at !== void 0) snapshot.revokedAt = data.revoked_at;
|
|
516
|
+
if (data.expires_at !== void 0) snapshot.expiresAt = data.expires_at;
|
|
517
|
+
return snapshot;
|
|
439
518
|
}
|
|
440
519
|
function bucketToSnapshot(bucket) {
|
|
441
520
|
return {
|
|
@@ -476,10 +555,6 @@ function normalizeDeploymentStatus(value) {
|
|
|
476
555
|
default: return "pending";
|
|
477
556
|
}
|
|
478
557
|
}
|
|
479
|
-
function aiGatewayEnabledFromResponse(data) {
|
|
480
|
-
if (data !== null && typeof data === "object" && "enabled" in data) return data.enabled === true;
|
|
481
|
-
return false;
|
|
482
|
-
}
|
|
483
558
|
/**
|
|
484
559
|
* Whether an error from a Preview-feature read means the feature simply isn't available
|
|
485
560
|
* for this project/branch/region (as opposed to a real, transient failure). Neon signals
|
|
@@ -498,16 +573,77 @@ function isPreviewFeatureUnavailable(err) {
|
|
|
498
573
|
return (message.includes("not available") || message.includes("does not exist") || message.includes("not enabled")) && (status === 503 || status === 404 || status === 501);
|
|
499
574
|
}
|
|
500
575
|
/**
|
|
576
|
+
* Reason phrase for the handful of HTTP statuses a Preview-feature read can surface as
|
|
577
|
+
* "unavailable". Used to print a short `HTTP <status> <reason>` line (not a stack trace),
|
|
578
|
+
* so the message reads like the API response the user would see in a tool like curl.
|
|
579
|
+
*/
|
|
580
|
+
const HTTP_STATUS_TEXT = {
|
|
581
|
+
401: "Unauthorized",
|
|
582
|
+
403: "Forbidden",
|
|
583
|
+
404: "Not Found",
|
|
584
|
+
500: "Internal Server Error",
|
|
585
|
+
501: "Not Implemented",
|
|
586
|
+
503: "Service Unavailable"
|
|
587
|
+
};
|
|
588
|
+
/**
|
|
589
|
+
* Per-status guidance for a Preview feature that came back "unavailable". A preview can be
|
|
590
|
+
* gated several different ways and the HTTP status is the best signal for which, so we tailor
|
|
591
|
+
* the next step instead of emitting one catch-all — valuable while these features are in
|
|
592
|
+
* preview and rolling out region by region:
|
|
593
|
+
*
|
|
594
|
+
* - 404 / 501 — the route isn't deployed for this project's region (or the account isn't in
|
|
595
|
+
* the private preview): create a project in a region where the preview is enabled, and
|
|
596
|
+
* confirm your account has preview access.
|
|
597
|
+
* - 503 — the route exists but is refusing right now: either the preview is still coming up,
|
|
598
|
+
* or Neon is having a transient incident. Retry; if it persists it's likely an incident.
|
|
599
|
+
* - anything else — generic "not enabled for your account/region; request access".
|
|
600
|
+
*
|
|
601
|
+
* Only statuses {@link isPreviewFeatureUnavailable} accepts (404/501/503) actually reach
|
|
602
|
+
* this, so there is intentionally no 401/403 branch — those never classify as "unavailable".
|
|
603
|
+
*/
|
|
604
|
+
function previewUnavailableHint(status) {
|
|
605
|
+
switch (status) {
|
|
606
|
+
case 404:
|
|
607
|
+
case 501: return "This usually means the preview isn't available in your project's region yet, or your Neon account isn't in the private preview: create a project in a region where the preview is enabled, and make sure your account has access to the preview.";
|
|
608
|
+
case 503: return "The endpoint is reachable but refused the request — the preview may still be coming up, or Neon may be having a transient incident. Retry shortly; if it keeps failing, check https://neonstatus.com and report it to Neon support.";
|
|
609
|
+
default: return "This usually means the preview isn't enabled for your Neon account or the project's region. Request access to the preview, or use a project in a region where it's available.";
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
501
613
|
* Convert a Preview-feature error into a clear {@link PlatformError} when the feature is
|
|
502
614
|
* unavailable for the project; otherwise pass the original error through unchanged so a
|
|
503
615
|
* genuine failure (auth, transient 5xx, …) keeps its specific code and message.
|
|
616
|
+
*
|
|
617
|
+
* The message names the failing feature, summarizes the response in one short
|
|
618
|
+
* `HTTP <status> <reason>` line, includes the raw Neon API message + request id (valuable
|
|
619
|
+
* signal while the feature is in preview), gives status-specific guidance (see
|
|
620
|
+
* {@link previewUnavailableHint}), and offers removing the feature from the policy as an
|
|
621
|
+
* escape hatch. `status`/`requestId` are also kept on `details` for programmatic consumers.
|
|
504
622
|
*/
|
|
505
623
|
function previewUnavailableError(err, featureLabel) {
|
|
506
624
|
if (!isPreviewFeatureUnavailable(err)) return err;
|
|
507
|
-
const
|
|
508
|
-
|
|
625
|
+
const details = err instanceof PlatformError ? err.details : {};
|
|
626
|
+
const status = typeof details.status === "number" ? details.status : void 0;
|
|
627
|
+
const neonMessage = typeof details.neonMessage === "string" ? details.neonMessage : void 0;
|
|
628
|
+
const requestId = typeof details.requestId === "string" ? details.requestId : void 0;
|
|
629
|
+
const statusText = status ? HTTP_STATUS_TEXT[status] : void 0;
|
|
630
|
+
const apiParts = [
|
|
631
|
+
status ? `HTTP ${status}${statusText ? ` ${statusText}` : ""}` : void 0,
|
|
632
|
+
neonMessage ? `Neon API said: "${neonMessage}"` : void 0,
|
|
633
|
+
requestId ? `request id ${requestId}` : void 0
|
|
634
|
+
].filter((part) => part !== void 0);
|
|
635
|
+
const apiContext = apiParts.length > 0 ? ` (${apiParts.join("; ")})` : "";
|
|
636
|
+
return new PlatformError(ErrorCode.FeatureUnavailable, [
|
|
637
|
+
`${featureLabel} is a Preview feature and isn't available for this Neon project${apiContext}.`,
|
|
638
|
+
previewUnavailableHint(status),
|
|
639
|
+
"If you don't need it, remove the corresponding feature from the `preview` block of your neon.ts and re-run."
|
|
640
|
+
].join(" "), {
|
|
509
641
|
cause: err,
|
|
510
|
-
details: {
|
|
642
|
+
details: {
|
|
643
|
+
feature: featureLabel,
|
|
644
|
+
...status !== void 0 ? { status } : {},
|
|
645
|
+
...requestId !== void 0 ? { requestId } : {}
|
|
646
|
+
}
|
|
511
647
|
});
|
|
512
648
|
}
|
|
513
649
|
function neonAuthResponseToSnapshot(data) {
|