@inflector/aura 0.2.6 → 0.2.7

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.
@@ -1,2 +1,12 @@
1
- export declare function createFunctionHandler(workspace: string, functionsFolder?: string): any;
1
+ export type SSEEvent<T = unknown> = {
2
+ event: string;
3
+ data: T;
4
+ raw: string;
5
+ };
6
+ export type SSEHandler<T> = (evt: SSEEvent<T>) => void;
7
+ export type FunctionResult<TJson, TSse> = Promise<TJson> | {
8
+ onMessage(cb: SSEHandler<TSse>): Promise<void>;
9
+ abort(): void;
10
+ };
11
+ export declare function createFunctionHandler<TFunctions extends Record<string, any>>(workspace: string): TFunctions;
2
12
  //# sourceMappingURL=function.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"function.d.ts","sourceRoot":"","sources":["../src/function.ts"],"names":[],"mappings":"AAAA,wBAAgB,qBAAqB,CAAE,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,GAAG,CAmHvF"}
1
+ {"version":3,"file":"function.d.ts","sourceRoot":"","sources":["../src/function.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,CAAC,CAAC,GAAG,OAAO,IAAI;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,CAAC,CAAC;IACR,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAEvD,MAAM,MAAM,cAAc,CAAC,KAAK,EAAE,IAAI,IAClC,OAAO,CAAC,KAAK,CAAC,GACd;IACE,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAqDN,wBAAgB,qBAAqB,CAAC,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1E,SAAS,EAAE,MAAM,cAyDlB"}
package/dist/function.js CHANGED
@@ -1,107 +1,89 @@
1
- export function createFunctionHandler(workspace, functionsFolder) {
2
- const handler = new Proxy({}, {
3
- get(_, prop) {
4
- return createNestedProxy([prop]);
1
+ async function parseSSE(response, onMessage, signal) {
2
+ if (!response.body)
3
+ throw new Error("Missing response body");
4
+ const reader = response.body.getReader();
5
+ const decoder = new TextDecoder();
6
+ let buffer = "";
7
+ while (true) {
8
+ const { value, done } = await reader.read();
9
+ if (done)
10
+ break;
11
+ buffer += decoder.decode(value, { stream: true });
12
+ const chunks = buffer.split("\n\n");
13
+ buffer = chunks.pop() ?? "";
14
+ for (const chunk of chunks) {
15
+ let event = "message";
16
+ const dataLines = [];
17
+ for (const line of chunk.split("\n")) {
18
+ if (line.startsWith("event:")) {
19
+ event = line.slice(6).trim();
20
+ }
21
+ if (line.startsWith("data:")) {
22
+ dataLines.push(line.slice(5).trim());
23
+ }
24
+ }
25
+ if (dataLines.length === 0)
26
+ continue;
27
+ const raw = dataLines.join("\n");
28
+ let parsed = raw;
29
+ try {
30
+ parsed = JSON.parse(raw);
31
+ }
32
+ catch { }
33
+ onMessage({
34
+ event,
35
+ data: parsed,
36
+ raw,
37
+ });
5
38
  }
6
- });
7
- function createNestedProxy(path) {
8
- // Target function is just a placeholder
9
- return new Proxy(() => { }, {
10
- get(_, prop) {
11
- return createNestedProxy([...path, prop]);
12
- },
13
- // 1. Remove 'async' here to prevent native Promise wrapping
14
- apply(_, __, args) {
15
- const url = ["api", "fn", workspace, ...path].join("/");
16
- const isFormData = args != null && args.length === 1 && args[0] instanceof FormData;
17
- const body = isFormData ? args[0] : JSON.stringify(args[0] ?? {});
39
+ }
40
+ }
41
+ export function createFunctionHandler(workspace) {
42
+ return new Proxy({}, {
43
+ get(_, fnName) {
44
+ return (...args) => {
45
+ const controller = new AbortController();
46
+ const url = ["api", "fn", workspace, fnName].join("/");
47
+ const body = args.length === 1 && args[0] instanceof FormData
48
+ ? args[0]
49
+ : JSON.stringify(args[0] ?? {});
18
50
  const headers = {
19
- "Accept": "text/event-stream,application/json",
51
+ Accept: "text/event-stream,application/json",
20
52
  };
21
- if (!isFormData)
53
+ if (!(body instanceof FormData)) {
22
54
  headers["Content-Type"] = "application/json";
23
- // 2. Start the fetch properly (without await)
24
- const requestPromise = fetch(url, {
55
+ }
56
+ const request = fetch(url, {
25
57
  method: "POST",
26
58
  body,
27
59
  headers,
28
- credentials: "include"
60
+ credentials: "include",
61
+ signal: controller.signal,
29
62
  });
30
- // 3. Return a custom "Thenable" object with full Promise interface
31
- const thenable = {
32
- then(onFulfilled, onRejected) {
33
- requestPromise.then(async (response) => {
34
- const contentType = response.headers.get("Content-Type") || "";
35
- // ─────────────────────────────────────────────
36
- // SSE PATH
37
- // ─────────────────────────────────────────────
38
- if (contentType.includes("text/event-stream")) {
39
- if (!response.body) {
40
- if (onRejected)
41
- onRejected(new Error("Response body is null"));
42
- return;
43
- }
44
- const reader = response.body.getReader();
45
- const decoder = new TextDecoder("utf-8");
46
- let buffer = "";
47
- try {
48
- while (true) {
49
- const { value, done } = await reader.read();
50
- if (done)
51
- break;
52
- buffer += decoder.decode(value, { stream: true });
53
- // Split by SSE message delimiter
54
- const parts = buffer.split("\n\n");
55
- buffer = parts.pop() || ""; // Keep incomplete chunk
56
- for (const part of parts) {
57
- // Extract data lines, remove 'data: ' prefix, join with newline
58
- const message = part
59
- .split("\n")
60
- .filter(line => line.trim().startsWith("data: "))
61
- .map(line => line.replace(/^data: /, ""))
62
- .join("\n");
63
- if (message) {
64
- try {
65
- // Emit parsed JSON
66
- onFulfilled(JSON.parse(message));
67
- }
68
- catch {
69
- // Or emit raw string if parsing fails
70
- onFulfilled(message);
71
- }
72
- }
73
- }
74
- }
75
- }
76
- catch (err) {
77
- if (onRejected)
78
- onRejected(err);
79
- }
80
- }
81
- // ─────────────────────────────────────────────
82
- // NORMAL JSON PATH
83
- // ─────────────────────────────────────────────
84
- else {
85
- const data = await response.json();
86
- onFulfilled(data.data ?? data);
87
- }
88
- }).catch((err) => {
89
- if (onRejected)
90
- onRejected(err);
91
- });
92
- // Return 'this' to allow chaining
93
- return thenable;
63
+ return {
64
+ async onMessage(cb) {
65
+ const res = await request;
66
+ const type = res.headers.get("Content-Type") ?? "";
67
+ if (!type.includes("text/event-stream")) {
68
+ throw new Error("Response is not SSE");
69
+ }
70
+ await parseSSE(res, cb, controller.signal);
94
71
  },
95
- catch(onRejected) {
96
- return thenable.then(() => { }, onRejected);
72
+ abort() {
73
+ controller.abort();
74
+ },
75
+ async then(resolve, reject) {
76
+ try {
77
+ const res = await request;
78
+ const json = await res.json();
79
+ resolve(json.data ?? json);
80
+ }
81
+ catch (e) {
82
+ reject?.(e);
83
+ }
97
84
  },
98
- finally(onFinally) {
99
- return thenable.then((value) => { onFinally?.(); return value; }, (err) => { onFinally?.(); throw err; });
100
- }
101
85
  };
102
- return thenable;
103
- },
104
- });
105
- }
106
- return handler;
86
+ };
87
+ },
88
+ });
107
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inflector/aura",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",