@capxul/sdk-react 0.1.0-alpha.6 → 0.1.0-alpha.8

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @capxul/sdk-react
2
2
 
3
- ## 0.1.0-alpha.6
3
+ ## 0.1.0-alpha.8
4
4
 
5
5
  ### Minor Changes
6
6
 
@@ -32,21 +32,7 @@
32
32
  ### Patch Changes
33
33
 
34
34
  - Updated dependencies [57203a4]
35
- - @capxul/sdk@0.1.0-alpha.6
36
-
37
- ## 0.1.0-alpha.5
38
-
39
- ### Patch Changes
40
-
41
- - Widen the React peer dependency to `>=18.2.0 <20` so React 18-era Ink 5
42
- CLI apps can install `@capxul/sdk-react` without forcing React 19 into
43
- Ink's React reconciler. The provider and hooks only use React 18-compatible
44
- APIs (`createContext`, `useContext`, `useMemo`, `useEffect`, `useState`, and
45
- `useSyncExternalStore`), preserving React 19 compatibility while unblocking
46
- React 18 hosts.
47
-
48
- - Updated dependencies
49
- - @capxul/sdk@0.1.0-alpha.5
35
+ - @capxul/sdk@0.1.0-alpha.8
50
36
 
51
37
  ## 0.1.0-alpha.4
52
38
 
package/README.md CHANGED
@@ -40,7 +40,12 @@ import { CapxulProvider } from "@capxul/sdk-react";
40
40
 
41
41
  export default function RootLayout({ children }) {
42
42
  return (
43
- <CapxulProvider publishableKey={process.env.NEXT_PUBLIC_CAPXUL_KEY!}>
43
+ <CapxulProvider
44
+ config={{
45
+ mode: "publishable-key",
46
+ publishableKey: process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY!,
47
+ }}
48
+ >
44
49
  {children}
45
50
  </CapxulProvider>
46
51
  );
@@ -119,6 +124,21 @@ subscription against the transport singleton — only the components
119
124
  that actually call it re-render when state changes. The provider
120
125
  itself never re-renders.
121
126
 
127
+ ## Publishable-key proof status
128
+
129
+ The publishable-key path is runtime-proven where the repo can run it
130
+ without secrets:
131
+
132
+ | Surface | Runtime proof | Expected safe signal |
133
+ |---|---|---|
134
+ | Provider + `useMe()` | `corepack pnpm --filter @capxul/sdk-react check-types` plus the headless proof tests under `packages/sdk-react/ops/proof` | provider reaches `useCapxulStatus().status === "ready"` after one bootstrap request |
135
+ | Reference CLI mock | `corepack pnpm --filter @capxul/reference-cli build && node apps/reference-cli/dist/cli.js bootstrap probe --mock --json` | `ok:true`, `mode:"publishable-key"`, `bootstrapRequests:1`, `authRequests:1`, `keyLengthClass:"provided"` |
136
+ | Reference CLI live | same command without `--mock`, with `CAPXUL_REF_PUBLISHABLE_KEY` and optional `CAPXUL_REF_BOOTSTRAP_URL` set locally | success only when the key/origin/runtime are valid; otherwise sanitized SDK error JSON |
137
+
138
+ Do not paste or commit publishable keys, session tokens, Convex JWTs,
139
+ cookies, or provider payloads. Proof output reports only
140
+ `keyLengthClass`.
141
+
122
142
  ## Hooks catalogue
123
143
 
124
144
  | Surface | Hook |
package/dist/index.cjs CHANGED
@@ -110,7 +110,12 @@ var Errors = {
110
110
  "Idempotency key was already used for a different request",
111
111
  { details }
112
112
  ),
113
- emailDeliveryFailed: (detail) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`),
113
+ emailDeliveryFailed: (detail, details) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
114
+ details
115
+ }),
116
+ rateLimited: (details) => new CapxulError("RATE_LIMITED", "Request was rate limited", {
117
+ details: { ...details }
118
+ }),
114
119
  internalError: (reason) => new CapxulError("INTERNAL_ERROR", `Internal error: ${reason}`),
115
120
  /**
116
121
  * Verification gate. Surfaced when a request hits a verification
@@ -217,6 +222,48 @@ function createReactDataClient(convexUrl, sessionStore) {
217
222
  }
218
223
  };
219
224
  }
225
+ function createLazyReactDataClient(transport, sessionStore, createClient = createReactDataClient) {
226
+ let client = null;
227
+ let initializeClient = null;
228
+ let closed = false;
229
+ function closedError() {
230
+ return new Error("Capxul React data client is closed");
231
+ }
232
+ async function getClient() {
233
+ if (closed) throw closedError();
234
+ if (client) return client;
235
+ initializeClient ??= (async () => {
236
+ const runtime = await transport.ensureRuntime();
237
+ if (closed) throw closedError();
238
+ const nextClient = createClient(runtime.convexUrl, sessionStore);
239
+ if (closed) {
240
+ nextClient.close();
241
+ throw closedError();
242
+ }
243
+ client = nextClient;
244
+ return nextClient;
245
+ })().catch((error) => {
246
+ if (!closed) {
247
+ initializeClient = null;
248
+ }
249
+ throw error;
250
+ });
251
+ return initializeClient;
252
+ }
253
+ return {
254
+ query: async (name, args) => (await getClient()).query(name, args),
255
+ mutation: async (name, args) => (await getClient()).mutation(name, args),
256
+ action: async (name, args) => (await getClient()).action?.(name, args),
257
+ refreshAuth: () => {
258
+ client?.refreshAuth();
259
+ },
260
+ close: () => {
261
+ closed = true;
262
+ client?.close();
263
+ client = null;
264
+ }
265
+ };
266
+ }
220
267
  function CapxulProvider({
221
268
  config,
222
269
  sessionStore,
@@ -246,12 +293,12 @@ function CapxulProvider({
246
293
  function buildWiring(config, sessionStore) {
247
294
  const validated = createCapxulConfig(config);
248
295
  const transport = sdk.makeHttpTransport(validated);
249
- const dataClient = validated.mode === "build-time-urls" ? createReactDataClient(validated.convexUrl, sessionStore) : null;
296
+ const dataClient = validated.mode === "build-time-urls" ? createReactDataClient(validated.convexUrl, sessionStore) : createLazyReactDataClient(transport, sessionStore);
250
297
  const sdkConfig = {
251
298
  _transport: transport,
252
299
  publishableKey: validated.mode === "publishable-key" ? validated.publishableKey : void 0,
253
300
  data: dataClient ?? void 0,
254
- auth: dataClient ? {
301
+ auth: validated.mode === "build-time-urls" || validated.mode === "publishable-key" ? {
255
302
  // The auth client builds the BetterAuth root URL from this
256
303
  // value. Convex's `.cloud` URL is the wrong host (BetterAuth
257
304
  // is mounted on the `.site` URL), but the build-time-urls
package/dist/index.js CHANGED
@@ -108,7 +108,12 @@ var Errors = {
108
108
  "Idempotency key was already used for a different request",
109
109
  { details }
110
110
  ),
111
- emailDeliveryFailed: (detail) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`),
111
+ emailDeliveryFailed: (detail, details) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
112
+ details
113
+ }),
114
+ rateLimited: (details) => new CapxulError("RATE_LIMITED", "Request was rate limited", {
115
+ details: { ...details }
116
+ }),
112
117
  internalError: (reason) => new CapxulError("INTERNAL_ERROR", `Internal error: ${reason}`),
