@theholocron/holocron-plugin-clerk 2.0.0-alpha.5 → 2.0.0-alpha.50

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
@@ -5,8 +5,10 @@ against [Clerk's Backend REST API](https://clerk.com/docs/reference/backend-api)
5
5
 
6
6
  ## Install
7
7
 
8
+ <!-- prettier-ignore -->
8
9
  ```bash
9
10
  pnpm add -D @theholocron/holocron-plugin-clerk@alpha
11
+
10
12
  ```
11
13
 
12
14
  ## Auth
@@ -24,12 +26,14 @@ api …`, but the `clerk` CLI just wraps the same REST API holocron talks to.
24
26
 
25
27
  ## Config
26
28
 
29
+ <!-- prettier-ignore -->
27
30
  ```jsonc
28
31
  {
29
- "providers": {
30
- "auth": "clerk",
31
- },
32
+ "providers": {
33
+ "auth": "clerk",
34
+ },
32
35
  }
36
+
33
37
  ```
34
38
 
35
39
  No plugin-level options today. Per-instance scoping (Development vs.
package/dist/index.d.mts CHANGED
@@ -1,67 +1,16 @@
1
- import { Auth, AuthDescription, AuthEvent, AuthIdentity, AuthUser, CreateAuthUserInput, ParseWebhookInput, WebhookDashboardInfo } from "@theholocron/cli";
1
+ import { Auth, AuthDescription, AuthError, AuthEvent, AuthIdentity, AuthUser, CreateAuthUserInput, ParseWebhookInput, ResolveTokenInput, WebhookDashboardInfo } from "@theholocron/cli";
2
+ import { ClerkClient, createClerkClient } from "@theholocron/clerk-client";
2
3
 
3
4
  //#region src/auth.d.ts
4
- /**
5
- * Token resolution for the Clerk 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_CLERK_SECRET_KEY env var (preferred — explicit intent)
11
- * 3. CLERK_SECRET_KEY env var (the default Clerk's docs reference)
12
- * 4. keyring (com.theholocron.cli / "clerk")
13
- * 5. AuthError naming all four options + the bootstrap hint
14
- *
15
- * The key (sk_test_* / sk_live_*) determines which Clerk instance —
16
- * Development or Production — every call hits.
17
- */
18
- declare class AuthError extends Error {
19
- name: string;
20
- }
21
- interface ResolveTokenInput {
22
- /** From `--token` CLI flag. */
23
- cliToken?: string;
24
- /** Env vars; passed in for testability. Defaults to `process.env`. */
25
- env?: NodeJS.ProcessEnv;
26
- /** Keyring lookup fn; passed in for testability. Defaults to `getToken(provider)`. */
27
- keyring?: (provider: string) => string | null;
28
- }
29
- declare function resolveToken(input?: ResolveTokenInput): string;
30
- //#endregion
31
- //#region src/rest.d.ts
32
- /**
33
- * Thin REST wrapper around api.clerk.com/v1.
34
- *
35
- * Same pattern as the github/vercel/neon REST clients — bearer auth,
36
- * JSON-only bodies, transport-failure wrapping with `status: 0` so the
37
- * orchestrator's soft-skip path sees a clear "Clerk GET /path failed"
38
- * message instead of a generic `TypeError: fetch failed`.
39
- */
40
- interface RestClientOptions {
41
- token: string;
42
- fetch?: typeof fetch;
43
- baseUrl?: string;
44
- }
45
- interface RequestOptions {
46
- method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
47
- body?: unknown;
48
- query?: Record<string, string>;
49
- }
50
- declare class ClerkRestClient {
51
- private readonly token;
52
- private readonly fetchImpl;
53
- readonly baseUrl: string;
54
- constructor(opts: RestClientOptions);
55
- request<T>(path: string, opts?: RequestOptions): Promise<T>;
56
- }
5
+ declare const resolveToken: (input?: import("@theholocron/http-client").ResolveTokenInput) => string;
57
6
  //#endregion
58
7
  //#region src/capabilities/auth.d.ts
59
8
  type ClerkAuthOptions = Record<string, never>;
60
9
  declare class ClerkAuth implements Auth {
61
- private readonly rest;
10
+ private readonly client;
62
11
  readonly key: "auth";
63
12
  readonly providerName = "clerk";
64
- constructor(rest: ClerkRestClient, _opts?: ClerkAuthOptions);
13
+ constructor(client: ClerkClient, _opts?: ClerkAuthOptions);
65
14
  describe(): Promise<AuthDescription>;
66
15
  whoami(): Promise<AuthIdentity>;
67
16
  ensureWebhookApp(): Promise<{
@@ -109,7 +58,7 @@ interface ClerkPluginOptions extends ResolveTokenInput {
109
58
  }
110
59
  interface PluginContext {
111
60
  options: ClerkPluginOptions;
112
- rest: ClerkRestClient;
61
+ client: ClerkClient;
113
62
  }
114
63
  declare function createContext(options?: ClerkPluginOptions): PluginContext;
115
64
  declare function auth(ctx: PluginContext): Auth;
@@ -127,4 +76,4 @@ declare function createPlugin(options?: ClerkPluginOptions): {
127
76
  */
128
77
  declare const AUTH_HINT: string;
129
78
  //#endregion
130
- export { AUTH_HINT, AuthError, ClerkAuth, ClerkPluginOptions, ClerkRestClient, PluginContext, ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, auth, createContext, createPlugin, parseWebhook, resolveToken, verifyToken };
79
+ export { AUTH_HINT, AuthError, ClerkAuth, ClerkPluginOptions, PluginContext, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, auth, createClerkClient, createContext, createPlugin, parseWebhook, resolveToken, verifyToken };
package/dist/index.mjs CHANGED
@@ -1,29 +1,12 @@
1
- import { ProviderApiError, WebhookVerificationError, getToken } from "@theholocron/cli";
1
+ import { AuthError, ProviderApiError, WebhookVerificationError, createResolveToken } from "@theholocron/cli";
2
+ import { createClerkClient } from "@theholocron/clerk-client";
2
3
  //#region src/auth.ts
3
- /**
4
- * Token resolution for the Clerk plugin.
5
- *
6
- * Resolution order (matches the standard 4-step precedence set by
7
- * `.notes/tech-auth-bootstrap.spec.md`):
8
- * 1. explicit `cliToken` argument (from `--token` flag)
9
- * 2. HOLOCRON_CLERK_SECRET_KEY env var (preferred — explicit intent)
10
- * 3. CLERK_SECRET_KEY env var (the default Clerk's docs reference)
11
- * 4. keyring (com.theholocron.cli / "clerk")
12
- * 5. AuthError naming all four options + the bootstrap hint
13
- *
14
- * The key (sk_test_* / sk_live_*) determines which Clerk instance —
15
- * Development or Production — every call hits.
16
- */
17
- var AuthError = class extends Error {
18
- name = "AuthError";
19
- };
20
- function resolveToken(input = {}) {
21
- const env = input.env ?? process.env;
22
- const keyring = input.keyring ?? getToken;
23
- const token = input.cliToken || env["HOLOCRON_CLERK_SECRET_KEY"] || env["CLERK_SECRET_KEY"] || keyring("clerk");
24
- if (!token) throw new AuthError("no Clerk secret key found. Pass --token <KEY>, set HOLOCRON_CLERK_SECRET_KEY / CLERK_SECRET_KEY, or run: holocron auth set clerk <KEY>");
25
- return token;
26
- }
4
+ const resolveToken = createResolveToken({
5
+ envName: "HOLOCRON_CLERK_SECRET_KEY",
6
+ vendorEnvName: "CLERK_SECRET_KEY",
7
+ keyringService: "clerk",
8
+ errorMessage: "no Clerk secret key found. Pass --token <KEY>, set HOLOCRON_CLERK_SECRET_KEY / CLERK_SECRET_KEY, or run: holocron auth set clerk <KEY>"
9
+ });
27
10
  //#endregion
28
11
  //#region src/capabilities/auth.ts
29
12
  /**
@@ -52,11 +35,11 @@ function resolveToken(input = {}) {
52
35
  * no per-call instance switch.
53
36
  */
54
37
  var ClerkAuth = class {
55
- rest;
38
+ client;
56
39
  key = "auth";
57
40
  providerName = "clerk";
58
- constructor(rest, _opts = {}) {
59
- this.rest = rest;
41
+ constructor(client, _opts = {}) {
42
+ this.client = client;
60
43
  }
61
44
  async describe() {
62
45
  return {
@@ -67,12 +50,12 @@ var ClerkAuth = class {
67
50
  async whoami() {
68
51
  return {
69
52
  provider: "clerk",
70
- details: { userCount: (await this.rest.request("/users/count")).total_count }
53
+ details: { userCount: (await this.client.users.count()).total_count }
71
54
  };
72
55
  }
73
56
  async ensureWebhookApp() {
74
57
  try {
75
- await this.rest.request("/webhooks/svix", { method: "POST" });
58
+ await this.client.webhooks.ensureSvixApp();
76
59
  return { alreadyExists: false };
77
60
  } catch (err) {
78
61
  if (err instanceof ProviderApiError && isAlreadyExistsError(err)) return { alreadyExists: true };
@@ -80,24 +63,21 @@ var ClerkAuth = class {
80
63
  }
81
64
  }
82
65
  async getWebhookDashboardUrl() {
83
- const body = await this.rest.request("/webhooks/svix_url", { method: "POST" });
66
+ const body = await this.client.webhooks.getSvixUrl();
84
67
  const url = body.url ?? body.svix_url;
85
68
  if (!url) throw new ProviderApiError("Clerk POST /webhooks/svix_url returned 200 but no `url` field", 500, void 0);
86
69
  return { url };
87
70
  }
88
71
  async createUser(input) {
89
- const body = await this.rest.request("/users", {
90
- method: "POST",
91
- body: {
92
- email_address: [input.email],
93
- password: input.password,
94
- ...input.firstName ? { first_name: input.firstName } : {},
95
- ...input.lastName ? { last_name: input.lastName } : {}
96
- }
72
+ const user = await this.client.users.create({
73
+ email_address: [input.email],
74
+ password: input.password,
75
+ ...input.firstName ? { first_name: input.firstName } : {},
76
+ ...input.lastName ? { last_name: input.lastName } : {}
97
77
  });
98
- const email = body.email_addresses[0]?.email_address ?? input.email;
78
+ const email = user.email_addresses[0]?.email_address ?? input.email;
99
79
  return {
100
- id: body.id,
80
+ id: user.id,
101
81
  email
102
82
  };
103
83
  }
@@ -115,60 +95,6 @@ function isAlreadyExistsError(err) {
115
95
  return false;
116
96
  }
117
97
  //#endregion
118
- //#region src/rest.ts
119
- /**
120
- * Thin REST wrapper around api.clerk.com/v1.
121
- *
122
- * Same pattern as the github/vercel/neon REST clients — bearer auth,
123
- * JSON-only bodies, transport-failure wrapping with `status: 0` so the
124
- * orchestrator's soft-skip path sees a clear "Clerk GET /path failed"
125
- * message instead of a generic `TypeError: fetch failed`.
126
- */
127
- var ClerkRestClient = class {
128
- token;
129
- fetchImpl;
130
- baseUrl;
131
- constructor(opts) {
132
- this.token = opts.token;
133
- this.fetchImpl = opts.fetch ?? globalThis.fetch;
134
- let url = opts.baseUrl ?? "https://api.clerk.com/v1";
135
- while (url.endsWith("/")) url = url.slice(0, -1);
136
- this.baseUrl = url;
137
- }
138
- async request(path, opts = {}) {
139
- const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
140
- for (const [k, v] of Object.entries(opts.query ?? {})) url.searchParams.set(k, v);
141
- const fullUrl = url.toString();
142
- const headers = {
143
- authorization: `Bearer ${this.token}`,
144
- accept: "application/json"
145
- };
146
- const init = {
147
- method: opts.method ?? "GET",
148
- headers
149
- };
150
- if (opts.body !== void 0) {
151
- headers["content-type"] = "application/json";
152
- init.body = JSON.stringify(opts.body);
153
- }
154
- let res;
155
- try {
156
- res = await this.fetchImpl(fullUrl, init);
157
- } catch (err) {
158
- const detail = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
159
- throw new ProviderApiError(`Clerk ${init.method} ${path} failed: ${detail}`, 0, void 0);
160
- }
161
- if (!res.ok) {
162
- const body = await res.text().catch(() => "");
163
- throw new ProviderApiError(`Clerk ${init.method} ${path} → ${res.status}`, res.status, body);
164
- }
165
- if (res.status === 204) return void 0;
166
- const text = await res.text();
167
- if (!text) return void 0;
168
- return JSON.parse(text);
169
- }
170
- };
171
- //#endregion
172
98
  //#region src/parse-webhook.ts
173
99
  /**
174
100
  * Translates a Clerk webhook delivery into the normalized `AuthEvent`
@@ -248,15 +174,16 @@ async function verifySignature(input) {
248
174
  * canonical "is this secret key valid?" endpoint.
249
175
  */
250
176
  async function verifyToken(token, opts = {}) {
251
- const restOpts = { token };
252
- if (opts.baseUrl !== void 0) restOpts.baseUrl = opts.baseUrl;
253
- if (opts.fetch !== void 0) restOpts.fetch = opts.fetch;
254
- const rest = new ClerkRestClient(restOpts);
177
+ const client = createClerkClient({
178
+ token,
179
+ baseUrl: opts.baseUrl,
180
+ fetch: opts.fetch
181
+ });
255
182
  try {
256
- const instance = await rest.request("/instance");
183
+ const inst = await client.instance.get();
257
184
  return {
258
185
  ok: true,
259
- subject: `${instance?.environment_type ?? "unknown"} instance ${instance?.id ?? "unknown"}`
186
+ subject: `${inst?.environment_type ?? "unknown"} instance ${inst?.id ?? "unknown"}`
260
187
  };
261
188
  } catch (err) {
262
189
  return {
@@ -268,16 +195,17 @@ async function verifyToken(token, opts = {}) {
268
195
  //#endregion
269
196
  //#region src/index.ts
270
197
  function createContext(options = {}) {
271
- const restOpts = { token: resolveToken(options) };
272
- if (options.baseUrl !== void 0) restOpts.baseUrl = options.baseUrl;
273
- if (options.fetch !== void 0) restOpts.fetch = options.fetch;
274
198
  return {
275
199
  options,
276
- rest: new ClerkRestClient(restOpts)
200
+ client: createClerkClient({
201
+ token: resolveToken(options),
202
+ baseUrl: options.baseUrl,
203
+ fetch: options.fetch
204
+ })
277
205
  };
278
206
  }
279
207
  function auth(ctx) {
280
- return new ClerkAuth(ctx.rest);
208
+ return new ClerkAuth(ctx.client);
281
209
  }
282
210
  function createPlugin(options = {}) {
283
211
  const ctx = createContext(options);
@@ -294,4 +222,4 @@ function createPlugin(options = {}) {
294
222
  */
295
223
  const AUTH_HINT = "grab your SECRET key (sk_test_* / sk_live_*) at https://dashboard.clerk.com → API Keys, then run: holocron auth set clerk <KEY>. Do NOT use the publishable key.";
296
224
  //#endregion
297
- export { AUTH_HINT, AuthError, ClerkAuth, ClerkRestClient, auth, createContext, createPlugin, parseWebhook, resolveToken, verifyToken };
225
+ export { AUTH_HINT, AuthError, ClerkAuth, auth, createClerkClient, createContext, createPlugin, parseWebhook, resolveToken, verifyToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/holocron-plugin-clerk",
3
- "version": "2.0.0-alpha.5",
3
+ "version": "2.0.0-alpha.50",
4
4
  "description": "Holocron plugin for Clerk. Implements the auth capability against Clerk's Backend REST API.",
5
5
  "homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-clerk#readme",
6
6
  "bugs": "https://github.com/theholocron/holocron/issues",
@@ -21,19 +21,25 @@
21
21
  }
22
22
  },
23
23
  "peerDependencies": {
24
- "@theholocron/cli": "2.0.0-alpha.5"
24
+ "@theholocron/clerk-client": "^0.7.0",
25
+ "@theholocron/cli": "2.0.0-alpha.50"
25
26
  },
26
27
  "devDependencies": {
27
- "@theholocron/tsconfig": "^4.1.0",
28
- "@tsconfig/node-lts": "^24.0.0",
29
- "@vitest/coverage-v8": "^3.2.6",
30
- "eslint": "^9.36.0",
31
- "globals": "^16.5.0",
32
- "typescript": "^5.9.3",
33
- "vitest": "^3.2.6",
28
+ "@theholocron/clerk-client": "^0.7.0",
29
+ "@types/node": "^26",
30
+ "@theholocron/eslint-config": "^5.2.0",
31
+ "@theholocron/tsconfig": "^6.0.0",
32
+ "@theholocron/tsdown-config": "^5.1.2",
33
+ "@theholocron/vitest-config": "^5.2.0",
34
+ "@vitest/eslint-plugin": "^1.6.23",
35
+ "eslint": "^10.7.0",
36
+ "eslint-plugin-n": "^18.2.2",
37
+ "globals": "^17.7.0",
34
38
  "tsdown": "^0.22.3",
35
39
  "tsx": "^4.22.4",
36
- "@theholocron/cli": "2.0.0-alpha.5"
40
+ "typescript": "^5.9.3",
41
+ "vitest": "^4.1.10",
42
+ "@theholocron/cli": "2.0.0-alpha.50"
37
43
  },
38
44
  "publishConfig": {
39
45
  "access": "public"