@synap-core/sdk 1.2.0 → 1.3.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.
Files changed (42) hide show
  1. package/README.md +80 -0
  2. package/dist/chunk-J7XHAAFJ.js +79 -0
  3. package/dist/chunk-J7XHAAFJ.js.map +1 -0
  4. package/dist/{client-C5sF20f1.d.cts → client-CVUT1Mcf.d.cts} +1 -1
  5. package/dist/{client-C5sF20f1.d.ts → client-CVUT1Mcf.d.ts} +1 -1
  6. package/dist/connect.browser.cjs +22 -0
  7. package/dist/connect.browser.cjs.map +1 -0
  8. package/dist/connect.browser.d.cts +2 -0
  9. package/dist/connect.browser.d.ts +2 -0
  10. package/dist/connect.browser.js +5 -0
  11. package/dist/connect.browser.js.map +1 -0
  12. package/dist/connect.cjs +1206 -0
  13. package/dist/connect.cjs.map +1 -0
  14. package/dist/connect.d.cts +442 -0
  15. package/dist/connect.d.ts +442 -0
  16. package/dist/connect.js +1092 -0
  17. package/dist/connect.js.map +1 -0
  18. package/dist/public.d.cts +2 -2
  19. package/dist/public.d.ts +2 -2
  20. package/dist/testing.cjs +304 -19
  21. package/dist/testing.cjs.map +1 -1
  22. package/dist/testing.d.cts +73 -2
  23. package/dist/testing.d.ts +73 -2
  24. package/dist/testing.js +281 -19
  25. package/dist/testing.js.map +1 -1
  26. package/dist/types-DpqPNaaa.d.cts +76 -0
  27. package/dist/types-DpqPNaaa.d.ts +76 -0
  28. package/package.json +11 -3
  29. package/src/connect/auth.ts +405 -0
  30. package/src/connect/browser.ts +10 -0
  31. package/src/connect/client.ts +193 -0
  32. package/src/connect/discovery.ts +262 -0
  33. package/src/connect/encoding.ts +93 -0
  34. package/src/connect/errors.ts +115 -0
  35. package/src/connect/flow.ts +449 -0
  36. package/src/connect/guard.ts +25 -0
  37. package/src/connect/index.ts +59 -0
  38. package/src/connect/seal.ts +119 -0
  39. package/src/connect/store.ts +151 -0
  40. package/src/connect/types.ts +80 -0
  41. package/src/testing/auth-server.ts +380 -0
  42. package/src/testing/index.ts +5 -0
package/README.md CHANGED
@@ -119,6 +119,86 @@ SDK refuses out-of-bounds `offset`/`limit`/`sort` locally, without a request),
119
119
  pass `ifNoneMatch` from a browser: the doors allow no cross-origin request header
120
120
  but `Content-Type`, so the preflight fails (browsers revalidate on their own).
121
121
 
