@theholocron/holocron-plugin-neon 2.0.0-alpha.20 → 2.0.0-alpha.22
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 +3 -1
- package/dist/index.d.mts +8 -50
- package/dist/index.mjs +26 -83
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<!-- editorconfig-checker-disable-file -->
|
|
2
|
+
|
|
1
3
|
# `@theholocron/holocron-plugin-neon`
|
|
2
4
|
|
|
3
5
|
Neon plugin for [Holocron](../cli). Implements the `storage`
|
|
@@ -28,7 +30,7 @@ Token resolution order:
|
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
- `projectId` (required) — the Neon project id. The plugin binds to
|
|
31
|
-
|
|
33
|
+
this project; every method operates within it.
|
|
32
34
|
|
|
33
35
|
## What's implemented
|
|
34
36
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,56 +1,14 @@
|
|
|
1
|
-
import { ConnectionStringOptions, Storage, StorageBranch } from "@theholocron/cli";
|
|
1
|
+
import { AuthError, ConnectionStringOptions, ResolveTokenInput, RestClient, RestClient as RestClient$1, Storage, StorageBranch } from "@theholocron/cli";
|
|
2
2
|
|
|
3
3
|
//#region src/auth.d.ts
|
|
4
|
-
|
|
5
|
-
* Token resolution for the Neon plugin.
|
|
6
|
-
*
|
|
7
|
-
* Resolution order (matches the standard 4-step precedence set by
|
|
8
|
-
* `.notes/tech-auth-bootstrap.spec.md`):
|
|
9
|
-
* 1. explicit `cliToken` argument (from `--token` flag)
|
|
10
|
-
* 2. HOLOCRON_NEON_API_KEY env var (preferred — explicit intent)
|
|
11
|
-
* 3. NEON_API_KEY env var (the default Neon CLI reads)
|
|
12
|
-
* 4. keyring (com.theholocron.cli / "neon")
|
|
13
|
-
* 5. AuthError naming all four options + the bootstrap hint
|
|
14
|
-
*/
|
|
15
|
-
declare class AuthError extends Error {
|
|
16
|
-
name: string;
|
|
17
|
-
}
|
|
18
|
-
interface ResolveTokenInput {
|
|
19
|
-
/** From `--token` CLI flag. */
|
|
20
|
-
cliToken?: string;
|
|
21
|
-
/** Env vars; passed in for testability. Defaults to `process.env`. */
|
|
22
|
-
env?: NodeJS.ProcessEnv;
|
|
23
|
-
/** Keyring lookup fn; passed in for testability. Defaults to `getToken(provider)`. */
|
|
24
|
-
keyring?: (provider: string) => string | null;
|
|
25
|
-
}
|
|
26
|
-
declare function resolveToken(input?: ResolveTokenInput): string;
|
|
4
|
+
declare const resolveToken: (input?: import("@theholocron/http-client").ResolveTokenInput) => string;
|
|
27
5
|
//#endregion
|
|
28
6
|
//#region src/rest.d.ts
|
|
29
|
-
|
|
30
|
-
* Thin REST wrapper around console.neon.tech/api/v2.
|
|
31
|
-
*
|
|
32
|
-
* Same pattern as the GitHub + Vercel REST clients — bearer auth,
|
|
33
|
-
* JSON-only bodies, transport-failure wrapping with status: 0 so the
|
|
34
|
-
* orchestrator's soft-skip path sees a clear "Neon GET /path failed"
|
|
35
|
-
* message instead of a generic `TypeError: fetch failed`.
|
|
36
|
-
*/
|
|
37
|
-
interface RestClientOptions {
|
|
7
|
+
declare function createNeonRestClient(opts: {
|
|
38
8
|
token: string;
|
|
39
|
-
fetch?: typeof fetch;
|
|
40
9
|
baseUrl?: string;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
44
|
-
body?: unknown;
|
|
45
|
-
query?: Record<string, string>;
|
|
46
|
-
}
|
|
47
|
-
declare class NeonRestClient {
|
|
48
|
-
private readonly token;
|
|
49
|
-
private readonly fetchImpl;
|
|
50
|
-
readonly baseUrl: string;
|
|
51
|
-
constructor(opts: RestClientOptions);
|
|
52
|
-
request<T>(path: string, opts?: RequestOptions): Promise<T>;
|
|
53
|
-
}
|
|
10
|
+
fetch?: typeof fetch;
|
|
11
|
+
}): RestClient$1;
|
|
54
12
|
//#endregion
|
|
55
13
|
//#region src/capabilities/storage.d.ts
|
|
56
14
|
interface StorageOptions {
|
|
@@ -61,7 +19,7 @@ declare class NeonStorage implements Storage {
|
|
|
61
19
|
readonly key: "storage";
|
|
62
20
|
readonly providerName = "neon";
|
|
63
21
|
private readonly base;
|
|
64
|
-
constructor(rest:
|
|
22
|
+
constructor(rest: RestClient, opts: StorageOptions);
|
|
65
23
|
getConnectionString(scope: string, options?: ConnectionStringOptions): Promise<string>;
|
|
66
24
|
listBranches(): Promise<StorageBranch[]>;
|
|
67
25
|
createBranch(input: {
|
|
@@ -118,7 +76,7 @@ interface NeonPluginOptions extends ResolveTokenInput {
|
|
|
118
76
|
}
|
|
119
77
|
interface PluginContext {
|
|
120
78
|
options: NeonPluginOptions;
|
|
121
|
-
rest:
|
|
79
|
+
rest: RestClient;
|
|
122
80
|
}
|
|
123
81
|
declare function createContext(options: NeonPluginOptions): PluginContext;
|
|
124
82
|
declare function storage(ctx: PluginContext): Storage;
|
|
@@ -136,4 +94,4 @@ declare function createPlugin(options: NeonPluginOptions): {
|
|
|
136
94
|
*/
|
|
137
95
|
declare const AUTH_HINT: string;
|
|
138
96
|
//#endregion
|
|
139
|
-
export { AUTH_HINT, AuthError, NeonPluginOptions,
|
|
97
|
+
export { AUTH_HINT, AuthError, NeonPluginOptions, NeonStorage, PluginContext, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createContext, createNeonRestClient, createPlugin, resolveToken, storage, verifyToken };
|
package/dist/index.mjs
CHANGED
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
import { ProviderApiError,
|
|
1
|
+
import { AuthError, ProviderApiError, createResolveToken, createRestClient } from "@theholocron/cli";
|
|
2
2
|
//#region src/auth.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* 2. HOLOCRON_NEON_API_KEY env var (preferred — explicit intent)
|
|
10
|
-
* 3. NEON_API_KEY env var (the default Neon CLI reads)
|
|
11
|
-
* 4. keyring (com.theholocron.cli / "neon")
|
|
12
|
-
* 5. AuthError naming all four options + the bootstrap hint
|
|
13
|
-
*/
|
|
14
|
-
var AuthError = class extends Error {
|
|
15
|
-
name = "AuthError";
|
|
16
|
-
};
|
|
17
|
-
function resolveToken(input = {}) {
|
|
18
|
-
const env = input.env ?? process.env;
|
|
19
|
-
const keyring = input.keyring ?? getToken;
|
|
20
|
-
const token = input.cliToken || env["HOLOCRON_NEON_API_KEY"] || env["NEON_API_KEY"] || keyring("neon");
|
|
21
|
-
if (!token) throw new AuthError("no Neon API key found. Pass --token <KEY>, set HOLOCRON_NEON_API_KEY / NEON_API_KEY, or run: holocron auth set neon <KEY>");
|
|
22
|
-
return token;
|
|
23
|
-
}
|
|
3
|
+
const resolveToken = createResolveToken({
|
|
4
|
+
envName: "HOLOCRON_NEON_API_KEY",
|
|
5
|
+
vendorEnvName: "NEON_API_KEY",
|
|
6
|
+
keyringService: "neon",
|
|
7
|
+
errorMessage: "no Neon API key found. Pass --token <KEY>, set HOLOCRON_NEON_API_KEY / NEON_API_KEY, or run: holocron auth set neon <KEY>"
|
|
8
|
+
});
|
|
24
9
|
//#endregion
|
|
25
10
|
//#region src/capabilities/storage.ts
|
|
26
11
|
/**
|
|
@@ -113,58 +98,14 @@ function mapBranch(raw) {
|
|
|
113
98
|
}
|
|
114
99
|
//#endregion
|
|
115
100
|
//#region src/rest.ts
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
var NeonRestClient = class {
|
|
125
|
-
token;
|
|
126
|
-
fetchImpl;
|
|
127
|
-
baseUrl;
|
|
128
|
-
constructor(opts) {
|
|
129
|
-
this.token = opts.token;
|
|
130
|
-
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
131
|
-
let url = opts.baseUrl ?? "https://console.neon.tech/api/v2";
|
|
132
|
-
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
133
|
-
this.baseUrl = url;
|
|
134
|
-
}
|
|
135
|
-
async request(path, opts = {}) {
|
|
136
|
-
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
|
|
137
|
-
for (const [k, v] of Object.entries(opts.query ?? {})) url.searchParams.set(k, v);
|
|
138
|
-
const fullUrl = url.toString();
|
|
139
|
-
const headers = {
|
|
140
|
-
authorization: `Bearer ${this.token}`,
|
|
141
|
-
accept: "application/json"
|
|
142
|
-
};
|
|
143
|
-
const init = {
|
|
144
|
-
method: opts.method ?? "GET",
|
|
145
|
-
headers
|
|
146
|
-
};
|
|
147
|
-
if (opts.body !== void 0) {
|
|
148
|
-
headers["content-type"] = "application/json";
|
|
149
|
-
init.body = JSON.stringify(opts.body);
|
|
150
|
-
}
|
|
151
|
-
let res;
|
|
152
|
-
try {
|
|
153
|
-
res = await this.fetchImpl(fullUrl, init);
|
|
154
|
-
} catch (err) {
|
|
155
|
-
const detail = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
156
|
-
throw new ProviderApiError(`Neon ${init.method} ${path} failed: ${detail}`, 0, void 0);
|
|
157
|
-
}
|
|
158
|
-
if (!res.ok) {
|
|
159
|
-
const body = await res.text().catch(() => "");
|
|
160
|
-
throw new ProviderApiError(`Neon ${init.method} ${path} → ${res.status}`, res.status, body);
|
|
161
|
-
}
|
|
162
|
-
if (res.status === 204) return void 0;
|
|
163
|
-
const text = await res.text();
|
|
164
|
-
if (!text) return void 0;
|
|
165
|
-
return JSON.parse(text);
|
|
166
|
-
}
|
|
167
|
-
};
|
|
101
|
+
function createNeonRestClient(opts) {
|
|
102
|
+
return createRestClient({
|
|
103
|
+
baseUrl: opts.baseUrl ?? "https://console.neon.tech/api/v2",
|
|
104
|
+
token: opts.token,
|
|
105
|
+
vendor: "Neon",
|
|
106
|
+
fetch: opts.fetch
|
|
107
|
+
});
|
|
108
|
+
}
|
|
168
109
|
//#endregion
|
|
169
110
|
//#region src/verify-token.ts
|
|
170
111
|
/**
|
|
@@ -174,10 +115,11 @@ var NeonRestClient = class {
|
|
|
174
115
|
* shape.
|
|
175
116
|
*/
|
|
176
117
|
async function verifyToken(token, opts = {}) {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
118
|
+
const rest = createNeonRestClient({
|
|
119
|
+
token,
|
|
120
|
+
baseUrl: opts.baseUrl,
|
|
121
|
+
fetch: opts.fetch
|
|
122
|
+
});
|
|
181
123
|
try {
|
|
182
124
|
const me = await rest.request("/users/me");
|
|
183
125
|
return {
|
|
@@ -195,12 +137,13 @@ async function verifyToken(token, opts = {}) {
|
|
|
195
137
|
//#region src/index.ts
|
|
196
138
|
function createContext(options) {
|
|
197
139
|
if (!options.projectId) throw new Error("@theholocron/holocron-plugin-neon requires `projectId` in options");
|
|
198
|
-
const restOpts = { token: resolveToken(options) };
|
|
199
|
-
if (options.baseUrl !== void 0) restOpts.baseUrl = options.baseUrl;
|
|
200
|
-
if (options.fetch !== void 0) restOpts.fetch = options.fetch;
|
|
201
140
|
return {
|
|
202
141
|
options,
|
|
203
|
-
rest:
|
|
142
|
+
rest: createNeonRestClient({
|
|
143
|
+
token: resolveToken(options),
|
|
144
|
+
baseUrl: options.baseUrl,
|
|
145
|
+
fetch: options.fetch
|
|
146
|
+
})
|
|
204
147
|
};
|
|
205
148
|
}
|
|
206
149
|
function storage(ctx) {
|
|
@@ -221,4 +164,4 @@ function createPlugin(options) {
|
|
|
221
164
|
*/
|
|
222
165
|
const AUTH_HINT = "generate a Neon API key at https://console.neon.tech/app/settings/api-keys, then run: holocron auth set neon <KEY>";
|
|
223
166
|
//#endregion
|
|
224
|
-
export { AUTH_HINT, AuthError,
|
|
167
|
+
export { AUTH_HINT, AuthError, NeonStorage, createContext, createNeonRestClient, createPlugin, resolveToken, storage, verifyToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-neon",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.22",
|
|
4
4
|
"description": "Holocron plugin for Neon. Implements the storage capability against Neon's REST API — branch ops, connection strings, extensions.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-neon#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
@@ -21,13 +21,14 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@theholocron/cli": "2.0.0-alpha.
|
|
24
|
+
"@theholocron/cli": "2.0.0-alpha.22"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"@theholocron/
|
|
29
|
-
"@tsconfig
|
|
30
|
-
"@
|
|
27
|
+
"@types/node": "^26",
|
|
28
|
+
"@theholocron/eslint-config": "^5.2.0",
|
|
29
|
+
"@theholocron/tsconfig": "^6.0.0",
|
|
30
|
+
"@theholocron/tsdown-config": "^5.1.2",
|
|
31
|
+
"@theholocron/vitest-config": "^5.2.0",
|
|
31
32
|
"@vitest/eslint-plugin": "^1.6.23",
|
|
32
33
|
"eslint": "^10.7.0",
|
|
33
34
|
"eslint-plugin-n": "^18.2.2",
|
|
@@ -35,8 +36,8 @@
|
|
|
35
36
|
"tsdown": "^0.22.3",
|
|
36
37
|
"tsx": "^4.22.4",
|
|
37
38
|
"typescript": "^5.9.3",
|
|
38
|
-
"vitest": "^
|
|
39
|
-
"@theholocron/cli": "2.0.0-alpha.
|
|
39
|
+
"vitest": "^4.1.10",
|
|
40
|
+
"@theholocron/cli": "2.0.0-alpha.22"
|
|
40
41
|
},
|
|
41
42
|
"publishConfig": {
|
|
42
43
|
"access": "public"
|