113
118
  /**
114
119
  * Verification gate. Surfaced when a request hits a verification
@@ -215,6 +220,48 @@ function createReactDataClient(convexUrl, sessionStore) {
215
220
  }
216
221
  };
217
222
  }
223
+ function createLazyReactDataClient(transport, sessionStore, createClient = createReactDataClient) {
224
+ let client = null;
225
+ let initializeClient = null;
226
+ let closed = false;
227
+ function closedError() {
228
+ return new Error("Capxul React data client is closed");
229
+ }
230
+ async function getClient() {
231
+ if (closed) throw closedError();
232
+ if (client) return client;
233
+ initializeClient ??= (async () => {
234
+ const runtime = await transport.ensureRuntime();
235
+ if (closed) throw closedError();
236
+ const nextClient = createClient(runtime.convexUrl, sessionStore);
237
+ if (closed) {
238
+ nextClient.close();
239
+ throw closedError();
240
+ }
241
+ client = nextClient;
242
+ return nextClient;
243
+ })().catch((error) => {
244
+ if (!closed) {
245
+ initializeClient = null;
246
+ }
247
+ throw error;
248
+ });
249
+ return initializeClient;
250
+ }
251
+ return {
252
+ query: async (name, args) => (await getClient()).query(name, args),
253
+ mutation: async (name, args) => (await getClient()).mutation(name, args),
254
+ action: async (name, args) => (await getClient()).action?.(name, args),
255
+ refreshAuth: () => {
256
+ client?.refreshAuth();
257
+ },
258
+ close: () => {
259
+ closed = true;
260
+ client?.close();
261
+ client = null;
262
+ }
263
+ };
264
+ }
218
265
  function CapxulProvider({
219
266
  config,
220
267
  sessionStore,
@@ -244,12 +291,12 @@ function CapxulProvider({
244
291
  function buildWiring(config, sessionStore) {
245
292
  const validated = createCapxulConfig(config);
246
293
  const transport = makeHttpTransport(validated);
247
- const dataClient = validated.mode === "build-time-urls" ? createReactDataClient(validated.convexUrl, sessionStore) : null;
294
+ const dataClient = validated.mode === "build-time-urls" ? createReactDataClient(validated.convexUrl, sessionStore) : createLazyReactDataClient(transport, sessionStore);
248
295
  const sdkConfig = {
249
296
  _transport: transport,
250
297
  publishableKey: validated.mode === "publishable-key" ? validated.publishableKey : void 0,
251
298
  data: dataClient ?? void 0,
252
- auth: dataClient ? {
299
+ auth: validated.mode === "build-time-urls" || validated.mode === "publishable-key" ? {
253
300
  // The auth client builds the BetterAuth root URL from this
254
301
  // value. Convex's `.cloud` URL is the wrong host (BetterAuth
255
302
  // is mounted on the `.site` URL), but the build-time-urls
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capxul/sdk-react",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "React provider + hooks for the @capxul/sdk headless client.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -46,13 +46,13 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@capxul/sdk": "0.1.0-alpha.6"
49
+ "@capxul/sdk": "0.1.0-alpha.8"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@tanstack/react-query": "^5",
53
53
  "@xstate/react": "^5",
54
54
  "convex": ">=1.0.0",
55
- "react": ">=18.2.0 <20",
55
+ "react": ">=19.0.0",
56
56
  "viem": ">=2.0.0"
57
57
  },
58
58
  "devDependencies": {