122
+ ## Pod-app sign-in (server-only): `@synap-core/sdk/connect` (1.3.0)
123
+
124
+ An app that reads and writes a person's OWN pod connects as an OAuth 2.1 public
125
+ client of that pod: its `client_id` is the https URL of its client metadata
126
+ document (CIMD), PKCE S256, every answer checked for the pod's `iss`. The pod
127
+ token stays on the app's server, sealed (AES-GCM) in an HttpOnly cookie; the
128
+ browser never sees it. Framework-free and Web-standard (`Request` → `Response`,
129
+ WebCrypto): Next route handlers and `proxy.ts`, Hono, Remix, edge runtimes.
130
+ **Server-only**: it throws when loaded in a browser, and a browser bundle
131
+ resolves it to a stub that throws.
132
+
133
+ ```typescript
134
+ // lib/synap/pod-app/auth.ts (server-only)
135
+ import { createPodAppAuth } from "@synap-core/sdk/connect";
136
+
137
+ export const synap = createPodAppAuth({
138
+ appUrl: process.env.SITE_URL!, // https://studio.example.com
139
+ clientName: "Studio",
140
+ requests: [
141
+ { permission: "entity.post.read", reason: "Show your posts" },
142
+ { permission: "entity.post.create", reason: "Save drafts to your pod" },
143
+ ],
144
+ secret: process.env.SYNAP_APP_SESSION_SECRET!, // ≥ 32 chars, never NEXT_PUBLIC_
145
+ });
146
+
147
+ // app/.well-known/synap-client.json/route.ts export const GET = synap.handlers.clientMetadata;
148
+ // app/api/synap/connect/route.ts export const POST = synap.handlers.connect; // form { pod, next?, space? }
149
+ // app/api/synap/callback/route.ts export const GET = synap.handlers.callback;
150
+ // app/api/synap/disconnect/route.ts export const POST = synap.handlers.disconnect; // form { next? }
151
+ ```
152
+
153
+ ```typescript
154
+ // proxy.ts: refresh before render (server components cannot set cookies)
155
+ const out = await synap.refreshIfNeeded(request);
156
+ // out.status: none | skipped | fresh | refreshed | kept | expired | revoked | unavailable
157
+ // send out.setCookies on the response; forward out.cookieHeader (when set) as the request's Cookie
158
+
159
+ // a server component / action (no Request there: pass the Cookie header)
160
+ const connection = await synap.readConnection((await headers()).get("cookie"));
161
+ const pod = synap.client(connection!);
162
+ const res = await pod.post("/entities", { profileSlug: "post", title: "Hello" });
163
+ if (res.ok && res.status === "proposed") showPending(res.reviewUrl); // success: queued for review, never retry
164
+ if (!res.ok) res.error.code; // expired | unauthorized | forbidden | door_unsupported | rate_limited (retryAfterSec) | pod_unreachable | …
165
+ ```
166
+
167
+ - **Discovery**: `discoverPod("pod.example.com")` fetches the pod's RFC 8414
168
+ metadata and requires `issuer` equal to the address, CIMD, `iss` on answers and
169
+ S256 (`not_a_pod`, `pod_unsupported`, `pod_unreachable`). A browser launch
170
+ (`?iss=…&target_link_uri=…`) is read with `acceptLaunch` and only pre-fills the
171
+ connect page. Synap Cloud discovery is not available yet:
172
+ `cloudConnectAvailable()` is `false`.
173
+ - **Failures** redirect with `?error=<ConnectErrorCode>`: `denied`,
174
+ `expired_transaction`, `mismatch`, `misconfigured`, `rate_limited`,
175
+ `pod_unreachable`, … (`parseConnectErrorCode` reads it back). `connect` and
176
+ `disconnect` are POST and same-origin only.
177
+ - **Refresh** (rotating, single-use, 30 days from issue) is feature-detected: a
178
+ pod that issues no `refresh_token` gives a connection that ends with its
179
+ 1-hour access token. `refreshIfNeeded` rotates only inside the last 5
180
+ minutes, is single-flight per connection in the process, and persists the
181
+ rotated connection BEFORE returning it. The pod revokes the whole connection
182
+ when a spent refresh token is presented again, so for 2 minutes a request
183
+ still carrying the spent token (a parallel request, a second tab) gets the
184
+ rotation's result instead of reaching the pod. Across server instances only
185
+ a shared server-side `ConnectionStore` rules that replay out. A concurrent
186
+ loser (refused softly by the pod) keeps the still-valid access token.
187
+ - **Your own store**: implement `ConnectionStore` (`read`, `write`, `clear`,
188
+ returning Set-Cookie values) and pass it as `store`.
189
+ - Lower level: `startConnect`, `handleCallback`, `refreshConnection`,
190
+ `revokeConnection`, `buildClientMetadata`, `seal`/`unseal`.
191
+
192
+ Test without a pod: `createMockAuthServer()` from `@synap-core/sdk/testing`
193
+ (metadata, `authorize()` as the consent screen, `/token` with PKCE, rotating
194
+ refresh, `/revoke`, Hub REST doors; `writeMode`, `failures`, `refresh: false`):
195
+
196
+ ```typescript
197
+ const pod = createMockAuthServer();
198
+ const synap = createPodAppAuth({ ...config, fetch: pod.fetch });
199
+ // POST connect → Location → pod.authorize(location) → GET callback with that URL
200
+ ```
201
+
122
202
  ## Mock pod: `@synap-core/sdk/testing`
123
203
 
124
204
  A fake `fetch` that answers the public doors (forms, shares, sites) from fixtures,
