@synap-core/sdk 0.2.0 → 1.0.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/README.md +40 -140
- package/dist/index.cjs +115 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +80 -0
- package/dist/index.d.ts +72 -47
- package/dist/index.js +80 -47
- package/dist/index.js.map +1 -1
- package/package.json +28 -24
- package/src/client.ts +193 -0
- package/src/index.ts +2 -0
- package/dist/client.d.ts +0 -105
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -95
- package/dist/client.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/resources/entities.d.ts +0 -90
- package/dist/resources/entities.d.ts.map +0 -1
- package/dist/resources/entities.js +0 -126
- package/dist/resources/entities.js.map +0 -1
- package/dist/resources/events.d.ts +0 -51
- package/dist/resources/events.d.ts.map +0 -1
- package/dist/resources/events.js +0 -92
- package/dist/resources/events.js.map +0 -1
- package/dist/resources/preferences.d.ts +0 -24
- package/dist/resources/preferences.d.ts.map +0 -1
- package/dist/resources/preferences.js +0 -30
- package/dist/resources/preferences.js.map +0 -1
- package/dist/resources/relations.d.ts +0 -71
- package/dist/resources/relations.d.ts.map +0 -1
- package/dist/resources/relations.js +0 -121
- package/dist/resources/relations.js.map +0 -1
- package/dist/resources/views.d.ts +0 -43
- package/dist/resources/views.d.ts.map +0 -1
- package/dist/resources/views.js +0 -54
- package/dist/resources/views.js.map +0 -1
- package/dist/resources/workspaces.d.ts +0 -50
- package/dist/resources/workspaces.d.ts.map +0 -1
- package/dist/resources/workspaces.js +0 -66
- package/dist/resources/workspaces.js.map +0 -1
- package/dist/types/events.d.ts +0 -24
- package/dist/types/events.d.ts.map +0 -1
- package/dist/types/events.js +0 -5
- package/dist/types/events.js.map +0 -1
- package/dist/types/relations.d.ts +0 -26
- package/dist/types/relations.d.ts.map +0 -1
- package/dist/types/relations.js +0 -5
- package/dist/types/relations.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,173 +1,73 @@
|
|
|
1
|
-
# @synap/sdk
|
|
1
|
+
# @synap-core/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Type-safe tRPC client for [Synap](https://synap.live) data pods. Connect any JavaScript/TypeScript app to your personal data pod with full autocomplete on all 47+ routers.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
npm install @synap-core/sdk
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @synap-core/sdk
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
## Quick
|
|
13
|
+
## Quick start
|
|
12
14
|
|
|
13
15
|
```typescript
|
|
14
|
-
import {
|
|
16
|
+
import { createSynapClient } from "@synap-core/sdk";
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const synap = createSynapClient({
|
|
19
|
+
podUrl: "https://your-pod.synap.live", // your data pod URL
|
|
20
|
+
apiKey: "sk_live_...", // from Workspace → Settings → API Keys
|
|
21
|
+
workspaceId: "ws_...", // your workspace ID
|
|
20
22
|
});
|
|
21
23
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
title: 'Call Marie tomorrow',
|
|
26
|
-
metadata: {
|
|
27
|
-
dueDate: '2024-12-20',
|
|
28
|
-
priority: 'high'
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Create relationship (event-sourced)
|
|
33
|
-
await synap.relations.create(entityId, personId, 'assigned_to');
|
|
24
|
+
// Fully typed — autocomplete works on all procedures
|
|
25
|
+
const entities = await synap.entities.list.query({ limit: 20 });
|
|
26
|
+
const workspace = await synap.workspaces.get.query();
|
|
34
27
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
// Create an entity
|
|
29
|
+
await synap.entities.create.mutate({
|
|
30
|
+
profileSlug: "note",
|
|
31
|
+
title: "My first note",
|
|
32
|
+
properties: { content: "Hello from the SDK!" },
|
|
33
|
+
});
|
|
38
34
|
```
|
|
39
35
|
|
|
40
|
-
##
|
|
41
|
-
|
|
42
|
-
### Event Sourcing
|
|
43
|
-
|
|
44
|
-
All **mutations** go through event sourcing:
|
|
45
|
-
- `entities.create/update/delete` → `events.log` → Inngest worker → Database
|
|
46
|
-
- `relations.create/delete` → `events.log` → Inngest worker → Database
|
|
47
|
-
|
|
48
|
-
All **queries** are direct database reads:
|
|
49
|
-
- `entities.get/list/search` → Direct query
|
|
50
|
-
- `relations.get/getRelated` → Direct query
|
|
51
|
-
- `events.getHistory` → Direct query
|
|
52
|
-
|
|
53
|
-
### Benefits
|
|
54
|
-
|
|
55
|
-
- **Audit trail**: Every change logged as immutable event
|
|
56
|
-
- **Time travel**: Replay events to see past states
|
|
57
|
-
- **Reliability**: Workers handle async processing with retries
|
|
58
|
-
- **Real-time**: Events trigger webhooks and SSE broadcasts
|
|
36
|
+
## Authentication
|
|
59
37
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
### Entities API
|
|
38
|
+
**API key (server-side / external apps)**
|
|
63
39
|
|
|
64
40
|
```typescript
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
content: '# Discussion points...'
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Update
|
|
73
|
-
await synap.entities.update(entityId, {
|
|
74
|
-
title: 'Updated Title'
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Delete (soft delete)
|
|
78
|
-
await synap.entities.delete(entityId);
|
|
79
|
-
|
|
80
|
-
// Get
|
|
81
|
-
const entity = await synap.entities.get(entityId);
|
|
82
|
-
|
|
83
|
-
// List
|
|
84
|
-
const notes = await synap.entities.list({
|
|
85
|
-
type: 'note',
|
|
86
|
-
limit: 20
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Search
|
|
90
|
-
const results = await synap.entities.search({
|
|
91
|
-
query: 'project planning',
|
|
92
|
-
types: ['note', 'task']
|
|
41
|
+
const synap = createSynapClient({
|
|
42
|
+
podUrl: "https://your-pod.synap.live",
|
|
43
|
+
apiKey: "sk_live_...", // Bearer token — generate in workspace settings
|
|
44
|
+
workspaceId: "ws_...",
|
|
93
45
|
});
|
|
94
46
|
```
|
|
95
47
|
|
|
96
|
-
|
|
48
|
+
**Session cookie (browser — after Kratos login)**
|
|
97
49
|
|
|
98
50
|
```typescript
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
'assigned_to'
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
// Get relations
|
|
107
|
-
const relations = await synap.relations.get(entityId, {
|
|
108
|
-
type: 'assigned_to',
|
|
109
|
-
direction: 'both' // 'source' | 'target' | 'both'
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Get related entities
|
|
113
|
-
const people = await synap.relations.getRelated(taskId, {
|
|
114
|
-
type: 'assigned_to',
|
|
115
|
-
direction: 'source'
|
|
51
|
+
const synap = createSynapClient({
|
|
52
|
+
podUrl: "https://your-pod.synap.live",
|
|
53
|
+
workspaceId: "ws_...",
|
|
54
|
+
// omit apiKey — credentials: "include" is set automatically
|
|
116
55
|
});
|
|
117
|
-
|
|
118
|
-
// Get statistics
|
|
119
|
-
const stats = await synap.relations.getStats(entityId);
|
|
120
|
-
// Returns: { total, outgoing, incoming, byType }
|
|
121
|
-
|
|
122
|
-
// Delete relationship
|
|
123
|
-
await synap.relations.delete(relationId);
|
|
124
56
|
```
|
|
125
57
|
|
|
126
|
-
|
|
58
|
+
## Type helpers
|
|
127
59
|
|
|
128
60
|
```typescript
|
|
129
|
-
|
|
130
|
-
const history = await synap.events.getHistory(entityId, {
|
|
131
|
-
eventTypes: ['entities.create.validated'],
|
|
132
|
-
limit: 100
|
|
133
|
-
});
|
|
61
|
+
import type { RouterInputs, RouterOutputs } from "@synap-core/sdk";
|
|
134
62
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
limit: 50
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Replay events to rebuild state
|
|
141
|
-
const state = await synap.events.replay(entityId);
|
|
63
|
+
type EntityListInput = RouterInputs["entities"]["list"];
|
|
64
|
+
type EntityListOutput = RouterOutputs["entities"]["list"];
|
|
142
65
|
```
|
|
143
66
|
|
|
144
|
-
##
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
type EntityType =
|
|
148
|
-
| 'task'
|
|
149
|
-
| 'note'
|
|
150
|
-
| 'person'
|
|
151
|
-
| 'event'
|
|
152
|
-
| 'file';
|
|
153
|
-
```
|
|
67
|
+
## React
|
|
154
68
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
type RelationType =
|
|
159
|
-
| 'assigned_to' // Task → Person
|
|
160
|
-
| 'mentions' // Note → Entity
|
|
161
|
-
| 'links_to' // Note → Note
|
|
162
|
-
| 'parent_of' // Project → Task
|
|
163
|
-
| 'relates_to' // Generic
|
|
164
|
-
| 'tagged_with' // Entity → Tag
|
|
165
|
-
| 'created_by' // Entity → Person
|
|
166
|
-
| 'attended_by' // Event → Person
|
|
167
|
-
| 'depends_on' // Task → Task
|
|
168
|
-
| 'blocks'; // Task → Task
|
|
169
|
-
```
|
|
69
|
+
For React apps use [`@synap-core/react`](https://www.npmjs.com/package/@synap-core/react) which wraps this SDK with TanStack Query hooks.
|
|
170
70
|
|
|
171
|
-
##
|
|
71
|
+
## API reference
|
|
172
72
|
|
|
173
|
-
|
|
73
|
+
Full procedure reference: [docs.synap.live/reference/api-reference](https://docs.synap.live/reference/api-reference)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createSynapClient: () => createSynapClient
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/client.ts
|
|
38
|
+
var import_client = require("@trpc/client");
|
|
39
|
+
var import_superjson = __toESM(require("superjson"), 1);
|
|
40
|
+
function createSynapClient(options) {
|
|
41
|
+
const { podUrl, apiKey, workspaceId, extraHeaders, onAuthError } = options;
|
|
42
|
+
const url = `${podUrl.replace(/\/$/, "")}/trpc`;
|
|
43
|
+
const baseFetch = (input, init) => fetch(input, { ...init, credentials: "include" });
|
|
44
|
+
const wrappedFetch = onAuthError ? async (input, init) => {
|
|
45
|
+
const response = await baseFetch(input, init);
|
|
46
|
+
if (response.ok) return response;
|
|
47
|
+
const cloned = response.clone();
|
|
48
|
+
let body = "";
|
|
49
|
+
try {
|
|
50
|
+
body = await cloned.text();
|
|
51
|
+
} catch {
|
|
52
|
+
body = "";
|
|
53
|
+
}
|
|
54
|
+
let isTrpcFormat = false;
|
|
55
|
+
try {
|
|
56
|
+
isTrpcFormat = Array.isArray(JSON.parse(body));
|
|
57
|
+
} catch {
|
|
58
|
+
isTrpcFormat = false;
|
|
59
|
+
}
|
|
60
|
+
if (isTrpcFormat) return response;
|
|
61
|
+
const parsedMessage = (() => {
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(body);
|
|
64
|
+
return parsed.error ?? parsed.message ?? null;
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
const message = response.status === 401 ? "Session expired. Please sign in again." : `Request failed (${response.status}).`;
|
|
70
|
+
if (response.status === 401) {
|
|
71
|
+
onAuthError({ status: 401, message });
|
|
72
|
+
}
|
|
73
|
+
const trpcError = [
|
|
74
|
+
{
|
|
75
|
+
error: {
|
|
76
|
+
message,
|
|
77
|
+
code: response.status === 401 ? -32001 : -32e3,
|
|
78
|
+
data: {
|
|
79
|
+
code: response.status === 401 ? "UNAUTHORIZED" : "INTERNAL_SERVER_ERROR",
|
|
80
|
+
httpStatus: response.status,
|
|
81
|
+
// Server-supplied detail — untrusted; consumers must escape it
|
|
82
|
+
// before rendering (never inject as HTML).
|
|
83
|
+
detail: parsedMessage ?? void 0
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
return new Response(JSON.stringify(trpcError), {
|
|
89
|
+
status: response.status,
|
|
90
|
+
headers: { "content-type": "application/json" }
|
|
91
|
+
});
|
|
92
|
+
} : baseFetch;
|
|
93
|
+
return (0, import_client.createTRPCClient)({
|
|
94
|
+
links: [
|
|
95
|
+
(0, import_client.httpBatchLink)({
|
|
96
|
+
url,
|
|
97
|
+
transformer: import_superjson.default,
|
|
98
|
+
fetch: wrappedFetch,
|
|
99
|
+
headers: () => ({
|
|
100
|
+
// Session token mode (Kratos, preferred for non-browser clients)
|
|
101
|
+
...options.sessionToken ? { "x-session-token": options.sessionToken } : {},
|
|
102
|
+
// API key mode (Bearer, for legacy SDK usage)
|
|
103
|
+
...apiKey && !options.sessionToken ? { Authorization: `Bearer ${apiKey}` } : {},
|
|
104
|
+
...workspaceId ? { "x-workspace-id": workspaceId } : {},
|
|
105
|
+
...extraHeaders
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
]
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
112
|
+
0 && (module.exports = {
|
|
113
|
+
createSynapClient
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export { createSynapClient } from \"./client\";\nexport type { SynapClient, SynapClientOptions, AppRouter } from \"./client\";\n","/**\n * @synap-core/sdk — Type-safe tRPC client for Synap data pods.\n *\n * Usage:\n * ```typescript\n * import { createSynapClient } from \"@synap-core/sdk\";\n *\n * const synap = createSynapClient({\n * podUrl: \"https://pod.example.com\",\n * apiKey: \"sk_live_...\",\n * workspaceId: \"ws_...\",\n * });\n *\n * // Fully typed — autocomplete works on all 47+ routers\n * const entities = await synap.entities.list.query({ limit: 20 });\n * const workspace = await synap.workspaces.get.query();\n * ```\n */\n\nimport {\n createTRPCClient,\n httpBatchLink,\n type CreateTRPCClient,\n} from \"@trpc/client\";\nimport superjson from \"superjson\";\nimport type { AppRouter } from \"@synap-core/api-types\";\n\nexport interface SynapClientOptions {\n /**\n * Base URL of the data pod (no trailing slash, no `/trpc` suffix).\n * @example \"https://pod.example.com\"\n */\n podUrl: string;\n\n /**\n * Ory Kratos session token (for example, from the Pod's\n * `/api/federation/exchange` endpoint).\n * Sent as `X-Session-Token` header — preferred for non-browser clients\n * like extensions, CLI tools, and mobile apps.\n * @example \"oathkeeper_token_...\"\n */\n sessionToken?: string;\n\n /**\n * API key for authentication (Bearer token).\n * Generate one in your workspace developer settings.\n * If omitted, the client falls back to cookie-based Kratos session auth.\n * @example \"sk_live_abc123...\"\n */\n apiKey?: string;\n\n /**\n * Workspace ID to scope all requests to.\n * Pass empty string for procedures that don't require workspace context\n * (e.g. workspaces.list, which is protectedProcedure only).\n * @example \"ws_01jk...\"\n */\n workspaceId: string;\n\n /**\n * Additional headers to include in every request.\n */\n extraHeaders?: Record<string, string>;\n\n /**\n * Called when the pod returns a `401 Unauthorized` (expired/invalid session\n * or API key). The app should sign out and re-authenticate (re-handshake).\n *\n * The pod's auth middleware can reply with a plain-JSON body\n * (`{\"error\":\"Invalid session\"}`) that is NOT in tRPC batch format and would\n * otherwise crash the superjson transformer; when an `onAuthError` handler is\n * provided the client rewrites such responses into a well-formed tRPC error so\n * the query rejects gracefully instead of throwing a parse error.\n */\n onAuthError?: (error: { status: number; message: string }) => void;\n}\n\n/**\n * Creates a fully-typed tRPC client connected to a Synap data pod.\n *\n * All 47+ tRPC routers are available with full TypeScript autocomplete\n * and compile-time safety. Auth is handled via Bearer token + workspace header.\n */\nexport function createSynapClient(\n options: SynapClientOptions,\n): CreateTRPCClient<AppRouter> {\n const { podUrl, apiKey, workspaceId, extraHeaders, onAuthError } = options;\n\n const url = `${podUrl.replace(/\\/$/, \"\")}/trpc`;\n\n // Base fetch: always include credentials so session cookies are sent in\n // cookie-auth mode. When apiKey is present, the Bearer token takes precedence\n // on the pod.\n const baseFetch: typeof fetch = (input, init) =>\n fetch(input, { ...init, credentials: \"include\" });\n\n // When an onAuthError handler is provided, wrap fetch to catch 401 (and other\n // non-tRPC error) responses. The pod's auth middleware replies with a plain\n // JSON body that is not a tRPC batch array; left untouched it crashes the\n // superjson transformer. We notify the handler and rewrite the body into a\n // valid tRPC error envelope so the query rejects cleanly.\n const wrappedFetch: typeof fetch = onAuthError\n ? async (input, init) => {\n const response = await baseFetch(input, init);\n if (response.ok) return response;\n\n const cloned = response.clone();\n let body = \"\";\n try {\n body = await cloned.text();\n } catch {\n body = \"\";\n }\n\n let isTrpcFormat = false;\n try {\n isTrpcFormat = Array.isArray(JSON.parse(body));\n } catch {\n isTrpcFormat = false;\n }\n if (isTrpcFormat) return response;\n\n const parsedMessage = (() => {\n try {\n const parsed = JSON.parse(body) as {\n error?: string;\n message?: string;\n };\n return parsed.error ?? parsed.message ?? null;\n } catch {\n return null;\n }\n })();\n // Never forward a server-controlled string as user-facing copy — map by\n // status. The raw server detail is preserved (untrusted) on `data.detail`.\n const message =\n response.status === 401\n ? \"Session expired. Please sign in again.\"\n : `Request failed (${response.status}).`;\n\n if (response.status === 401) {\n onAuthError({ status: 401, message });\n }\n\n const trpcError = [\n {\n error: {\n message,\n code: response.status === 401 ? -32001 : -32000,\n data: {\n code:\n response.status === 401\n ? \"UNAUTHORIZED\"\n : \"INTERNAL_SERVER_ERROR\",\n httpStatus: response.status,\n // Server-supplied detail — untrusted; consumers must escape it\n // before rendering (never inject as HTML).\n detail: parsedMessage ?? undefined,\n },\n\n },\n },\n ];\n return new Response(JSON.stringify(trpcError), {\n status: response.status,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n : baseFetch;\n\n return createTRPCClient<AppRouter>({\n links: [\n httpBatchLink({\n url,\n transformer: superjson,\n fetch: wrappedFetch,\n headers: () => ({\n // Session token mode (Kratos, preferred for non-browser clients)\n ...(options.sessionToken ? { \"x-session-token\": options.sessionToken } : {}),\n // API key mode (Bearer, for legacy SDK usage)\n ...(apiKey && !options.sessionToken ? { Authorization: `Bearer ${apiKey}` } : {}),\n ...(workspaceId ? { \"x-workspace-id\": workspaceId } : {}),\n ...extraHeaders,\n }),\n }),\n ],\n });\n}\n\nexport type SynapClient = ReturnType<typeof createSynapClient>;\n\n// Re-export type helpers so consumers don't need @trpc/server\nexport type { AppRouter };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBA,oBAIO;AACP,uBAAsB;AA2Df,SAAS,kBACd,SAC6B;AAC7B,QAAM,EAAE,QAAQ,QAAQ,aAAa,cAAc,YAAY,IAAI;AAEnE,QAAM,MAAM,GAAG,OAAO,QAAQ,OAAO,EAAE,CAAC;AAKxC,QAAM,YAA0B,CAAC,OAAO,SACtC,MAAM,OAAO,EAAE,GAAG,MAAM,aAAa,UAAU,CAAC;AAOlD,QAAM,eAA6B,cAC/B,OAAO,OAAO,SAAS;AACrB,UAAM,WAAW,MAAM,UAAU,OAAO,IAAI;AAC5C,QAAI,SAAS,GAAI,QAAO;AAExB,UAAM,SAAS,SAAS,MAAM;AAC9B,QAAI,OAAO;AACX,QAAI;AACF,aAAO,MAAM,OAAO,KAAK;AAAA,IAC3B,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,QAAI,eAAe;AACnB,QAAI;AACF,qBAAe,MAAM,QAAQ,KAAK,MAAM,IAAI,CAAC;AAAA,IAC/C,QAAQ;AACN,qBAAe;AAAA,IACjB;AACA,QAAI,aAAc,QAAO;AAEzB,UAAM,iBAAiB,MAAM;AAC3B,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,IAAI;AAI9B,eAAO,OAAO,SAAS,OAAO,WAAW;AAAA,MAC3C,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,GAAG;AAGH,UAAM,UACJ,SAAS,WAAW,MAChB,2CACA,mBAAmB,SAAS,MAAM;AAExC,QAAI,SAAS,WAAW,KAAK;AAC3B,kBAAY,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAAA,IACtC;AAEA,UAAM,YAAY;AAAA,MAChB;AAAA,QACE,OAAO;AAAA,UACL;AAAA,UACA,MAAM,SAAS,WAAW,MAAM,SAAS;AAAA,UACzC,MAAM;AAAA,YACJ,MACE,SAAS,WAAW,MAChB,iBACA;AAAA,YACN,YAAY,SAAS;AAAA;AAAA;AAAA,YAGrB,QAAQ,iBAAiB;AAAA,UAC3B;AAAA,QAEF;AAAA,MACF;AAAA,IACF;AACA,WAAO,IAAI,SAAS,KAAK,UAAU,SAAS,GAAG;AAAA,MAC7C,QAAQ,SAAS;AAAA,MACjB,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAChD,CAAC;AAAA,EACH,IACA;AAEJ,aAAO,gCAA4B;AAAA,IACjC,OAAO;AAAA,UACL,6BAAc;AAAA,QACZ;AAAA,QACA,aAAa,iBAAAA;AAAA,QACb,OAAO;AAAA,QACP,SAAS,OAAO;AAAA;AAAA,UAEd,GAAI,QAAQ,eAAe,EAAE,mBAAmB,QAAQ,aAAa,IAAI,CAAC;AAAA;AAAA,UAE1E,GAAI,UAAU,CAAC,QAAQ,eAAe,EAAE,eAAe,UAAU,MAAM,GAAG,IAAI,CAAC;AAAA,UAC/E,GAAI,cAAc,EAAE,kBAAkB,YAAY,IAAI,CAAC;AAAA,UACvD,GAAG;AAAA,QACL;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;","names":["superjson"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { CreateTRPCClient } from '@trpc/client';
|
|
2
|
+
import { AppRouter } from '@synap-core/api-types';
|
|
3
|
+
export { AppRouter } from '@synap-core/api-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @synap-core/sdk — Type-safe tRPC client for Synap data pods.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { createSynapClient } from "@synap-core/sdk";
|
|
11
|
+
*
|
|
12
|
+
* const synap = createSynapClient({
|
|
13
|
+
* podUrl: "https://pod.example.com",
|
|
14
|
+
* apiKey: "sk_live_...",
|
|
15
|
+
* workspaceId: "ws_...",
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Fully typed — autocomplete works on all 47+ routers
|
|
19
|
+
* const entities = await synap.entities.list.query({ limit: 20 });
|
|
20
|
+
* const workspace = await synap.workspaces.get.query();
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
interface SynapClientOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Base URL of the data pod (no trailing slash, no `/trpc` suffix).
|
|
27
|
+
* @example "https://pod.example.com"
|
|
28
|
+
*/
|
|
29
|
+
podUrl: string;
|
|
30
|
+
/**
|
|
31
|
+
* Ory Kratos session token (for example, from the Pod's
|
|
32
|
+
* `/api/federation/exchange` endpoint).
|
|
33
|
+
* Sent as `X-Session-Token` header — preferred for non-browser clients
|
|
34
|
+
* like extensions, CLI tools, and mobile apps.
|
|
35
|
+
* @example "oathkeeper_token_..."
|
|
36
|
+
*/
|
|
37
|
+
sessionToken?: string;
|
|
38
|
+
/**
|
|
39
|
+
* API key for authentication (Bearer token).
|
|
40
|
+
* Generate one in your workspace developer settings.
|
|
41
|
+
* If omitted, the client falls back to cookie-based Kratos session auth.
|
|
42
|
+
* @example "sk_live_abc123..."
|
|
43
|
+
*/
|
|
44
|
+
apiKey?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Workspace ID to scope all requests to.
|
|
47
|
+
* Pass empty string for procedures that don't require workspace context
|
|
48
|
+
* (e.g. workspaces.list, which is protectedProcedure only).
|
|
49
|
+
* @example "ws_01jk..."
|
|
50
|
+
*/
|
|
51
|
+
workspaceId: string;
|
|
52
|
+
/**
|
|
53
|
+
* Additional headers to include in every request.
|
|
54
|
+
*/
|
|
55
|
+
extraHeaders?: Record<string, string>;
|
|
56
|
+
/**
|
|
57
|
+
* Called when the pod returns a `401 Unauthorized` (expired/invalid session
|
|
58
|
+
* or API key). The app should sign out and re-authenticate (re-handshake).
|
|
59
|
+
*
|
|
60
|
+
* The pod's auth middleware can reply with a plain-JSON body
|
|
61
|
+
* (`{"error":"Invalid session"}`) that is NOT in tRPC batch format and would
|
|
62
|
+
* otherwise crash the superjson transformer; when an `onAuthError` handler is
|
|
63
|
+
* provided the client rewrites such responses into a well-formed tRPC error so
|
|
64
|
+
* the query rejects gracefully instead of throwing a parse error.
|
|
65
|
+
*/
|
|
66
|
+
onAuthError?: (error: {
|
|
67
|
+
status: number;
|
|
68
|
+
message: string;
|
|
69
|
+
}) => void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Creates a fully-typed tRPC client connected to a Synap data pod.
|
|
73
|
+
*
|
|
74
|
+
* All 47+ tRPC routers are available with full TypeScript autocomplete
|
|
75
|
+
* and compile-time safety. Auth is handled via Bearer token + workspace header.
|
|
76
|
+
*/
|
|
77
|
+
declare function createSynapClient(options: SynapClientOptions): CreateTRPCClient<AppRouter>;
|
|
78
|
+
type SynapClient = ReturnType<typeof createSynapClient>;
|
|
79
|
+
|
|
80
|
+
export { type SynapClient, type SynapClientOptions, createSynapClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,55 +1,80 @@
|
|
|
1
|
+
import { CreateTRPCClient } from '@trpc/client';
|
|
2
|
+
import { AppRouter } from '@synap-core/api-types';
|
|
3
|
+
export { AppRouter } from '@synap-core/api-types';
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
|
-
* @synap/sdk
|
|
6
|
+
* @synap-core/sdk — Type-safe tRPC client for Synap data pods.
|
|
3
7
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
8
|
+
* Usage:
|
|
7
9
|
* ```typescript
|
|
8
|
-
* import {
|
|
9
|
-
*
|
|
10
|
-
* const synap = new SynapSDK({
|
|
11
|
-
* url: 'https://api.synap.app',
|
|
12
|
-
* apiKey: 'your-api-key',
|
|
13
|
-
* realtime: {
|
|
14
|
-
* userId: 'user-123',
|
|
15
|
-
* userName: 'Alice',
|
|
16
|
-
* },
|
|
17
|
-
* });
|
|
10
|
+
* import { createSynapClient } from "@synap-core/sdk";
|
|
18
11
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* metadata: { dueDate: '2024-12-20', priority: 'high' }
|
|
12
|
+
* const synap = createSynapClient({
|
|
13
|
+
* podUrl: "https://pod.example.com",
|
|
14
|
+
* apiKey: "sk_live_...",
|
|
15
|
+
* workspaceId: "ws_...",
|
|
24
16
|
* });
|
|
25
17
|
*
|
|
26
|
-
* //
|
|
27
|
-
* const
|
|
28
|
-
*
|
|
29
|
-
* // Create whiteboard
|
|
30
|
-
* const board = await synap.views.create({
|
|
31
|
-
* type: 'whiteboard',
|
|
32
|
-
* name: 'Brainstorm',
|
|
33
|
-
* workspaceId: workspace.id,
|
|
34
|
-
* });
|
|
35
|
-
*
|
|
36
|
-
* // Real-time collaboration
|
|
37
|
-
* synap.realtime?.connectPresence(board.id);
|
|
38
|
-
* const ydoc = synap.realtime?.connectYjs(board.id);
|
|
18
|
+
* // Fully typed — autocomplete works on all 47+ routers
|
|
19
|
+
* const entities = await synap.entities.list.query({ limit: 20 });
|
|
20
|
+
* const workspace = await synap.workspaces.get.query();
|
|
39
21
|
* ```
|
|
40
22
|
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
23
|
+
|
|
24
|
+
interface SynapClientOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Base URL of the data pod (no trailing slash, no `/trpc` suffix).
|
|
27
|
+
* @example "https://pod.example.com"
|
|
28
|
+
*/
|
|
29
|
+
podUrl: string;
|
|
30
|
+
/**
|
|
31
|
+
* Ory Kratos session token (for example, from the Pod's
|
|
32
|
+
* `/api/federation/exchange` endpoint).
|
|
33
|
+
* Sent as `X-Session-Token` header — preferred for non-browser clients
|
|
34
|
+
* like extensions, CLI tools, and mobile apps.
|
|
35
|
+
* @example "oathkeeper_token_..."
|
|
36
|
+
*/
|
|
37
|
+
sessionToken?: string;
|
|
38
|
+
/**
|
|
39
|
+
* API key for authentication (Bearer token).
|
|
40
|
+
* Generate one in your workspace developer settings.
|
|
41
|
+
* If omitted, the client falls back to cookie-based Kratos session auth.
|
|
42
|
+
* @example "sk_live_abc123..."
|
|
43
|
+
*/
|
|
44
|
+
apiKey?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Workspace ID to scope all requests to.
|
|
47
|
+
* Pass empty string for procedures that don't require workspace context
|
|
48
|
+
* (e.g. workspaces.list, which is protectedProcedure only).
|
|
49
|
+
* @example "ws_01jk..."
|
|
50
|
+
*/
|
|
51
|
+
workspaceId: string;
|
|
52
|
+
/**
|
|
53
|
+
* Additional headers to include in every request.
|
|
54
|
+
*/
|
|
55
|
+
extraHeaders?: Record<string, string>;
|
|
56
|
+
/**
|
|
57
|
+
* Called when the pod returns a `401 Unauthorized` (expired/invalid session
|
|
58
|
+
* or API key). The app should sign out and re-authenticate (re-handshake).
|
|
59
|
+
*
|
|
60
|
+
* The pod's auth middleware can reply with a plain-JSON body
|
|
61
|
+
* (`{"error":"Invalid session"}`) that is NOT in tRPC batch format and would
|
|
62
|
+
* otherwise crash the superjson transformer; when an `onAuthError` handler is
|
|
63
|
+
* provided the client rewrites such responses into a well-formed tRPC error so
|
|
64
|
+
* the query rejects gracefully instead of throwing a parse error.
|
|
65
|
+
*/
|
|
66
|
+
onAuthError?: (error: {
|
|
67
|
+
status: number;
|
|
68
|
+
message: string;
|
|
69
|
+
}) => void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Creates a fully-typed tRPC client connected to a Synap data pod.
|
|
73
|
+
*
|
|
74
|
+
* All 47+ tRPC routers are available with full TypeScript autocomplete
|
|
75
|
+
* and compile-time safety. Auth is handled via Bearer token + workspace header.
|
|
76
|
+
*/
|
|
77
|
+
declare function createSynapClient(options: SynapClientOptions): CreateTRPCClient<AppRouter>;
|
|
78
|
+
type SynapClient = ReturnType<typeof createSynapClient>;
|
|
79
|
+
|
|
80
|
+
export { type SynapClient, type SynapClientOptions, createSynapClient };
|