@eidos.space/client 0.5.0 → 0.5.1
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 +8 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +46 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +24 -13
- package/src/index.ts +135 -0
package/README.md
CHANGED
|
@@ -115,6 +115,14 @@ The `createEidosClient` function accepts a configuration object:
|
|
|
115
115
|
| `fetch` | `typeof fetch` | Custom fetch implementation (optional) |
|
|
116
116
|
| `apiKey` | `string` | API Key for authentication (optional) |
|
|
117
117
|
|
|
118
|
+
> **Note on localhost subdomains:** The client automatically handles `*.localhost` URLs (e.g., `http://space1.localhost:3000/rpc`) to work around DNS issues. When such URLs are detected, the client will:
|
|
119
|
+
>
|
|
120
|
+
> - Rewrite the request to `127.0.0.1` (bypassing DNS resolution)
|
|
121
|
+
> - Preserve the original subdomain in the `Host` header
|
|
122
|
+
> - Add `X-Forwarded-Host` header for compatibility
|
|
123
|
+
>
|
|
124
|
+
> This ensures reliable communication with the Eidos Desktop RPC server when using subdomains.
|
|
125
|
+
|
|
118
126
|
## Features
|
|
119
127
|
|
|
120
128
|
- **Prisma-style API**: Type-safe (when used with generated types) and intuitive CRUD operations.
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/transport.ts","../src/space-proxy.ts","../src/binary-data.ts","../src/index.ts"],"mappings":";;;;AAMA;;;UAAiB,gBAAA;EAAA;EAEf,YAAA,GAAe,OAAA;IACb,KAAA;IACA,MAAA;IAAA,CACC,GAAA;EAAA,MACG,OAAA;EAAA;EAGN,cAAA,GAAiB,OAAA;IACf,KAAA;IACA,MAAA;IACA,MAAA,EAAQ,MAAA;IAAA,CACP,GAAA;EAAA,MACG,OAAA,CAAQ,MAAA;EAAA;EAGd,UAAA,GAAa,QAAA,aAAqB,IAAA,YAAgB,OAAA;EAAA;EAGlD,SAAA,GAAY,GAAA,UAAa,OAAA,GAAU,WAAA,KAAgB,OAAA,CAAQ,IAAA;EAAA;EAG3D,iBAAA,GAAoB,OAAA,UAAiB,KAAA,UAAe,OAAA;AAAA;AAAA;;;AAStD;;;AATsD,UASrC,WAAA,SAAoB,KAAA;EAAA;;;EAInC,YAAA,EAAc,WAAA;EAAA;;;EAId,KAAA,EAAO,WAAA;AAAA;AAAA;;;AAAA,UAMQ,iBAAA;EAAA;EAEf,QAAA;EAAA;EAEA,OAAA;EAAA;EAEA,KAAA,UAAe,KAAA;EAAA;EAEf,MAAA;EAAA;;;;EAKA,gBAAA,GAAmB,gBAAA;AAAA;;;;AC5DrB;;;UAAiB,eAAA;EACf,QAAA;EACA,OAAA;EACA,KAAA,UAAe,KAAA;EACf,MAAA;AAAA;AAAA,UAGe,aAAA;EACf,SAAA,IAAa,KAAA;IAAS,IAAA;EAAA;EACtB,KAAA;AAAA;AAAA;;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/transport.ts","../src/space-proxy.ts","../src/binary-data.ts","../src/index.ts"],"mappings":";;;;AAMA;;;UAAiB,gBAAA;EAAA;EAEf,YAAA,GAAe,OAAA;IACb,KAAA;IACA,MAAA;IAAA,CACC,GAAA;EAAA,MACG,OAAA;EAAA;EAGN,cAAA,GAAiB,OAAA;IACf,KAAA;IACA,MAAA;IACA,MAAA,EAAQ,MAAA;IAAA,CACP,GAAA;EAAA,MACG,OAAA,CAAQ,MAAA;EAAA;EAGd,UAAA,GAAa,QAAA,aAAqB,IAAA,YAAgB,OAAA;EAAA;EAGlD,SAAA,GAAY,GAAA,UAAa,OAAA,GAAU,WAAA,KAAgB,OAAA,CAAQ,IAAA;EAAA;EAG3D,iBAAA,GAAoB,OAAA,UAAiB,KAAA,UAAe,OAAA;AAAA;AAAA;;;AAStD;;;AATsD,UASrC,WAAA,SAAoB,KAAA;EAAA;;;EAInC,YAAA,EAAc,WAAA;EAAA;;;EAId,KAAA,EAAO,WAAA;AAAA;AAAA;;;AAAA,UAMQ,iBAAA;EAAA;EAEf,QAAA;EAAA;EAEA,OAAA;EAAA;EAEA,KAAA,UAAe,KAAA;EAAA;EAEf,MAAA;EAAA;;;;EAKA,gBAAA,GAAmB,gBAAA;AAAA;;;;AC5DrB;;;UAAiB,eAAA;EACf,QAAA;EACA,OAAA;EACA,KAAA,UAAe,KAAA;EACf,MAAA;AAAA;AAAA,UAGe,aAAA;EACf,SAAA,IAAa,KAAA;IAAS,IAAA;EAAA;EACtB,KAAA;AAAA;AAAA;;;AAAA,iBAiEc,mBAAA,CAAoB,MAAA,EAAQ,eAAA;8BAOR,OAAA,CAAQ,aAAA;;;;;;iBAuG5B,UAAA,CAAW,IAAA,EAAM,aAAA,GAAgB,OAAA;;;;ACjLjD;;iBAAgB,gBAAA,CAAiB,MAAA,EAAQ,eAAA;;;iBCXzB,kBAAA,CAAmB,IAAA;AAAA,iBAoFnB,4BAAA,CACd,IAAA,OACA,YAAA,GAAe,UAAA,EAAY,IAAA;AAAA,iBAiDb,iBAAA,CACd,IAAA,OACA,aAAA,EAAe,MAAA;AAAA,iBAiFK,sBAAA,CACpB,GAAA,EAAK,OAAA,GACJ,OAAA,CAAQ,MAAA;;;;AClKX;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,CAAkB,MAAA,EAAQ,iBAAA,GAAoB,WAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -196,18 +196,55 @@ async function parseMultipartFormData(req) {
|
|
|
196
196
|
//#endregion
|
|
197
197
|
//#region src/transport.ts
|
|
198
198
|
/**
|
|
199
|
+
* Check if URL uses *.localhost pattern which may have DNS issues
|
|
200
|
+
* e.g., xxx.localhost:3000 -> replace host with 127.0.0.1 but keep Host header
|
|
201
|
+
*/
|
|
202
|
+
function isLocalhostSubdomain(url) {
|
|
203
|
+
try {
|
|
204
|
+
const hostname = new URL(url).hostname;
|
|
205
|
+
return hostname.endsWith(".localhost") && hostname !== "localhost";
|
|
206
|
+
} catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Create a smart fetch that handles *.localhost DNS issues
|
|
212
|
+
* by converting to 127.0.0.1 while preserving the Host header
|
|
213
|
+
*/
|
|
214
|
+
function createSmartFetch(customFetch) {
|
|
215
|
+
const baseFetch = customFetch || globalThis.fetch;
|
|
216
|
+
return async (input, init) => {
|
|
217
|
+
const url = input.toString();
|
|
218
|
+
if (isLocalhostSubdomain(url)) {
|
|
219
|
+
const parsed = new URL(url);
|
|
220
|
+
const originalHost = parsed.host;
|
|
221
|
+
parsed.hostname;
|
|
222
|
+
parsed.hostname = "127.0.0.1";
|
|
223
|
+
const newUrl = parsed.toString();
|
|
224
|
+
const headers = new Headers(init?.headers);
|
|
225
|
+
headers.set("Host", originalHost);
|
|
226
|
+
if (!headers.has("X-Forwarded-Host")) headers.set("X-Forwarded-Host", originalHost);
|
|
227
|
+
return baseFetch(newUrl, {
|
|
228
|
+
...init,
|
|
229
|
+
headers
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return baseFetch(input, init);
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
199
236
|
* Create HTTP transport for RPC calls
|
|
200
237
|
*/
|
|
201
238
|
function createHttpTransport(config) {
|
|
202
239
|
const { endpoint, timeout = 3e4 } = config;
|
|
203
|
-
const fetchFn = config.fetch
|
|
240
|
+
const fetchFn = config.fetch ? createSmartFetch(config.fetch) : createSmartFetch();
|
|
204
241
|
return {
|
|
205
242
|
send: async (requestData) => {
|
|
206
243
|
const controller = new AbortController();
|
|
207
244
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
208
245
|
try {
|
|
209
246
|
let body;
|
|
210
|
-
let headers = { ...config.apiKey ? {
|
|
247
|
+
let headers = { ...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {} };
|
|
211
248
|
if (containsBinaryData(requestData)) {
|
|
212
249
|
const formData = new FormData();
|
|
213
250
|
let binaryIndex = 0;
|
|
@@ -287,11 +324,6 @@ function onCallBack(port) {
|
|
|
287
324
|
//#endregion
|
|
288
325
|
//#region src/space-proxy.ts
|
|
289
326
|
/**
|
|
290
|
-
* Space Proxy for Eidos RPC client
|
|
291
|
-
* Creates a Proxy-based interface for calling RPC methods
|
|
292
|
-
* Extracted and adapted from packages/sandbox/src/sdk-inject-script.html
|
|
293
|
-
*/
|
|
294
|
-
/**
|
|
295
327
|
* Create space proxy with Prisma-style API
|
|
296
328
|
*/
|
|
297
329
|
function createSpaceProxy(config) {
|
|
@@ -360,24 +392,24 @@ function createSpaceProxy(config) {
|
|
|
360
392
|
//#region src/index.ts
|
|
361
393
|
/**
|
|
362
394
|
* @eidos.space/client
|
|
363
|
-
*
|
|
395
|
+
*
|
|
364
396
|
* Eidos RPC client for Node.js and browser environments.
|
|
365
397
|
* Connect to a headless Eidos server via HTTP.
|
|
366
|
-
*
|
|
398
|
+
*
|
|
367
399
|
* @example
|
|
368
400
|
* ```typescript
|
|
369
401
|
* import { createEidosClient } from '@eidos.space/client'
|
|
370
|
-
*
|
|
402
|
+
*
|
|
371
403
|
* const eidos = createEidosClient({
|
|
372
404
|
* endpoint: 'http://localhost:3000/rpc'
|
|
373
405
|
* })
|
|
374
|
-
*
|
|
406
|
+
*
|
|
375
407
|
* // Query table data
|
|
376
408
|
* const posts = await eidos.currentSpace.table('posts').findMany({
|
|
377
409
|
* where: { published: true },
|
|
378
410
|
* orderBy: { created_at: 'desc' }
|
|
379
411
|
* })
|
|
380
|
-
*
|
|
412
|
+
*
|
|
381
413
|
* // Get document
|
|
382
414
|
* const doc = await eidos.currentSpace.doc.get('doc-id')
|
|
383
415
|
* ```
|
|
@@ -388,14 +420,14 @@ function createSpaceProxy(config) {
|
|
|
388
420
|
const NO_BRIDGE_ERROR = (feature) => `${feature} is not available without a mainThreadBridge. Please provide a mainThreadBridge in the configuration to enable this feature.`;
|
|
389
421
|
/**
|
|
390
422
|
* Create an Eidos client for connecting to headless server
|
|
391
|
-
*
|
|
423
|
+
*
|
|
392
424
|
* @example
|
|
393
425
|
* ```typescript
|
|
394
426
|
* // Basic usage (headless mode)
|
|
395
427
|
* const eidos = createEidosClient({
|
|
396
428
|
* endpoint: 'http://localhost:3000/rpc'
|
|
397
429
|
* })
|
|
398
|
-
*
|
|
430
|
+
*
|
|
399
431
|
* // With main thread bridge (for iframe/webview environments)
|
|
400
432
|
* const eidos = createEidosClient({
|
|
401
433
|
* endpoint: 'http://localhost:3000/rpc',
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/binary-data.ts","../src/transport.ts","../src/space-proxy.ts","../src/index.ts"],"sourcesContent":["// Helper function to check if data contains binary content\nexport function containsBinaryData(data: any): boolean {\n if (data === null || data === undefined) return false\n\n if (\n data instanceof ArrayBuffer ||\n data instanceof Blob ||\n data instanceof File\n ) {\n return true\n }\n\n // Check for TypedArray types (Uint8Array, Int8Array, etc.)\n // TypedArray instances have a buffer property that is an ArrayBuffer\n if (\n data &&\n data.buffer instanceof ArrayBuffer &&\n data.byteLength !== undefined\n ) {\n return true\n }\n\n if (Array.isArray(data)) {\n return data.some((item) => containsBinaryData(item))\n }\n\n if (typeof data === \"object\") {\n return Object.values(data).some((value) => containsBinaryData(value))\n }\n\n return false\n}\n\n// Helper function to process binary data and replace with references (for requests)\nexport function processBinaryData(\n data: any,\n onBinaryData: (binaryData: Blob) => string\n): any {\n if (data === null || data === undefined) return data\n\n if (data instanceof ArrayBuffer) {\n const fieldName = onBinaryData(new Blob([data]))\n return { __binary_ref: fieldName, type: \"ArrayBuffer\" }\n }\n\n // Handle TypedArray types (Uint8Array, Int8Array, etc.)\n if (\n data &&\n data.buffer instanceof ArrayBuffer &&\n data.byteLength !== undefined\n ) {\n const fieldName = onBinaryData(new Blob([data.buffer]))\n return {\n __binary_ref: fieldName,\n type: data.constructor.name,\n byteLength: data.byteLength,\n }\n }\n\n if (data instanceof Blob) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"Blob\" }\n }\n\n if (data instanceof File) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"File\", name: data.name }\n }\n\n if (Array.isArray(data)) {\n return data.map((item) => processBinaryData(item, onBinaryData))\n }\n\n if (typeof data === \"object\") {\n const processed: Record<string, any> = {}\n for (const [key, value] of Object.entries(data)) {\n processed[key] = processBinaryData(value, onBinaryData)\n }\n return processed\n }\n\n return data\n}\n\n// Helper function to process binary data for responses (server -> client)\nexport function processBinaryDataForResponse(\n data: any,\n onBinaryData: (binaryData: Blob) => string\n): any {\n if (data === null || data === undefined) return data\n\n if (data instanceof ArrayBuffer) {\n const fieldName = onBinaryData(new Blob([data]))\n return { __binary_ref: fieldName, type: \"ArrayBuffer\" }\n }\n\n // Handle TypedArray types (Uint8Array, Int8Array, etc.)\n if (\n data &&\n data.buffer instanceof ArrayBuffer &&\n data.byteLength !== undefined\n ) {\n const fieldName = onBinaryData(new Blob([data.buffer]))\n return {\n __binary_ref: fieldName,\n type: data.constructor.name,\n byteLength: data.byteLength,\n }\n }\n\n if (data instanceof Blob) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"Blob\" }\n }\n\n if (data instanceof File) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"File\", name: data.name }\n }\n\n if (Array.isArray(data)) {\n return data.map((item) => processBinaryDataForResponse(item, onBinaryData))\n }\n\n if (typeof data === \"object\") {\n const processed: Record<string, any> = {}\n for (const [key, value] of Object.entries(data)) {\n processed[key] = processBinaryDataForResponse(value, onBinaryData)\n }\n return processed\n }\n\n return data\n}\n\n// Helper function to restore binary data from form fields (for responses)\nexport function restoreBinaryData(\n data: any,\n binaryDataMap: Record<string, any>\n): any {\n if (data === null || data === undefined) return data\n\n if (typeof data === \"object\" && data.__binary_ref) {\n const binaryData = binaryDataMap[data.__binary_ref]\n if (!binaryData) {\n throw new Error(`Binary data reference not found: ${data.__binary_ref}`)\n }\n\n if (data.type === \"ArrayBuffer\") {\n // Return the ArrayBuffer directly\n return binaryData.data\n }\n\n // Handle TypedArray types\n if (\n data.type &&\n data.type.includes(\"Array\") &&\n data.type !== \"ArrayBuffer\"\n ) {\n // Create a view of the ArrayBuffer for the specific TypedArray type\n const arrayBuffer = binaryData.data\n const byteLength = data.byteLength || arrayBuffer.byteLength\n\n switch (data.type) {\n case \"Uint8Array\":\n return new Uint8Array(arrayBuffer, 0, byteLength)\n case \"Uint8ClampedArray\":\n return new Uint8ClampedArray(arrayBuffer, 0, byteLength)\n case \"Uint16Array\":\n return new Uint16Array(arrayBuffer, 0, byteLength / 2)\n case \"Uint32Array\":\n return new Uint32Array(arrayBuffer, 0, byteLength / 4)\n case \"Int8Array\":\n return new Int8Array(arrayBuffer, 0, byteLength)\n case \"Int16Array\":\n return new Int16Array(arrayBuffer, 0, byteLength / 2)\n case \"Int32Array\":\n return new Int32Array(arrayBuffer, 0, byteLength / 4)\n case \"Float32Array\":\n return new Float32Array(arrayBuffer, 0, byteLength / 4)\n case \"Float64Array\":\n return new Float64Array(arrayBuffer, 0, byteLength / 8)\n default:\n // Fallback: create Uint8Array for unknown TypedArray types\n return new Uint8Array(arrayBuffer, 0, byteLength)\n }\n }\n\n if (data.type === \"Blob\") {\n // Create a Blob from the ArrayBuffer\n return new Blob([binaryData.data], { type: binaryData.type })\n }\n\n if (data.type === \"File\") {\n // Create a File from the ArrayBuffer\n return new File([binaryData.data], data.name || binaryData.name, {\n type: binaryData.type,\n })\n }\n\n return binaryData\n }\n\n if (Array.isArray(data)) {\n return data.map((item) => restoreBinaryData(item, binaryDataMap))\n }\n\n if (typeof data === \"object\") {\n const restored: Record<string, any> = {}\n for (const [key, value] of Object.entries(data)) {\n restored[key] = restoreBinaryData(value, binaryDataMap)\n }\n return restored\n }\n\n return data\n}\n\n// Helper function to parse multipart form data manually (server-side only)\nexport async function parseMultipartFormData(\n req: Request\n): Promise<Record<string, any>> {\n const contentType = req.headers.get(\"content-type\") || \"\"\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"Not a multipart/form-data request\")\n }\n\n // Extract boundary from content-type header\n const boundaryMatch = contentType.match(/boundary=([^;]+)/)\n if (!boundaryMatch) {\n throw new Error(\"No boundary found in multipart/form-data header\")\n }\n const boundary = boundaryMatch[1].trim()\n\n // Read the request body as ArrayBuffer to preserve binary data\n const bodyBuffer = await req.arrayBuffer()\n const bodyBytes = new Uint8Array(bodyBuffer)\n\n // Convert boundary to bytes\n const boundaryBytes = new TextEncoder().encode(`--${boundary}`)\n\n const formData: Record<string, any> = {}\n\n // Find all parts by splitting on boundary\n let start = 0\n let partIndex = 0\n\n while (start < bodyBytes.length) {\n // Find next boundary\n let boundaryStart = -1\n for (let i = start; i <= bodyBytes.length - boundaryBytes.length; i++) {\n let match = true\n for (let j = 0; j < boundaryBytes.length; j++) {\n if (bodyBytes[i + j] !== boundaryBytes[j]) {\n match = false\n break\n }\n }\n if (match) {\n boundaryStart = i\n break\n }\n }\n\n if (boundaryStart === -1) break\n\n // Skip boundary and CRLF\n start = boundaryStart + boundaryBytes.length\n if (\n start < bodyBytes.length &&\n bodyBytes[start] === 13 &&\n bodyBytes[start + 1] === 10\n ) {\n start += 2\n }\n\n // Find end of this part (next boundary or end of data)\n let nextBoundaryStart = -1\n for (let i = start; i <= bodyBytes.length - boundaryBytes.length; i++) {\n let match = true\n for (let j = 0; j < boundaryBytes.length; j++) {\n if (bodyBytes[i + j] !== boundaryBytes[j]) {\n match = false\n break\n }\n }\n if (match) {\n nextBoundaryStart = i\n break\n }\n }\n\n if (nextBoundaryStart === -1) {\n // Last part - check for terminating boundary\n const termBoundary = new TextEncoder().encode(`--${boundary}--`)\n for (let i = start; i <= bodyBytes.length - termBoundary.length; i++) {\n let match = true\n for (let j = 0; j < termBoundary.length; j++) {\n if (bodyBytes[i + j] !== termBoundary[j]) {\n match = false\n break\n }\n }\n if (match) {\n nextBoundaryStart = i\n break\n }\n }\n if (nextBoundaryStart === -1) {\n nextBoundaryStart = bodyBytes.length\n }\n }\n\n const partEnd = nextBoundaryStart\n if (partEnd <= start) break\n\n // Extract this part\n const partBytes = bodyBytes.slice(start, partEnd)\n\n // Find header/body split (look for \\r\\n\\r\\n)\n let headerEnd = -1\n for (let i = 0; i <= partBytes.length - 4; i++) {\n if (\n partBytes[i] === 13 &&\n partBytes[i + 1] === 10 &&\n partBytes[i + 2] === 13 &&\n partBytes[i + 3] === 10\n ) {\n headerEnd = i\n break\n }\n }\n\n if (headerEnd === -1) {\n start = partEnd\n continue\n }\n\n // Parse headers\n const headerBytes = partBytes.slice(0, headerEnd)\n const headerText = new TextDecoder().decode(headerBytes)\n\n // Extract field name from Content-Disposition header\n const contentDisposition = headerText.match(\n /Content-Disposition: form-data; name=\"([^\"]+)\"/\n )\n if (!contentDisposition) {\n start = partEnd\n continue\n }\n\n const fieldName = contentDisposition[1]\n\n // Extract content (skip the \\r\\n\\r\\n separator)\n const contentStart = headerEnd + 4\n let contentEnd = partBytes.length\n\n // Remove trailing \\r\\n if present\n if (\n contentEnd >= contentStart + 2 &&\n partBytes[contentEnd - 2] === 13 &&\n partBytes[contentEnd - 1] === 10\n ) {\n contentEnd -= 2\n }\n\n const contentBytes = partBytes.slice(contentStart, contentEnd)\n\n // Check if this is a file upload\n const filenameMatch = headerText.match(/filename=\"([^\"]+)\"/)\n\n if (filenameMatch) {\n // This is a file upload - create a Blob-like object\n const filename = filenameMatch[1]\n const contentTypeMatch = headerText.match(/Content-Type: ([^\\r\\n]+)/)\n const mimeType = contentTypeMatch\n ? contentTypeMatch[1].trim()\n : \"application/octet-stream\"\n\n // Create ArrayBuffer from raw bytes\n const arrayBuffer = contentBytes.slice().buffer\n\n formData[fieldName] = {\n name: filename,\n type: mimeType,\n data: arrayBuffer,\n size: contentBytes.length,\n }\n } else {\n // This is a regular field - decode as text\n formData[fieldName] = new TextDecoder().decode(contentBytes)\n }\n\n start = partEnd\n partIndex++\n }\n\n return formData\n}\n","/**\n * HTTP Transport for Eidos RPC client\n * Extracted and adapted from packages/sandbox/src/sdk-inject-script.html\n */\n\nexport interface TransportConfig {\n endpoint: string\n timeout?: number\n fetch?: typeof fetch\n apiKey?: string\n}\n\nexport interface TransportPort {\n onmessage: ((event: { data: any }) => void) | null\n close: () => void\n}\n\nimport { containsBinaryData, processBinaryData, restoreBinaryData } from './binary-data'\n\n/**\n * Create HTTP transport for RPC calls\n */\nexport function createHttpTransport(config: TransportConfig) {\n const { endpoint, timeout = 30000 } = config\n const fetchFn = config.fetch || globalThis.fetch\n \n return {\n send: async (requestData: any): Promise<TransportPort> => {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n \n try {\n let body: any\n let headers: Record<string, string> = {\n ...(config.apiKey ? { 'Authorization': `Bearer ${config.apiKey}` } : {}),\n }\n\n const hasBinaryData = containsBinaryData(requestData)\n\n if (hasBinaryData) {\n const formData = new FormData()\n let binaryIndex = 0\n const processedData = processBinaryData(requestData, (binaryData) => {\n const fieldName = `binary_${binaryIndex++}`\n formData.append(fieldName, binaryData)\n return fieldName\n })\n formData.append('json', JSON.stringify(processedData))\n body = formData\n // Browser will set Content-Type with boundary for FormData\n } else {\n body = JSON.stringify(requestData)\n headers['Content-Type'] = 'application/json'\n }\n\n const response = await fetchFn(endpoint, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n })\n \n clearTimeout(timeoutId)\n \n if (!response.ok) {\n throw new Error(`HTTP error: ${response.status}`)\n }\n \n const contentType = response.headers.get('content-type')\n let responseData: any\n\n if (contentType && contentType.includes('multipart/form-data')) {\n const formData = await response.formData()\n const jsonData = JSON.parse(formData.get('json') as string || '{}')\n \n if (!jsonData.success) {\n throw new Error(jsonData.error || 'RPC call failed')\n }\n\n const binaryDataMap: Record<string, any> = {}\n for (const [key, entryValue] of formData.entries()) {\n const value = entryValue as any\n if (key.startsWith('binary_') && value instanceof Blob) {\n const arrayBuffer = await value.arrayBuffer()\n binaryDataMap[key] = {\n data: arrayBuffer,\n type: value.type,\n size: value.size,\n }\n }\n }\n responseData = restoreBinaryData(jsonData.data, binaryDataMap)\n } else {\n const jsonData = await response.json()\n if (!jsonData.success) {\n throw new Error(jsonData.error || 'RPC call failed')\n }\n responseData = jsonData.data\n }\n \n // Create simulated port for callback compatibility\n const simulatedPort: TransportPort = {\n onmessage: null,\n close: () => {},\n }\n \n // Async callback\n setTimeout(() => {\n if (simulatedPort.onmessage) {\n simulatedPort.onmessage({\n data: { type: 'rpcCallResp', data: responseData },\n })\n }\n }, 0)\n \n return simulatedPort\n } catch (error) {\n clearTimeout(timeoutId)\n throw error\n }\n },\n close: () => {},\n }\n}\n\n/**\n * Wait for callback from transport port\n */\nexport function onCallBack(port: TransportPort): Promise<any> {\n return new Promise((resolve, reject) => {\n port.onmessage = (event) => {\n port.close()\n const { type, data } = event.data\n if (type === 'rpcCallResp') {\n resolve(data)\n } else {\n reject(new Error('RPC call failed'))\n }\n }\n })\n}\n","/**\n * Space Proxy for Eidos RPC client\n * Creates a Proxy-based interface for calling RPC methods\n * Extracted and adapted from packages/sandbox/src/sdk-inject-script.html\n */\n\nimport { createHttpTransport, onCallBack, TransportConfig } from './transport'\n\n/**\n * Create space proxy with Prisma-style API\n */\nexport function createSpaceProxy(config: TransportConfig) {\n const transport = createHttpTransport(config)\n \n return new Proxy({}, {\n get: (target, method: string) => {\n // Prisma-style table() API\n if (method === 'table') {\n return function(tableId: string) {\n const tableMethods = [\n 'create',\n 'createMany', \n 'findUnique',\n 'findFirst',\n 'findMany',\n 'count',\n 'update',\n 'updateMany',\n 'delete',\n 'deleteMany',\n ]\n \n return new Proxy({}, {\n get(target, tableMethod: string) {\n if (tableMethods.includes(tableMethod)) {\n return async function(args?: any) {\n const port = await transport.send({\n method: `table(${tableId}).${tableMethod}`,\n params: [args],\n })\n return onCallBack(port)\n }\n }\n return undefined\n },\n })\n }\n }\n \n // Namespace APIs: doc, tree, file, kv, fs, etc.\n const namespaces = [\n 'doc',\n 'action', \n 'schema',\n 'graft',\n 'script',\n 'extension',\n 'tree',\n 'view',\n 'column',\n 'embedding',\n 'file',\n 'extNode',\n 'theme',\n 'dataView',\n 'kv',\n 'fs',\n 'AI',\n 'ai',\n 'utils',\n ]\n \n if (namespaces.includes(method)) {\n return new Proxy({}, {\n get(target, subMethod: string) {\n return async function(...params: any[]) {\n const port = await transport.send({\n method: `${method}.${subMethod}`,\n params,\n })\n return onCallBack(port)\n }\n },\n })\n }\n \n // Direct method call\n return async (...params: any[]) => {\n const port = await transport.send({\n method,\n params,\n })\n return onCallBack(port)\n }\n },\n })\n}\n","/**\n * @eidos.space/client\n * \n * Eidos RPC client for Node.js and browser environments.\n * Connect to a headless Eidos server via HTTP.\n * \n * @example\n * ```typescript\n * import { createEidosClient } from '@eidos.space/client'\n * \n * const eidos = createEidosClient({\n * endpoint: 'http://localhost:3000/rpc'\n * })\n * \n * // Query table data\n * const posts = await eidos.currentSpace.table('posts').findMany({\n * where: { published: true },\n * orderBy: { created_at: 'desc' }\n * })\n * \n * // Get document\n * const doc = await eidos.currentSpace.doc.get('doc-id')\n * ```\n */\n\nimport { createSpaceProxy } from './space-proxy'\nimport type { EidosClient, EidosClientConfig } from './types'\nimport type { DataSpace } from '@eidos.space/core'\n\n/**\n * Error message for features not available without main thread bridge\n */\nconst NO_BRIDGE_ERROR = (feature: string) => \n `${feature} is not available without a mainThreadBridge. ` +\n `Please provide a mainThreadBridge in the configuration to enable this feature.`\n\n/**\n * Create an Eidos client for connecting to headless server\n * \n * @example\n * ```typescript\n * // Basic usage (headless mode)\n * const eidos = createEidosClient({\n * endpoint: 'http://localhost:3000/rpc'\n * })\n * \n * // With main thread bridge (for iframe/webview environments)\n * const eidos = createEidosClient({\n * endpoint: 'http://localhost:3000/rpc',\n * mainThreadBridge: {\n * generateText: async (options) => { ... },\n * generateObject: async (options) => { ... },\n * callScript: async (scriptId, ...args) => { ... },\n * fetchBlob: async (url, options) => { ... },\n * tableHighlightRow: (tableId, rowId, fieldId) => { ... }\n * }\n * })\n * ```\n */\nexport function createEidosClient(config: EidosClientConfig): EidosClient {\n const { endpoint, timeout, fetch: fetchFn, mainThreadBridge } = config\n \n const spaceProxy = createSpaceProxy({\n endpoint,\n timeout,\n fetch: fetchFn,\n apiKey: config.apiKey,\n }) as unknown as DataSpace\n \n return {\n currentSpace: spaceProxy,\n space: spaceProxy,\n \n // AI methods - use bridge if available\n AI: {\n generateText: (options) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR('AI.generateText'))\n }\n return mainThreadBridge.generateText(options)\n },\n generateObject: (options) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR('AI.generateObject'))\n }\n return mainThreadBridge.generateObject(options)\n },\n },\n \n // Script methods - use bridge if available\n script: {\n call: (scriptId, ...args) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR('script.call'))\n }\n return mainThreadBridge.callScript(scriptId, ...args)\n },\n },\n \n // Utils methods - use bridge if available\n utils: {\n fetchBlob: (url, options) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR('utils.fetchBlob'))\n }\n return mainThreadBridge.fetchBlob(url, options)\n },\n tableHighlightRow: (tableId, rowId, fieldId) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR('utils.tableHighlightRow'))\n }\n return mainThreadBridge.tableHighlightRow(tableId, rowId, fieldId)\n },\n },\n }\n}\n\n// Re-export types\nexport type {\n EidosClient,\n EidosClientConfig,\n MainThreadBridge,\n} from './types'\n\n// Re-export DataSpace for convenience\nexport type { DataSpace } from '@eidos.space/core'\n\n// Re-export low-level APIs for advanced usage\nexport { createSpaceProxy } from './space-proxy'\nexport { createHttpTransport, onCallBack } from './transport'\nexport type { TransportConfig, TransportPort } from './transport'\n\n// Re-export binary data utilities for server-side usage\nexport {\n containsBinaryData,\n processBinaryDataForResponse,\n restoreBinaryData,\n parseMultipartFormData,\n} from './binary-data'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/binary-data.ts","../src/transport.ts","../src/space-proxy.ts","../src/index.ts"],"sourcesContent":["// Helper function to check if data contains binary content\nexport function containsBinaryData(data: any): boolean {\n if (data === null || data === undefined) return false\n\n if (\n data instanceof ArrayBuffer ||\n data instanceof Blob ||\n data instanceof File\n ) {\n return true\n }\n\n // Check for TypedArray types (Uint8Array, Int8Array, etc.)\n // TypedArray instances have a buffer property that is an ArrayBuffer\n if (\n data &&\n data.buffer instanceof ArrayBuffer &&\n data.byteLength !== undefined\n ) {\n return true\n }\n\n if (Array.isArray(data)) {\n return data.some((item) => containsBinaryData(item))\n }\n\n if (typeof data === \"object\") {\n return Object.values(data).some((value) => containsBinaryData(value))\n }\n\n return false\n}\n\n// Helper function to process binary data and replace with references (for requests)\nexport function processBinaryData(\n data: any,\n onBinaryData: (binaryData: Blob) => string\n): any {\n if (data === null || data === undefined) return data\n\n if (data instanceof ArrayBuffer) {\n const fieldName = onBinaryData(new Blob([data]))\n return { __binary_ref: fieldName, type: \"ArrayBuffer\" }\n }\n\n // Handle TypedArray types (Uint8Array, Int8Array, etc.)\n if (\n data &&\n data.buffer instanceof ArrayBuffer &&\n data.byteLength !== undefined\n ) {\n const fieldName = onBinaryData(new Blob([data.buffer]))\n return {\n __binary_ref: fieldName,\n type: data.constructor.name,\n byteLength: data.byteLength,\n }\n }\n\n if (data instanceof Blob) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"Blob\" }\n }\n\n if (data instanceof File) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"File\", name: data.name }\n }\n\n if (Array.isArray(data)) {\n return data.map((item) => processBinaryData(item, onBinaryData))\n }\n\n if (typeof data === \"object\") {\n const processed: Record<string, any> = {}\n for (const [key, value] of Object.entries(data)) {\n processed[key] = processBinaryData(value, onBinaryData)\n }\n return processed\n }\n\n return data\n}\n\n// Helper function to process binary data for responses (server -> client)\nexport function processBinaryDataForResponse(\n data: any,\n onBinaryData: (binaryData: Blob) => string\n): any {\n if (data === null || data === undefined) return data\n\n if (data instanceof ArrayBuffer) {\n const fieldName = onBinaryData(new Blob([data]))\n return { __binary_ref: fieldName, type: \"ArrayBuffer\" }\n }\n\n // Handle TypedArray types (Uint8Array, Int8Array, etc.)\n if (\n data &&\n data.buffer instanceof ArrayBuffer &&\n data.byteLength !== undefined\n ) {\n const fieldName = onBinaryData(new Blob([data.buffer]))\n return {\n __binary_ref: fieldName,\n type: data.constructor.name,\n byteLength: data.byteLength,\n }\n }\n\n if (data instanceof Blob) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"Blob\" }\n }\n\n if (data instanceof File) {\n const fieldName = onBinaryData(data)\n return { __binary_ref: fieldName, type: \"File\", name: data.name }\n }\n\n if (Array.isArray(data)) {\n return data.map((item) => processBinaryDataForResponse(item, onBinaryData))\n }\n\n if (typeof data === \"object\") {\n const processed: Record<string, any> = {}\n for (const [key, value] of Object.entries(data)) {\n processed[key] = processBinaryDataForResponse(value, onBinaryData)\n }\n return processed\n }\n\n return data\n}\n\n// Helper function to restore binary data from form fields (for responses)\nexport function restoreBinaryData(\n data: any,\n binaryDataMap: Record<string, any>\n): any {\n if (data === null || data === undefined) return data\n\n if (typeof data === \"object\" && data.__binary_ref) {\n const binaryData = binaryDataMap[data.__binary_ref]\n if (!binaryData) {\n throw new Error(`Binary data reference not found: ${data.__binary_ref}`)\n }\n\n if (data.type === \"ArrayBuffer\") {\n // Return the ArrayBuffer directly\n return binaryData.data\n }\n\n // Handle TypedArray types\n if (\n data.type &&\n data.type.includes(\"Array\") &&\n data.type !== \"ArrayBuffer\"\n ) {\n // Create a view of the ArrayBuffer for the specific TypedArray type\n const arrayBuffer = binaryData.data\n const byteLength = data.byteLength || arrayBuffer.byteLength\n\n switch (data.type) {\n case \"Uint8Array\":\n return new Uint8Array(arrayBuffer, 0, byteLength)\n case \"Uint8ClampedArray\":\n return new Uint8ClampedArray(arrayBuffer, 0, byteLength)\n case \"Uint16Array\":\n return new Uint16Array(arrayBuffer, 0, byteLength / 2)\n case \"Uint32Array\":\n return new Uint32Array(arrayBuffer, 0, byteLength / 4)\n case \"Int8Array\":\n return new Int8Array(arrayBuffer, 0, byteLength)\n case \"Int16Array\":\n return new Int16Array(arrayBuffer, 0, byteLength / 2)\n case \"Int32Array\":\n return new Int32Array(arrayBuffer, 0, byteLength / 4)\n case \"Float32Array\":\n return new Float32Array(arrayBuffer, 0, byteLength / 4)\n case \"Float64Array\":\n return new Float64Array(arrayBuffer, 0, byteLength / 8)\n default:\n // Fallback: create Uint8Array for unknown TypedArray types\n return new Uint8Array(arrayBuffer, 0, byteLength)\n }\n }\n\n if (data.type === \"Blob\") {\n // Create a Blob from the ArrayBuffer\n return new Blob([binaryData.data], { type: binaryData.type })\n }\n\n if (data.type === \"File\") {\n // Create a File from the ArrayBuffer\n return new File([binaryData.data], data.name || binaryData.name, {\n type: binaryData.type,\n })\n }\n\n return binaryData\n }\n\n if (Array.isArray(data)) {\n return data.map((item) => restoreBinaryData(item, binaryDataMap))\n }\n\n if (typeof data === \"object\") {\n const restored: Record<string, any> = {}\n for (const [key, value] of Object.entries(data)) {\n restored[key] = restoreBinaryData(value, binaryDataMap)\n }\n return restored\n }\n\n return data\n}\n\n// Helper function to parse multipart form data manually (server-side only)\nexport async function parseMultipartFormData(\n req: Request\n): Promise<Record<string, any>> {\n const contentType = req.headers.get(\"content-type\") || \"\"\n if (!contentType.includes(\"multipart/form-data\")) {\n throw new Error(\"Not a multipart/form-data request\")\n }\n\n // Extract boundary from content-type header\n const boundaryMatch = contentType.match(/boundary=([^;]+)/)\n if (!boundaryMatch) {\n throw new Error(\"No boundary found in multipart/form-data header\")\n }\n const boundary = boundaryMatch[1].trim()\n\n // Read the request body as ArrayBuffer to preserve binary data\n const bodyBuffer = await req.arrayBuffer()\n const bodyBytes = new Uint8Array(bodyBuffer)\n\n // Convert boundary to bytes\n const boundaryBytes = new TextEncoder().encode(`--${boundary}`)\n\n const formData: Record<string, any> = {}\n\n // Find all parts by splitting on boundary\n let start = 0\n let partIndex = 0\n\n while (start < bodyBytes.length) {\n // Find next boundary\n let boundaryStart = -1\n for (let i = start; i <= bodyBytes.length - boundaryBytes.length; i++) {\n let match = true\n for (let j = 0; j < boundaryBytes.length; j++) {\n if (bodyBytes[i + j] !== boundaryBytes[j]) {\n match = false\n break\n }\n }\n if (match) {\n boundaryStart = i\n break\n }\n }\n\n if (boundaryStart === -1) break\n\n // Skip boundary and CRLF\n start = boundaryStart + boundaryBytes.length\n if (\n start < bodyBytes.length &&\n bodyBytes[start] === 13 &&\n bodyBytes[start + 1] === 10\n ) {\n start += 2\n }\n\n // Find end of this part (next boundary or end of data)\n let nextBoundaryStart = -1\n for (let i = start; i <= bodyBytes.length - boundaryBytes.length; i++) {\n let match = true\n for (let j = 0; j < boundaryBytes.length; j++) {\n if (bodyBytes[i + j] !== boundaryBytes[j]) {\n match = false\n break\n }\n }\n if (match) {\n nextBoundaryStart = i\n break\n }\n }\n\n if (nextBoundaryStart === -1) {\n // Last part - check for terminating boundary\n const termBoundary = new TextEncoder().encode(`--${boundary}--`)\n for (let i = start; i <= bodyBytes.length - termBoundary.length; i++) {\n let match = true\n for (let j = 0; j < termBoundary.length; j++) {\n if (bodyBytes[i + j] !== termBoundary[j]) {\n match = false\n break\n }\n }\n if (match) {\n nextBoundaryStart = i\n break\n }\n }\n if (nextBoundaryStart === -1) {\n nextBoundaryStart = bodyBytes.length\n }\n }\n\n const partEnd = nextBoundaryStart\n if (partEnd <= start) break\n\n // Extract this part\n const partBytes = bodyBytes.slice(start, partEnd)\n\n // Find header/body split (look for \\r\\n\\r\\n)\n let headerEnd = -1\n for (let i = 0; i <= partBytes.length - 4; i++) {\n if (\n partBytes[i] === 13 &&\n partBytes[i + 1] === 10 &&\n partBytes[i + 2] === 13 &&\n partBytes[i + 3] === 10\n ) {\n headerEnd = i\n break\n }\n }\n\n if (headerEnd === -1) {\n start = partEnd\n continue\n }\n\n // Parse headers\n const headerBytes = partBytes.slice(0, headerEnd)\n const headerText = new TextDecoder().decode(headerBytes)\n\n // Extract field name from Content-Disposition header\n const contentDisposition = headerText.match(\n /Content-Disposition: form-data; name=\"([^\"]+)\"/\n )\n if (!contentDisposition) {\n start = partEnd\n continue\n }\n\n const fieldName = contentDisposition[1]\n\n // Extract content (skip the \\r\\n\\r\\n separator)\n const contentStart = headerEnd + 4\n let contentEnd = partBytes.length\n\n // Remove trailing \\r\\n if present\n if (\n contentEnd >= contentStart + 2 &&\n partBytes[contentEnd - 2] === 13 &&\n partBytes[contentEnd - 1] === 10\n ) {\n contentEnd -= 2\n }\n\n const contentBytes = partBytes.slice(contentStart, contentEnd)\n\n // Check if this is a file upload\n const filenameMatch = headerText.match(/filename=\"([^\"]+)\"/)\n\n if (filenameMatch) {\n // This is a file upload - create a Blob-like object\n const filename = filenameMatch[1]\n const contentTypeMatch = headerText.match(/Content-Type: ([^\\r\\n]+)/)\n const mimeType = contentTypeMatch\n ? contentTypeMatch[1].trim()\n : \"application/octet-stream\"\n\n // Create ArrayBuffer from raw bytes\n const arrayBuffer = contentBytes.slice().buffer\n\n formData[fieldName] = {\n name: filename,\n type: mimeType,\n data: arrayBuffer,\n size: contentBytes.length,\n }\n } else {\n // This is a regular field - decode as text\n formData[fieldName] = new TextDecoder().decode(contentBytes)\n }\n\n start = partEnd\n partIndex++\n }\n\n return formData\n}\n","/**\n * HTTP Transport for Eidos RPC client\n * Extracted and adapted from packages/sandbox/src/sdk-inject-script.html\n */\n\nexport interface TransportConfig {\n endpoint: string\n timeout?: number\n fetch?: typeof fetch\n apiKey?: string\n}\n\nexport interface TransportPort {\n onmessage: ((event: { data: any }) => void) | null\n close: () => void\n}\n\nimport {\n containsBinaryData,\n processBinaryData,\n restoreBinaryData,\n} from \"./binary-data\"\n\n/**\n * Check if URL uses *.localhost pattern which may have DNS issues\n * e.g., xxx.localhost:3000 -> replace host with 127.0.0.1 but keep Host header\n */\nfunction isLocalhostSubdomain(url: string): boolean {\n try {\n const parsed = new URL(url)\n const hostname = parsed.hostname\n // Match *.localhost or *.localhost.localdomain\n return hostname.endsWith(\".localhost\") && hostname !== \"localhost\"\n } catch {\n return false\n }\n}\n\n/**\n * Create a smart fetch that handles *.localhost DNS issues\n * by converting to 127.0.0.1 while preserving the Host header\n */\nfunction createSmartFetch(customFetch?: typeof fetch): typeof fetch {\n const baseFetch = customFetch || globalThis.fetch\n\n return async (input: RequestInfo | URL, init?: RequestInit) => {\n const url = input.toString()\n\n if (isLocalhostSubdomain(url)) {\n const parsed = new URL(url)\n const originalHost = parsed.host // includes port\n const originalHostname = parsed.hostname\n\n // Replace hostname with 127.0.0.1\n parsed.hostname = \"127.0.0.1\"\n const newUrl = parsed.toString()\n\n // Merge headers, preserving original Host\n const headers = new Headers(init?.headers)\n headers.set(\"Host\", originalHost)\n\n // For some environments, also set X-Forwarded-Host as fallback\n if (!headers.has(\"X-Forwarded-Host\")) {\n headers.set(\"X-Forwarded-Host\", originalHost)\n }\n\n return baseFetch(newUrl, {\n ...init,\n headers,\n })\n }\n\n return baseFetch(input, init)\n }\n}\n\n/**\n * Create HTTP transport for RPC calls\n */\nexport function createHttpTransport(config: TransportConfig) {\n const { endpoint, timeout = 30000 } = config\n const fetchFn = config.fetch\n ? createSmartFetch(config.fetch)\n : createSmartFetch()\n\n return {\n send: async (requestData: any): Promise<TransportPort> => {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n let body: any\n let headers: Record<string, string> = {\n ...(config.apiKey\n ? { Authorization: `Bearer ${config.apiKey}` }\n : {}),\n }\n\n const hasBinaryData = containsBinaryData(requestData)\n\n if (hasBinaryData) {\n const formData = new FormData()\n let binaryIndex = 0\n const processedData = processBinaryData(requestData, (binaryData) => {\n const fieldName = `binary_${binaryIndex++}`\n formData.append(fieldName, binaryData)\n return fieldName\n })\n formData.append(\"json\", JSON.stringify(processedData))\n body = formData\n // Browser will set Content-Type with boundary for FormData\n } else {\n body = JSON.stringify(requestData)\n headers[\"Content-Type\"] = \"application/json\"\n }\n\n const response = await fetchFn(endpoint, {\n method: \"POST\",\n headers,\n body,\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n throw new Error(`HTTP error: ${response.status}`)\n }\n\n const contentType = response.headers.get(\"content-type\")\n let responseData: any\n\n if (contentType && contentType.includes(\"multipart/form-data\")) {\n const formData = await response.formData()\n const jsonData = JSON.parse((formData.get(\"json\") as string) || \"{}\")\n\n if (!jsonData.success) {\n throw new Error(jsonData.error || \"RPC call failed\")\n }\n\n const binaryDataMap: Record<string, any> = {}\n for (const [key, entryValue] of formData.entries()) {\n const value = entryValue as any\n if (key.startsWith(\"binary_\") && value instanceof Blob) {\n const arrayBuffer = await value.arrayBuffer()\n binaryDataMap[key] = {\n data: arrayBuffer,\n type: value.type,\n size: value.size,\n }\n }\n }\n responseData = restoreBinaryData(jsonData.data, binaryDataMap)\n } else {\n const jsonData = await response.json()\n if (!jsonData.success) {\n throw new Error(jsonData.error || \"RPC call failed\")\n }\n responseData = jsonData.data\n }\n\n // Create simulated port for callback compatibility\n const simulatedPort: TransportPort = {\n onmessage: null,\n close: () => {},\n }\n\n // Async callback\n setTimeout(() => {\n if (simulatedPort.onmessage) {\n simulatedPort.onmessage({\n data: { type: \"rpcCallResp\", data: responseData },\n })\n }\n }, 0)\n\n return simulatedPort\n } catch (error) {\n clearTimeout(timeoutId)\n throw error\n }\n },\n close: () => {},\n }\n}\n\n/**\n * Wait for callback from transport port\n */\nexport function onCallBack(port: TransportPort): Promise<any> {\n return new Promise((resolve, reject) => {\n port.onmessage = (event) => {\n port.close()\n const { type, data } = event.data\n if (type === \"rpcCallResp\") {\n resolve(data)\n } else {\n reject(new Error(\"RPC call failed\"))\n }\n }\n })\n}\n","/**\n * Space Proxy for Eidos RPC client\n * Creates a Proxy-based interface for calling RPC methods\n * Extracted and adapted from packages/sandbox/src/sdk-inject-script.html\n */\n\nimport type { TransportConfig } from \"./transport\"\nimport { createHttpTransport, onCallBack } from \"./transport\"\n\n/**\n * Create space proxy with Prisma-style API\n */\nexport function createSpaceProxy(config: TransportConfig) {\n const transport = createHttpTransport(config)\n\n return new Proxy(\n {},\n {\n get: (target, method: string) => {\n // Prisma-style table() API\n if (method === \"table\") {\n return function (tableId: string) {\n const tableMethods = [\n \"create\",\n \"createMany\",\n \"findUnique\",\n \"findFirst\",\n \"findMany\",\n \"count\",\n \"update\",\n \"updateMany\",\n \"delete\",\n \"deleteMany\",\n ]\n\n return new Proxy(\n {},\n {\n get(target, tableMethod: string) {\n if (tableMethods.includes(tableMethod)) {\n return async function (args?: any) {\n const port = await transport.send({\n method: `table(${tableId}).${tableMethod}`,\n params: [args],\n })\n return onCallBack(port)\n }\n }\n return undefined\n },\n }\n )\n }\n }\n\n // Namespace APIs: doc, tree, file, kv, fs, etc.\n const namespaces = [\n \"doc\",\n \"action\",\n \"schema\",\n \"graft\",\n \"script\",\n \"extension\",\n \"tree\",\n \"view\",\n \"column\",\n \"embedding\",\n \"file\",\n \"extNode\",\n \"theme\",\n \"dataView\",\n \"kv\",\n \"fs\",\n \"AI\",\n \"ai\",\n \"utils\",\n ]\n\n if (namespaces.includes(method)) {\n return new Proxy(\n {},\n {\n get(target, subMethod: string) {\n return async function (...params: any[]) {\n const port = await transport.send({\n method: `${method}.${subMethod}`,\n params,\n })\n return onCallBack(port)\n }\n },\n }\n )\n }\n\n // Direct method call\n return async (...params: any[]) => {\n const port = await transport.send({\n method,\n params,\n })\n return onCallBack(port)\n }\n },\n }\n )\n}\n","/**\n * @eidos.space/client\n *\n * Eidos RPC client for Node.js and browser environments.\n * Connect to a headless Eidos server via HTTP.\n *\n * @example\n * ```typescript\n * import { createEidosClient } from '@eidos.space/client'\n *\n * const eidos = createEidosClient({\n * endpoint: 'http://localhost:3000/rpc'\n * })\n *\n * // Query table data\n * const posts = await eidos.currentSpace.table('posts').findMany({\n * where: { published: true },\n * orderBy: { created_at: 'desc' }\n * })\n *\n * // Get document\n * const doc = await eidos.currentSpace.doc.get('doc-id')\n * ```\n */\n\nimport { createSpaceProxy } from \"./space-proxy\"\nimport type { EidosClient, EidosClientConfig } from \"./types\"\nimport type { DataSpace } from \"@eidos.space/core\"\n\n/**\n * Error message for features not available without main thread bridge\n */\nconst NO_BRIDGE_ERROR = (feature: string) =>\n `${feature} is not available without a mainThreadBridge. ` +\n `Please provide a mainThreadBridge in the configuration to enable this feature.`\n\n/**\n * Create an Eidos client for connecting to headless server\n *\n * @example\n * ```typescript\n * // Basic usage (headless mode)\n * const eidos = createEidosClient({\n * endpoint: 'http://localhost:3000/rpc'\n * })\n *\n * // With main thread bridge (for iframe/webview environments)\n * const eidos = createEidosClient({\n * endpoint: 'http://localhost:3000/rpc',\n * mainThreadBridge: {\n * generateText: async (options) => { ... },\n * generateObject: async (options) => { ... },\n * callScript: async (scriptId, ...args) => { ... },\n * fetchBlob: async (url, options) => { ... },\n * tableHighlightRow: (tableId, rowId, fieldId) => { ... }\n * }\n * })\n * ```\n */\nexport function createEidosClient(config: EidosClientConfig): EidosClient {\n const { endpoint, timeout, fetch: fetchFn, mainThreadBridge } = config\n\n const spaceProxy = createSpaceProxy({\n endpoint,\n timeout,\n fetch: fetchFn,\n apiKey: config.apiKey,\n }) as unknown as DataSpace\n\n return {\n currentSpace: spaceProxy,\n space: spaceProxy,\n\n // AI methods - use bridge if available\n AI: {\n generateText: (options) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR(\"AI.generateText\"))\n }\n return mainThreadBridge.generateText(options)\n },\n generateObject: (options) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR(\"AI.generateObject\"))\n }\n return mainThreadBridge.generateObject(options)\n },\n },\n\n // Script methods - use bridge if available\n script: {\n call: (scriptId, ...args) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR(\"script.call\"))\n }\n return mainThreadBridge.callScript(scriptId, ...args)\n },\n },\n\n // Utils methods - use bridge if available\n utils: {\n fetchBlob: (url, options) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR(\"utils.fetchBlob\"))\n }\n return mainThreadBridge.fetchBlob(url, options)\n },\n tableHighlightRow: (tableId, rowId, fieldId) => {\n if (!mainThreadBridge) {\n throw new Error(NO_BRIDGE_ERROR(\"utils.tableHighlightRow\"))\n }\n return mainThreadBridge.tableHighlightRow(tableId, rowId, fieldId)\n },\n },\n }\n}\n\n// Re-export types\nexport type { EidosClient, EidosClientConfig, MainThreadBridge } from \"./types\"\n\n// Re-export DataSpace for convenience\nexport type { DataSpace } from \"@eidos.space/core\"\n\n// Re-export low-level APIs for advanced usage\nexport { createSpaceProxy } from \"./space-proxy\"\nexport { createHttpTransport, onCallBack } from \"./transport\"\nexport type { TransportConfig, TransportPort } from \"./transport\"\n\n// Re-export binary data utilities for server-side usage\nexport {\n containsBinaryData,\n processBinaryDataForResponse,\n restoreBinaryData,\n parseMultipartFormData,\n} from \"./binary-data\"\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eidos.space/client",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Eidos RPC client for Node.js and browser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eidos",
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
"homepage": "https://github.com/mayneyao/eidos/tree/main/packages/client#readme",
|
|
24
24
|
"type": "module",
|
|
25
25
|
"sideEffects": false,
|
|
26
|
-
"main": "
|
|
27
|
-
"module": "
|
|
28
|
-
"types": "
|
|
26
|
+
"main": "src/index.ts",
|
|
27
|
+
"module": "src/index.ts",
|
|
28
|
+
"types": "src/index.ts",
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
31
|
-
"types": "./
|
|
32
|
-
"import": "./
|
|
33
|
-
"default": "./
|
|
31
|
+
"types": "./src/index.ts",
|
|
32
|
+
"import": "./src/index.ts",
|
|
33
|
+
"default": "./src/index.ts"
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
"README.md",
|
|
39
39
|
"LICENSE"
|
|
40
40
|
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsdown",
|
|
43
|
+
"dev": "tsdown --watch",
|
|
44
|
+
"prepublishOnly": "pnpm run build"
|
|
45
|
+
},
|
|
41
46
|
"dependencies": {
|
|
42
47
|
"@eidos.space/core": "^0.28.3"
|
|
43
48
|
},
|
|
@@ -55,10 +60,16 @@
|
|
|
55
60
|
},
|
|
56
61
|
"publishConfig": {
|
|
57
62
|
"access": "public",
|
|
58
|
-
"registry": "https://registry.npmjs.org/"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
+
"registry": "https://registry.npmjs.org/",
|
|
64
|
+
"main": "dist/index.mjs",
|
|
65
|
+
"module": "dist/index.mjs",
|
|
66
|
+
"types": "dist/index.d.mts",
|
|
67
|
+
"exports": {
|
|
68
|
+
".": {
|
|
69
|
+
"types": "./dist/index.d.mts",
|
|
70
|
+
"import": "./dist/index.mjs",
|
|
71
|
+
"default": "./dist/index.mjs"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
63
74
|
}
|
|
64
|
-
}
|
|
75
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @eidos.space/client
|
|
3
|
+
*
|
|
4
|
+
* Eidos RPC client for Node.js and browser environments.
|
|
5
|
+
* Connect to a headless Eidos server via HTTP.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createEidosClient } from '@eidos.space/client'
|
|
10
|
+
*
|
|
11
|
+
* const eidos = createEidosClient({
|
|
12
|
+
* endpoint: 'http://localhost:3000/rpc'
|
|
13
|
+
* })
|
|
14
|
+
*
|
|
15
|
+
* // Query table data
|
|
16
|
+
* const posts = await eidos.currentSpace.table('posts').findMany({
|
|
17
|
+
* where: { published: true },
|
|
18
|
+
* orderBy: { created_at: 'desc' }
|
|
19
|
+
* })
|
|
20
|
+
*
|
|
21
|
+
* // Get document
|
|
22
|
+
* const doc = await eidos.currentSpace.doc.get('doc-id')
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { createSpaceProxy } from "./space-proxy"
|
|
27
|
+
import type { EidosClient, EidosClientConfig } from "./types"
|
|
28
|
+
import type { DataSpace } from "@eidos.space/core"
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Error message for features not available without main thread bridge
|
|
32
|
+
*/
|
|
33
|
+
const NO_BRIDGE_ERROR = (feature: string) =>
|
|
34
|
+
`${feature} is not available without a mainThreadBridge. ` +
|
|
35
|
+
`Please provide a mainThreadBridge in the configuration to enable this feature.`
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create an Eidos client for connecting to headless server
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Basic usage (headless mode)
|
|
43
|
+
* const eidos = createEidosClient({
|
|
44
|
+
* endpoint: 'http://localhost:3000/rpc'
|
|
45
|
+
* })
|
|
46
|
+
*
|
|
47
|
+
* // With main thread bridge (for iframe/webview environments)
|
|
48
|
+
* const eidos = createEidosClient({
|
|
49
|
+
* endpoint: 'http://localhost:3000/rpc',
|
|
50
|
+
* mainThreadBridge: {
|
|
51
|
+
* generateText: async (options) => { ... },
|
|
52
|
+
* generateObject: async (options) => { ... },
|
|
53
|
+
* callScript: async (scriptId, ...args) => { ... },
|
|
54
|
+
* fetchBlob: async (url, options) => { ... },
|
|
55
|
+
* tableHighlightRow: (tableId, rowId, fieldId) => { ... }
|
|
56
|
+
* }
|
|
57
|
+
* })
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function createEidosClient(config: EidosClientConfig): EidosClient {
|
|
61
|
+
const { endpoint, timeout, fetch: fetchFn, mainThreadBridge } = config
|
|
62
|
+
|
|
63
|
+
const spaceProxy = createSpaceProxy({
|
|
64
|
+
endpoint,
|
|
65
|
+
timeout,
|
|
66
|
+
fetch: fetchFn,
|
|
67
|
+
apiKey: config.apiKey,
|
|
68
|
+
}) as unknown as DataSpace
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
currentSpace: spaceProxy,
|
|
72
|
+
space: spaceProxy,
|
|
73
|
+
|
|
74
|
+
// AI methods - use bridge if available
|
|
75
|
+
AI: {
|
|
76
|
+
generateText: (options) => {
|
|
77
|
+
if (!mainThreadBridge) {
|
|
78
|
+
throw new Error(NO_BRIDGE_ERROR("AI.generateText"))
|
|
79
|
+
}
|
|
80
|
+
return mainThreadBridge.generateText(options)
|
|
81
|
+
},
|
|
82
|
+
generateObject: (options) => {
|
|
83
|
+
if (!mainThreadBridge) {
|
|
84
|
+
throw new Error(NO_BRIDGE_ERROR("AI.generateObject"))
|
|
85
|
+
}
|
|
86
|
+
return mainThreadBridge.generateObject(options)
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Script methods - use bridge if available
|
|
91
|
+
script: {
|
|
92
|
+
call: (scriptId, ...args) => {
|
|
93
|
+
if (!mainThreadBridge) {
|
|
94
|
+
throw new Error(NO_BRIDGE_ERROR("script.call"))
|
|
95
|
+
}
|
|
96
|
+
return mainThreadBridge.callScript(scriptId, ...args)
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Utils methods - use bridge if available
|
|
101
|
+
utils: {
|
|
102
|
+
fetchBlob: (url, options) => {
|
|
103
|
+
if (!mainThreadBridge) {
|
|
104
|
+
throw new Error(NO_BRIDGE_ERROR("utils.fetchBlob"))
|
|
105
|
+
}
|
|
106
|
+
return mainThreadBridge.fetchBlob(url, options)
|
|
107
|
+
},
|
|
108
|
+
tableHighlightRow: (tableId, rowId, fieldId) => {
|
|
109
|
+
if (!mainThreadBridge) {
|
|
110
|
+
throw new Error(NO_BRIDGE_ERROR("utils.tableHighlightRow"))
|
|
111
|
+
}
|
|
112
|
+
return mainThreadBridge.tableHighlightRow(tableId, rowId, fieldId)
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Re-export types
|
|
119
|
+
export type { EidosClient, EidosClientConfig, MainThreadBridge } from "./types"
|
|
120
|
+
|
|
121
|
+
// Re-export DataSpace for convenience
|
|
122
|
+
export type { DataSpace } from "@eidos.space/core"
|
|
123
|
+
|
|
124
|
+
// Re-export low-level APIs for advanced usage
|
|
125
|
+
export { createSpaceProxy } from "./space-proxy"
|
|
126
|
+
export { createHttpTransport, onCallBack } from "./transport"
|
|
127
|
+
export type { TransportConfig, TransportPort } from "./transport"
|
|
128
|
+
|
|
129
|
+
// Re-export binary data utilities for server-side usage
|
|
130
|
+
export {
|
|
131
|
+
containsBinaryData,
|
|
132
|
+
processBinaryDataForResponse,
|
|
133
|
+
restoreBinaryData,
|
|
134
|
+
parseMultipartFormData,
|
|
135
|
+
} from "./binary-data"
|