@@ -0,0 +1,79 @@
1
+ // src/connect/encoding.ts
2
+ var encoder = new TextEncoder();
3
+ var decoder = new TextDecoder();
4
+ function utf8(text) {
5
+ return encoder.encode(text);
6
+ }
7
+ function fromUtf8(bytes) {
8
+ return decoder.decode(bytes);
9
+ }
10
+ function base64url(bytes) {
11
+ let binary = "";
12
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
13
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
14
+ }
15
+ function fromBase64url(text) {
16
+ if (!/^[A-Za-z0-9_-]*$/.test(text)) return null;
17
+ const padded = text.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((text.length + 3) % 4);
18
+ let binary;
19
+ try {
20
+ binary = atob(padded);
21
+ } catch {
22
+ return null;
23
+ }
24
+ const out = new Uint8Array(binary.length);
25
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
26
+ return out;
27
+ }
28
+ function randomBytes(length) {
29
+ const out = new Uint8Array(length);
30
+ crypto.getRandomValues(out);
31
+ return out;
32
+ }
33
+ function randomToken() {
34
+ return base64url(randomBytes(32));
35
+ }
36
+ async function pkceChallenge(verifier) {
37
+ const digest = await crypto.subtle.digest("SHA-256", utf8(verifier));
38
+ return base64url(new Uint8Array(digest));
39
+ }
40
+ function constantTimeEqual(a, b) {
41
+ const x = utf8(a);
42
+ const y = utf8(b);
43
+ let diff = x.length ^ y.length;
44
+ for (let i = 0; i < Math.max(x.length, y.length); i++) diff |= (x[i] ?? 0) ^ (y[i] ?? 0);
45
+ return diff === 0;
46
+ }
47
+ function parseCookies(header) {
48
+ const out = /* @__PURE__ */ new Map();
49
+ if (!header) return out;
50
+ for (const part of header.split(";")) {
51
+ const eq = part.indexOf("=");
52
+ if (eq < 0) continue;
53
+ const name = part.slice(0, eq).trim();
54
+ if (!name || out.has(name)) continue;
55
+ let value = part.slice(eq + 1).trim();
56
+ if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
57
+ out.set(name, value);
58
+ }
59
+ return out;
60
+ }
61
+ function serializeCookie(name, value, attrs) {
62
+ const parts = [`${name}=${value}`, "Path=/", "HttpOnly", "SameSite=Lax", `Max-Age=${Math.max(0, Math.floor(attrs.maxAgeSec))}`];
63
+ if (attrs.secure) parts.push("Secure");
64
+ return parts.join("; ");
65
+ }
66
+
67
+ export {
68
+ utf8,
69
+ fromUtf8,
70
+ base64url,
71
+ fromBase64url,
72
+ randomBytes,
73
+ randomToken,
74
+ pkceChallenge,
75
+ constantTimeEqual,
76
+ parseCookies,
77
+ serializeCookie
78
+ };
79
+ //# sourceMappingURL=chunk-J7XHAAFJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connect/encoding.ts"],"sourcesContent":["/**\n * Web-standard primitives (WebCrypto, TextEncoder, btoa/atob): the same code\n * runs on Node 18+, edge runtimes and workers. No Node import.\n */\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** `Uint8Array<ArrayBuffer>`: what WebCrypto's `BufferSource` takes under TS 5.7+ lib types. */\nexport type Bytes = Uint8Array<ArrayBuffer>;\n\nexport function utf8(text: string): Bytes {\n return encoder.encode(text);\n}\n\nexport function fromUtf8(bytes: Uint8Array): string {\n return decoder.decode(bytes);\n}\n\nexport function base64url(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Null when `text` is not base64url. */\nexport function fromBase64url(text: string): Bytes | null {\n if (!/^[A-Za-z0-9_-]*$/.test(text)) return null;\n const padded = text.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"===\".slice((text.length + 3) % 4);\n let binary: string;\n try {\n binary = atob(padded);\n } catch {\n return null;\n }\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nexport function randomBytes(length: number): Bytes {\n const out = new Uint8Array(length);\n crypto.getRandomValues(out);\n return out;\n}\n\n/** 32 random bytes, base64url (43 chars): a state value or a PKCE verifier. */\nexport function randomToken(): string {\n return base64url(randomBytes(32));\n}\n\n/** RFC 7636 §4.2: `BASE64URL(SHA256(ASCII(code_verifier)))`. */\nexport async function pkceChallenge(verifier: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", utf8(verifier));\n return base64url(new Uint8Array(digest));\n}\n\n/** Compare two strings without an early exit on the first differing byte. */\nexport function constantTimeEqual(a: string, b: string): boolean {\n const x = utf8(a);\n const y = utf8(b);\n let diff = x.length ^ y.length;\n for (let i = 0; i < Math.max(x.length, y.length); i++) diff |= (x[i] ?? 0) ^ (y[i] ?? 0);\n return diff === 0;\n}\n\n/** The request's cookies by name (the first value wins, as browsers order the most specific first). */\nexport function parseCookies(header: string | null): Map<string, string> {\n const out = new Map<string, string>();\n if (!header) return out;\n for (const part of header.split(\";\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const name = part.slice(0, eq).trim();\n if (!name || out.has(name)) continue;\n let value = part.slice(eq + 1).trim();\n if (value.startsWith('\"') && value.endsWith('\"')) value = value.slice(1, -1);\n out.set(name, value);\n }\n return out;\n}\n\nexport interface CookieAttributes {\n maxAgeSec: number;\n secure: boolean;\n}\n\n/** `HttpOnly; SameSite=Lax; Path=/` always; `Secure` on https. Never a `Domain`. */\nexport function serializeCookie(name: string, value: string, attrs: CookieAttributes): string {\n const parts = [`${name}=${value}`, \"Path=/\", \"HttpOnly\", \"SameSite=Lax\", `Max-Age=${Math.max(0, Math.floor(attrs.maxAgeSec))}`];\n if (attrs.secure) parts.push(\"Secure\");\n return parts.join(\"; \");\n}\n"],"mappings":";AAKA,IAAM,UAAU,IAAI,YAAY;AAChC,IAAM,UAAU,IAAI,YAAY;AAKzB,SAAS,KAAK,MAAqB;AACxC,SAAO,QAAQ,OAAO,IAAI;AAC5B;AAEO,SAAS,SAAS,OAA2B;AAClD,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAEO,SAAS,UAAU,OAA2B;AACnD,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,WAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAC7E,SAAO,KAAK,MAAM,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC/E;AAGO,SAAS,cAAc,MAA4B;AACxD,MAAI,CAAC,mBAAmB,KAAK,IAAI,EAAG,QAAO;AAC3C,QAAM,SAAS,KAAK,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG,IAAI,MAAM,OAAO,KAAK,SAAS,KAAK,CAAC;AAC7F,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,MAAM,IAAI,WAAW,OAAO,MAAM;AACxC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,KAAI,CAAC,IAAI,OAAO,WAAW,CAAC;AACpE,SAAO;AACT;AAEO,SAAS,YAAY,QAAuB;AACjD,QAAM,MAAM,IAAI,WAAW,MAAM;AACjC,SAAO,gBAAgB,GAAG;AAC1B,SAAO;AACT;AAGO,SAAS,cAAsB;AACpC,SAAO,UAAU,YAAY,EAAE,CAAC;AAClC;AAGA,eAAsB,cAAc,UAAmC;AACrE,QAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK,QAAQ,CAAC;AACnE,SAAO,UAAU,IAAI,WAAW,MAAM,CAAC;AACzC;AAGO,SAAS,kBAAkB,GAAW,GAAoB;AAC/D,QAAM,IAAI,KAAK,CAAC;AAChB,QAAM,IAAI,KAAK,CAAC;AAChB,MAAI,OAAO,EAAE,SAAS,EAAE;AACxB,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAK,UAAS,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,KAAK;AACtF,SAAO,SAAS;AAClB;AAGO,SAAS,aAAa,QAA4C;AACvE,QAAM,MAAM,oBAAI,IAAoB;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,KAAK,EAAG;AACZ,UAAM,OAAO,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACpC,QAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,EAAG;AAC5B,QAAI,QAAQ,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AACpC,QAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,EAAG,SAAQ,MAAM,MAAM,GAAG,EAAE;AAC3E,QAAI,IAAI,MAAM,KAAK;AAAA,EACrB;AACA,SAAO;AACT;AAQO,SAAS,gBAAgB,MAAc,OAAe,OAAiC;AAC5F,QAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,UAAU,YAAY,gBAAgB,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE;AAC9H,MAAI,MAAM,OAAQ,OAAM,KAAK,QAAQ;AACrC,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
@@ -250,4 +250,4 @@ interface PublicClient {
250
250
  }
