@myco-dev/sdk 0.1.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/dist/server.js ADDED
@@ -0,0 +1,220 @@
1
+ // src/server.ts
2
+ import { createRemoteJWKSet, jwtVerify } from "jose";
3
+ var JWT_COOKIE_NAME = "app_jwt";
4
+ var jwksCache = null;
5
+ function getJwks(baseUrl) {
6
+ if (!jwksCache) {
7
+ jwksCache = createRemoteJWKSet(new URL(`${baseUrl}/auth/jwks`));
8
+ }
9
+ return jwksCache;
10
+ }
11
+ function getSharedBackendUrl(requestUrl) {
12
+ const url = new URL(requestUrl);
13
+ const isLocal = url.hostname === "localhost" || url.hostname === "127.0.0.1";
14
+ return isLocal ? "http://localhost:6926" : "https://api.myco.com";
15
+ }
16
+ function getAuthPortalUrl(requestUrl) {
17
+ const url = new URL(requestUrl);
18
+ const isLocal = url.hostname === "localhost" || url.hostname === "127.0.0.1";
19
+ return isLocal ? "http://localhost:5173" : "https://myco.com";
20
+ }
21
+ function getJwtFromCookie(request) {
22
+ const cookieHeader = request.headers.get("Cookie");
23
+ if (!cookieHeader) return null;
24
+ const cookies = cookieHeader.split(";").map((c) => c.trim());
25
+ let jwt = null;
26
+ for (const cookie of cookies) {
27
+ const [name, ...valueParts] = cookie.split("=");
28
+ if (name === JWT_COOKIE_NAME) {
29
+ jwt = valueParts.join("=");
30
+ }
31
+ }
32
+ return jwt;
33
+ }
34
+ async function verifyJwt(jwt, requestUrl) {
35
+ const baseUrl = getSharedBackendUrl(requestUrl);
36
+ const jwks = getJwks(baseUrl);
37
+ try {
38
+ const { payload } = await jwtVerify(jwt, jwks);
39
+ if (!payload.sub) return { user: null, error: "No sub claim in JWT" };
40
+ return {
41
+ user: {
42
+ userId: payload.sub,
43
+ email: payload.email,
44
+ name: payload.name
45
+ }
46
+ };
47
+ } catch (err) {
48
+ return { user: null, error: err instanceof Error ? err.message : String(err) };
49
+ }
50
+ }
51
+ function createServerSDK(env, request) {
52
+ const baseUrl = getSharedBackendUrl(request.url);
53
+ async function apiFetch(path, options = {}) {
54
+ const jwt = getJwtFromCookie(request);
55
+ const headers = new Headers(options.headers);
56
+ if (!headers.has("Content-Type") && options.body) {
57
+ headers.set("Content-Type", "application/json");
58
+ }
59
+ headers.set("Myco-App-Key", env.MYCO_APP_KEY);
60
+ if (jwt) {
61
+ headers.set("Authorization", `Bearer ${jwt}`);
62
+ }
63
+ const workspaceId = request.headers.get("Myco-Workspace-Id");
64
+ if (workspaceId) {
65
+ headers.set("Myco-Workspace-Id", workspaceId);
66
+ }
67
+ const response = await fetch(`${baseUrl}${path}`, {
68
+ ...options,
69
+ headers
70
+ });
71
+ if (!response.ok) {
72
+ const errorText = await response.text();
73
+ throw new Error(`API Error ${response.status} on ${path}: ${errorText}`);
74
+ }
75
+ const contentType = response.headers.get("Content-Type");
76
+ if (!contentType?.includes("application/json")) {
77
+ return void 0;
78
+ }
79
+ return response.json();
80
+ }
81
+ return {
82
+ fetch: apiFetch,
83
+ auth: {
84
+ async getVerifiedUser() {
85
+ const jwt = getJwtFromCookie(request);
86
+ if (!jwt) {
87
+ return { user: null, jwt: null };
88
+ }
89
+ const { user } = await verifyJwt(jwt, request.url);
90
+ return { user, jwt };
91
+ },
92
+ getLoginUrl(returnTo) {
93
+ const authBaseUrl = getAuthPortalUrl(request.url);
94
+ const encodedReturnTo = encodeURIComponent(returnTo);
95
+ return `${authBaseUrl}/federated/login?return_to=${encodedReturnTo}`;
96
+ }
97
+ },
98
+ workspaces: {
99
+ async list() {
100
+ return apiFetch("/api/workspaces");
101
+ },
102
+ async current() {
103
+ const workspaceId = request.headers.get("Myco-Workspace-Id");
104
+ if (!workspaceId) {
105
+ throw new Error("No workspace ID in request headers");
106
+ }
107
+ return apiFetch(`/api/workspaces/${workspaceId}`);
108
+ },
109
+ async create(name) {
110
+ return apiFetch("/api/workspaces", {
111
+ method: "POST",
112
+ body: JSON.stringify({ name })
113
+ });
114
+ },
115
+ async update(data) {
116
+ const workspaceId = request.headers.get("Myco-Workspace-Id");
117
+ if (!workspaceId) {
118
+ throw new Error("No workspace ID in request headers");
119
+ }
120
+ return apiFetch(`/api/workspaces/${workspaceId}`, {
121
+ method: "PATCH",
122
+ body: JSON.stringify(data)
123
+ });
124
+ },
125
+ async delete() {
126
+ const workspaceId = request.headers.get("Myco-Workspace-Id");
127
+ if (!workspaceId) {
128
+ throw new Error("No workspace ID in request headers");
129
+ }
130
+ await apiFetch(`/api/workspaces/${workspaceId}`, {
131
+ method: "DELETE"
132
+ });
133
+ },
134
+ async getMembers() {
135
+ const workspaceId = request.headers.get("Myco-Workspace-Id");
136
+ if (!workspaceId) {
137
+ throw new Error("No workspace ID in request headers");
138
+ }
139
+ return apiFetch(`/api/workspaces/${workspaceId}/members`);
140
+ },
141
+ async addMember(userId, role = "member") {
142
+ const workspaceId = request.headers.get("Myco-Workspace-Id");
143
+ if (!workspaceId) {
144
+ throw new Error("No workspace ID in request headers");
145
+ }
146
+ return apiFetch(`/api/workspaces/${workspaceId}/members`, {
147
+ method: "POST",
148
+ body: JSON.stringify({ userId, role })
149
+ });
150
+ },
151
+ async removeMember(userId) {
152
+ const workspaceId = request.headers.get("Myco-Workspace-Id");
153
+ if (!workspaceId) {
154
+ throw new Error("No workspace ID in request headers");
155
+ }
156
+ await apiFetch(`/api/workspaces/${workspaceId}/members/${userId}`, {
157
+ method: "DELETE"
158
+ });
159
+ }
160
+ },
161
+ data: {
162
+ async getEntities() {
163
+ return apiFetch("/api/entities");
164
+ },
165
+ async getEntity(entitySlug) {
166
+ return apiFetch(`/api/entities/${entitySlug}`);
167
+ },
168
+ async getRecords(entitySlug, options) {
169
+ const params = new URLSearchParams();
170
+ if (options?.page !== void 0) {
171
+ params.set("page", String(options.page));
172
+ }
173
+ if (options?.pageSize !== void 0) {
174
+ params.set("pageSize", String(options.pageSize));
175
+ }
176
+ if (options?.sort) {
177
+ params.set("sort", options.sort);
178
+ }
179
+ if (options?.order) {
180
+ params.set("order", options.order);
181
+ }
182
+ if (options?.filter) {
183
+ params.set("filter", JSON.stringify(options.filter));
184
+ }
185
+ const queryString = params.toString();
186
+ const path = `/api/entities/${entitySlug}/records${queryString ? `?${queryString}` : ""}`;
187
+ return apiFetch(path);
188
+ },
189
+ async getRecord(entitySlug, recordId) {
190
+ return apiFetch(`/api/entities/${entitySlug}/records/${recordId}`);
191
+ },
192
+ async createRecord(entitySlug, data) {
193
+ return apiFetch(`/api/entities/${entitySlug}/records`, {
194
+ method: "POST",
195
+ body: JSON.stringify({ data })
196
+ });
197
+ },
198
+ async updateRecord(entitySlug, recordId, data) {
199
+ return apiFetch(`/api/entities/${entitySlug}/records/${recordId}`, {
200
+ method: "PATCH",
201
+ body: JSON.stringify({ data })
202
+ });
203
+ },
204
+ async deleteRecord(entitySlug, recordId) {
205
+ await apiFetch(`/api/entities/${entitySlug}/records/${recordId}`, {
206
+ method: "DELETE"
207
+ });
208
+ },
209
+ async batchGetRecords(recordIds) {
210
+ return apiFetch("/api/records/batch", {
211
+ method: "POST",
212
+ body: JSON.stringify({ recordIds })
213
+ });
214
+ }
215
+ }
216
+ };
217
+ }
218
+ export {
219
+ createServerSDK
220
+ };
@@ -0,0 +1,76 @@
1
+ interface JoinResponse {
2
+ success: boolean;
3
+ message?: string;
4
+ workspaceId?: string;
5
+ }
6
+ interface VerifiedUser {
7
+ userId: string;
8
+ email?: string;
9
+ name?: string;
10
+ }
11
+ interface Workspace {
12
+ id: string;
13
+ name: string;
14
+ slug?: string;
15
+ createdAt: string;
16
+ updatedAt: string;
17
+ }
18
+ interface WorkspaceMember {
19
+ id: string;
20
+ userId: string;
21
+ workspaceId: string;
22
+ role: "owner" | "admin" | "member";
23
+ user?: {
24
+ id: string;
25
+ email: string;
26
+ name: string;
27
+ };
28
+ createdAt: string;
29
+ }
30
+ interface Entity {
31
+ id: string;
32
+ slug: string;
33
+ name: string;
34
+ description?: string;
35
+ fields: EntityField[];
36
+ }
37
+ interface EntityField {
38
+ id: string;
39
+ name: string;
40
+ slug: string;
41
+ type: string;
42
+ required?: boolean;
43
+ options?: Record<string, unknown>;
44
+ }
45
+ interface EntityRecord {
46
+ id: string;
47
+ entityId: string;
48
+ workspaceId: string;
49
+ data: Record<string, unknown>;
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ }
53
+ interface PaginatedResponse<T> {
54
+ data: T[];
55
+ total: number;
56
+ page: number;
57
+ pageSize: number;
58
+ hasMore: boolean;
59
+ }
60
+ interface GetRecordsOptions {
61
+ page?: number;
62
+ pageSize?: number;
63
+ sort?: string;
64
+ order?: "asc" | "desc";
65
+ filter?: Record<string, unknown>;
66
+ }
67
+ interface MycoSDKConfig {
68
+ appKey: string;
69
+ workspaceId?: string;
70
+ baseUrl?: string;
71
+ }
72
+ interface ServerEnv {
73
+ MYCO_APP_KEY: string;
74
+ }
75
+
76
+ export type { Entity as E, GetRecordsOptions as G, JoinResponse as J, MycoSDKConfig as M, PaginatedResponse as P, ServerEnv as S, VerifiedUser as V, Workspace as W, WorkspaceMember as a, EntityRecord as b, EntityField as c };
@@ -0,0 +1,89 @@
1
+ interface BootstrapResponse {
2
+ loggedIn: boolean;
3
+ enrolled: boolean;
4
+ userId?: string;
5
+ }
6
+ interface JoinResponse {
7
+ success: boolean;
8
+ message?: string;
9
+ workspaceId?: string;
10
+ }
11
+ interface VerifiedUser {
12
+ userId: string;
13
+ email?: string;
14
+ name?: string;
15
+ }
16
+ interface Workspace {
17
+ id: string;
18
+ name: string;
19
+ slug?: string;
20
+ createdAt: string;
21
+ updatedAt: string;
22
+ }
23
+ interface WorkspaceMember {
24
+ id: string;
25
+ userId: string;
26
+ workspaceId: string;
27
+ role: "owner" | "admin" | "member";
28
+ user?: {
29
+ id: string;
30
+ email: string;
31
+ name: string;
32
+ };
33
+ createdAt: string;
34
+ }
35
+ interface Entity {
36
+ id: string;
37
+ slug: string;
38
+ name: string;
39
+ description?: string;
40
+ fields: EntityField[];
41
+ }
42
+ interface EntityField {
43
+ id: string;
44
+ name: string;
45
+ slug: string;
46
+ type: string;
47
+ required?: boolean;
48
+ options?: Record<string, unknown>;
49
+ }
50
+ interface EntityRecord {
51
+ id: string;
52
+ entityId: string;
53
+ workspaceId: string;
54
+ data: Record<string, unknown>;
55
+ createdAt: string;
56
+ updatedAt: string;
57
+ }
58
+ interface PaginatedResponse<T> {
59
+ data: T[];
60
+ total: number;
61
+ page: number;
62
+ pageSize: number;
63
+ hasMore: boolean;
64
+ }
65
+ interface GetRecordsOptions {
66
+ page?: number;
67
+ pageSize?: number;
68
+ sort?: string;
69
+ order?: "asc" | "desc";
70
+ filter?: Record<string, unknown>;
71
+ }
72
+ interface MycoSDKConfig {
73
+ appKey: string;
74
+ workspaceId?: string;
75
+ baseUrl?: string;
76
+ }
77
+ interface ServerEnv {
78
+ APP_KEY: string;
79
+ INTERNAL_SECRET: string;
80
+ ENROLLMENT_KV?: KVNamespace;
81
+ }
82
+ interface KVNamespace {
83
+ get(key: string): Promise<string | null>;
84
+ put(key: string, value: string, options?: {
85
+ expirationTtl?: number;
86
+ }): Promise<void>;
87
+ }
88
+
89
+ export type { BootstrapResponse as B, Entity as E, GetRecordsOptions as G, JoinResponse as J, MycoSDKConfig as M, PaginatedResponse as P, ServerEnv as S, VerifiedUser as V, Workspace as W, WorkspaceMember as a, EntityRecord as b, EntityField as c };
@@ -0,0 +1,78 @@
1
+ interface JoinResponse {
2
+ success: boolean;
3
+ message?: string;
4
+ workspaceId?: string;
5
+ }
6
+ interface VerifiedUser {
7
+ userId: string;
8
+ email?: string;
9
+ name?: string;
10
+ }
11
+ interface Workspace {
12
+ id: string;
13
+ name: string;
14
+ slug?: string;
15
+ createdAt: string;
16
+ updatedAt: string;
17
+ }
18
+ interface WorkspaceMember {
19
+ id: string;
20
+ userId: string;
21
+ workspaceId: string;
22
+ role: "owner" | "admin" | "member";
23
+ user?: {
24
+ id: string;
25
+ email: string;
26
+ name: string;
27
+ };
28
+ createdAt: string;
29
+ }
30
+ interface Entity {
31
+ id: string;
32
+ slug: string;
33
+ name: string;
34
+ description?: string;
35
+ fields: EntityField[];
36
+ }
37
+ interface EntityField {
38
+ id: string;
39
+ name: string;
40
+ slug: string;
41
+ type: string;
42
+ required?: boolean;
43
+ options?: Record<string, unknown>;
44
+ }
45
+ interface EntityRecord {
46
+ id: string;
47
+ entityId: string;
48
+ workspaceId: string;
49
+ data: Record<string, unknown>;
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ }
53
+ interface PaginatedResponse<T> {
54
+ records: T[];
55
+ pagination: {
56
+ total: number;
57
+ limit: number;
58
+ offset: number;
59
+ hasMore: boolean;
60
+ };
61
+ }
62
+ interface GetRecordsOptions {
63
+ page?: number;
64
+ pageSize?: number;
65
+ sort?: string;
66
+ order?: "asc" | "desc";
67
+ filter?: Record<string, unknown>;
68
+ }
69
+ interface MycoSDKConfig {
70
+ appKey: string;
71
+ workspaceId?: string;
72
+ baseUrl?: string;
73
+ }
74
+ interface ServerEnv {
75
+ MYCO_APP_KEY: string;
76
+ }
77
+
78
+ export type { Entity as E, GetRecordsOptions as G, JoinResponse as J, MycoSDKConfig as M, PaginatedResponse as P, ServerEnv as S, VerifiedUser as V, Workspace as W, WorkspaceMember as a, EntityRecord as b, EntityField as c };
@@ -0,0 +1,89 @@
1
+ interface BootstrapResponse {
2
+ loggedIn: boolean;
3
+ enrolled: boolean;
4
+ userId?: string;
5
+ }
6
+ interface JoinResponse {
7
+ success: boolean;
8
+ message?: string;
9
+ workspaceId?: string;
10
+ }
11
+ interface VerifiedUser {
12
+ userId: string;
13
+ email?: string;
14
+ name?: string;
15
+ }
16
+ interface Workspace {
17
+ id: string;
18
+ name: string;
19
+ slug?: string;
20
+ createdAt: string;
21
+ updatedAt: string;
22
+ }
23
+ interface WorkspaceMember {
24
+ id: string;
25
+ userId: string;
26
+ workspaceId: string;
27
+ role: "owner" | "admin" | "member";
28
+ user?: {
29
+ id: string;
30
+ email: string;
31
+ name: string;
32
+ };
33
+ createdAt: string;
34
+ }
35
+ interface Entity {
36
+ id: string;
37
+ slug: string;
38
+ name: string;
39
+ description?: string;
40
+ fields: EntityField[];
41
+ }
42
+ interface EntityField {
43
+ id: string;
44
+ name: string;
45
+ slug: string;
46
+ type: string;
47
+ required?: boolean;
48
+ options?: Record<string, unknown>;
49
+ }
50
+ interface EntityRecord {
51
+ id: string;
52
+ entityId: string;
53
+ workspaceId: string;
54
+ data: Record<string, unknown>;
55
+ createdAt: string;
56
+ updatedAt: string;
57
+ }
58
+ interface PaginatedResponse<T> {
59
+ data: T[];
60
+ total: number;
61
+ page: number;
62
+ pageSize: number;
63
+ hasMore: boolean;
64
+ }
65
+ interface GetRecordsOptions {
66
+ page?: number;
67
+ pageSize?: number;
68
+ sort?: string;
69
+ order?: "asc" | "desc";
70
+ filter?: Record<string, unknown>;
71
+ }
72
+ interface MycoSDKConfig {
73
+ appKey: string;
74
+ workspaceId?: string;
75
+ baseUrl?: string;
76
+ }
77
+ interface ServerEnv {
78
+ MYCO_APP_KEY: string;
79
+ INTERNAL_SECRET: string;
80
+ ENROLLMENT_KV?: KVNamespace;
81
+ }
82
+ interface KVNamespace {
83
+ get(key: string): Promise<string | null>;
84
+ put(key: string, value: string, options?: {
85
+ expirationTtl?: number;
86
+ }): Promise<void>;
87
+ }
88
+
89
+ export type { BootstrapResponse as B, Entity as E, GetRecordsOptions as G, JoinResponse as J, MycoSDKConfig as M, PaginatedResponse as P, ServerEnv as S, VerifiedUser as V, Workspace as W, WorkspaceMember as a, EntityRecord as b, EntityField as c };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@myco-dev/sdk",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./server": {
13
+ "types": "./dist/server.d.ts",
14
+ "import": "./dist/server.js"
15
+ },
16
+ "./react": {
17
+ "types": "./dist/react.d.ts",
18
+ "import": "./dist/react.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup src/index.ts src/server.ts src/react.tsx --format esm --dts",
26
+ "dev": "tsup src/index.ts src/server.ts src/react.tsx --format esm --dts --watch"
27
+ },
28
+ "peerDependencies": {
29
+ "react": "^18 || ^19",
30
+ "better-auth": "^1.4",
31
+ "swr": "^2.4.0"
32
+ },
33
+ "dependencies": {
34
+ "jose": "^5.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/react": "^19.0.0",
38
+ "tsup": "^8.0.0",
39
+ "typescript": "^5.0.0"
40
+ }
41
+ }