251
251
  declare function createPublicClient(opts: PublicClientOptions): PublicClient;
252
252
 
253
- export { type CollectionQuery as C, type PublicForm as P, type Revalidate as R, type SubmitResult as S, type PublicReadError as a, type PublicRecordSummary as b, type PublicValue as c, type PublicCollectionPage as d, type PublicRecord as e, type PublicResult as f, type PublicShare as g, type PublicClient as h, type PublicClientOptions as i, type PublicFailure as j, type PublicFormField as k, type PublicFormFieldType as l, type PublicNotModified as m, type PublicSite as n, type PublicSiteCollection as o, type PublicSiteError as p, type PublicSiteResult as q, type PublicSubmitError as r, SITE_MAX_LIMIT as s, SITE_MAX_OFFSET as t, SITE_MAX_SORT_KEYS as u, SITE_PUBLISHED_AT_SORT as v, type SubmitFormInput as w, createPublicClient as x };
253
+ export { type CollectionQuery as C, type PublicForm as P, type Revalidate as R, type SubmitResult as S, type PublicShare as a, type PublicRecord as b, type PublicReadError as c, type PublicRecordSummary as d, type PublicValue as e, type PublicCollectionPage as f, type PublicResult as g, type PublicClient as h, type PublicClientOptions as i, type PublicFailure as j, type PublicFormField as k, type PublicFormFieldType as l, type PublicNotModified as m, type PublicSite as n, type PublicSiteCollection as o, type PublicSiteError as p, type PublicSiteResult as q, type PublicSubmitError as r, SITE_MAX_LIMIT as s, SITE_MAX_OFFSET as t, SITE_MAX_SORT_KEYS as u, SITE_PUBLISHED_AT_SORT as v, type SubmitFormInput as w, createPublicClient as x };
@@ -250,4 +250,4 @@ interface PublicClient {
250
250
  }
251
251
  declare function createPublicClient(opts: PublicClientOptions): PublicClient;
252
252
 
253
- export { type CollectionQuery as C, type PublicForm as P, type Revalidate as R, type SubmitResult as S, type PublicReadError as a, type PublicRecordSummary as b, type PublicValue as c, type PublicCollectionPage as d, type PublicRecord as e, type PublicResult as f, type PublicShare as g, type PublicClient as h, type PublicClientOptions as i, type PublicFailure as j, type PublicFormField as k, type PublicFormFieldType as l, type PublicNotModified as m, type PublicSite as n, type PublicSiteCollection as o, type PublicSiteError as p, type PublicSiteResult as q, type PublicSubmitError as r, SITE_MAX_LIMIT as s, SITE_MAX_OFFSET as t, SITE_MAX_SORT_KEYS as u, SITE_PUBLISHED_AT_SORT as v, type SubmitFormInput as w, createPublicClient as x };
253
+ export { type CollectionQuery as C, type PublicForm as P, type Revalidate as R, type SubmitResult as S, type PublicShare as a, type PublicRecord as b, type PublicReadError as c, type PublicRecordSummary as d, type PublicValue as e, type PublicCollectionPage as f, type PublicResult as g, type PublicClient as h, type PublicClientOptions as i, type PublicFailure as j, type PublicFormField as k, type PublicFormFieldType as l, type PublicNotModified as m, type PublicSite as n, type PublicSiteCollection as o, type PublicSiteError as p, type PublicSiteResult as q, type PublicSubmitError as r, SITE_MAX_LIMIT as s, SITE_MAX_OFFSET as t, SITE_MAX_SORT_KEYS as u, SITE_PUBLISHED_AT_SORT as v, type SubmitFormInput as w, createPublicClient as x };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/connect/browser.ts
17
+ var browser_exports = {};
18
+ module.exports = __toCommonJS(browser_exports);
19
+ throw new Error(
20
+ "@synap-core/sdk/connect is server-only: it holds the pod's tokens and the app secret. Import it from route handlers, proxy.ts or server code, never from a client component."
21
+ );
22
+ //# sourceMappingURL=connect.browser.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connect/browser.ts"],"sourcesContent":["/**\n * What a BROWSER bundle gets for `@synap-core/sdk/connect` (the `browser`\n * export condition): no exports, and a throw on load. See ./guard.ts.\n */\nthrow new Error(\n \"@synap-core/sdk/connect is server-only: it holds the pod's tokens and the app secret. \" +\n \"Import it from route handlers, proxy.ts or server code, never from a client component.\",\n);\n\nexport {};\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;AAIA,MAAM,IAAI;AAAA,EACR;AAEF;","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,5 @@
1
+ // src/connect/browser.ts
2
+ throw new Error(
3
+ "@synap-core/sdk/connect is server-only: it holds the pod's tokens and the app secret. Import it from route handlers, proxy.ts or server code, never from a client component."
4
+ );
5
+ //# sourceMappingURL=connect.browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connect/browser.ts"],"sourcesContent":["/**\n * What a BROWSER bundle gets for `@synap-core/sdk/connect` (the `browser`\n * export condition): no exports, and a throw on load. See ./guard.ts.\n */\nthrow new Error(\n \"@synap-core/sdk/connect is server-only: it holds the pod's tokens and the app secret. \" +\n \"Import it from route handlers, proxy.ts or server code, never from a client component.\",\n);\n\nexport {};\n"],"mappings":";AAIA,MAAM,IAAI;AAAA,EACR;AAEF;","names":[]}