@finalai/agent-provider 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.

Potentially problematic release.


This version of @finalai/agent-provider might be problematic. Click here for more details.

Files changed (176) hide show
  1. package/README.md +12 -0
  2. package/dist/modules/channel-context.d.mts +102 -0
  3. package/dist/modules/channel-context.mjs +52 -0
  4. package/dist/modules/channel-context.mjs.map +7 -0
  5. package/dist/modules/channel-socket.d.mts +65 -0
  6. package/dist/modules/channel-socket.mjs +188 -0
  7. package/dist/modules/channel-socket.mjs.map +7 -0
  8. package/dist/modules/channel.d.mts +382 -0
  9. package/dist/modules/channel.mjs +146 -0
  10. package/dist/modules/channel.mjs.map +7 -0
  11. package/dist/modules/conformance/agent.d.mts +20 -0
  12. package/dist/modules/conformance/agent.mjs +623 -0
  13. package/dist/modules/conformance/agent.mjs.map +7 -0
  14. package/dist/modules/conformance/fixtures.d.mts +41 -0
  15. package/dist/modules/conformance/fixtures.mjs +190 -0
  16. package/dist/modules/conformance/fixtures.mjs.map +7 -0
  17. package/dist/modules/conformance/harness-kit.d.mts +82 -0
  18. package/dist/modules/conformance/harness-kit.mjs +1303 -0
  19. package/dist/modules/conformance/harness-kit.mjs.map +7 -0
  20. package/dist/modules/conformance/image.d.mts +2 -0
  21. package/dist/modules/conformance/image.mjs +79 -0
  22. package/dist/modules/conformance/image.mjs.map +7 -0
  23. package/dist/modules/conformance/index.d.mts +8 -0
  24. package/dist/modules/conformance/index.mjs +12 -0
  25. package/dist/modules/conformance/index.mjs.map +7 -0
  26. package/dist/modules/conformance/local-harness.d.mts +25 -0
  27. package/dist/modules/conformance/local-harness.mjs +108 -0
  28. package/dist/modules/conformance/local-harness.mjs.map +7 -0
  29. package/dist/modules/conformance/protocol.d.mts +30 -0
  30. package/dist/modules/conformance/protocol.mjs +56 -0
  31. package/dist/modules/conformance/protocol.mjs.map +7 -0
  32. package/dist/modules/conformance/sandbox-kit.d.mts +19 -0
  33. package/dist/modules/conformance/sandbox-kit.mjs +320 -0
  34. package/dist/modules/conformance/sandbox-kit.mjs.map +7 -0
  35. package/dist/modules/conformance/scripted-model/chat-completions.d.mts +30 -0
  36. package/dist/modules/conformance/scripted-model/chat-completions.mjs +125 -0
  37. package/dist/modules/conformance/scripted-model/chat-completions.mjs.map +7 -0
  38. package/dist/modules/conformance/scripted-model/directives.d.mts +84 -0
  39. package/dist/modules/conformance/scripted-model/directives.mjs +304 -0
  40. package/dist/modules/conformance/scripted-model/directives.mjs.map +7 -0
  41. package/dist/modules/conformance/scripted-model/handler.d.mts +14 -0
  42. package/dist/modules/conformance/scripted-model/handler.mjs +133 -0
  43. package/dist/modules/conformance/scripted-model/handler.mjs.map +7 -0
  44. package/dist/modules/conformance/scripted-model/index.d.mts +6 -0
  45. package/dist/modules/conformance/scripted-model/index.mjs +7 -0
  46. package/dist/modules/conformance/scripted-model/index.mjs.map +7 -0
  47. package/dist/modules/conformance/scripted-model/messages.d.mts +39 -0
  48. package/dist/modules/conformance/scripted-model/messages.mjs +139 -0
  49. package/dist/modules/conformance/scripted-model/messages.mjs.map +7 -0
  50. package/dist/modules/conformance/scripted-model/responses.d.mts +62 -0
  51. package/dist/modules/conformance/scripted-model/responses.mjs +273 -0
  52. package/dist/modules/conformance/scripted-model/responses.mjs.map +7 -0
  53. package/dist/modules/conformance/scripted-model/sse.d.mts +15 -0
  54. package/dist/modules/conformance/scripted-model/sse.mjs +75 -0
  55. package/dist/modules/conformance/scripted-model/sse.mjs.map +7 -0
  56. package/dist/modules/conformance/v1-agent.d.mts +24 -0
  57. package/dist/modules/conformance/v1-agent.mjs +40 -0
  58. package/dist/modules/conformance/v1-agent.mjs.map +7 -0
  59. package/dist/modules/credential-routes.d.mts +24 -0
  60. package/dist/modules/credential-routes.mjs +25 -0
  61. package/dist/modules/credential-routes.mjs.map +7 -0
  62. package/dist/modules/credentials.d.mts +206 -0
  63. package/dist/modules/credentials.mjs +207 -0
  64. package/dist/modules/credentials.mjs.map +7 -0
  65. package/dist/modules/dynamic.d.mts +32 -0
  66. package/dist/modules/dynamic.mjs +77 -0
  67. package/dist/modules/dynamic.mjs.map +7 -0
  68. package/dist/modules/harness.d.mts +397 -0
  69. package/dist/modules/harness.mjs +213 -0
  70. package/dist/modules/harness.mjs.map +7 -0
  71. package/dist/modules/index.d.mts +15 -0
  72. package/dist/modules/index.mjs +40 -0
  73. package/dist/modules/index.mjs.map +7 -0
  74. package/dist/modules/json.d.mts +12 -0
  75. package/dist/modules/json.mjs +19 -0
  76. package/dist/modules/json.mjs.map +7 -0
  77. package/dist/modules/local-channel-socket.d.mts +23 -0
  78. package/dist/modules/local-channel-socket.mjs +141 -0
  79. package/dist/modules/local-channel-socket.mjs.map +7 -0
  80. package/dist/modules/manifest.d.mts +558 -0
  81. package/dist/modules/manifest.mjs +266 -0
  82. package/dist/modules/manifest.mjs.map +7 -0
  83. package/dist/modules/mcp-content.d.mts +21 -0
  84. package/dist/modules/mcp-content.mjs +86 -0
  85. package/dist/modules/mcp-content.mjs.map +7 -0
  86. package/dist/modules/provider-ref.d.mts +29 -0
  87. package/dist/modules/provider-ref.mjs +55 -0
  88. package/dist/modules/provider-ref.mjs.map +7 -0
  89. package/dist/modules/sandbox/bytes.d.mts +9 -0
  90. package/dist/modules/sandbox/bytes.mjs +154 -0
  91. package/dist/modules/sandbox/bytes.mjs.map +7 -0
  92. package/dist/modules/sandbox/client.d.mts +51 -0
  93. package/dist/modules/sandbox/client.mjs +500 -0
  94. package/dist/modules/sandbox/client.mjs.map +7 -0
  95. package/dist/modules/sandbox/confined-files.d.mts +22 -0
  96. package/dist/modules/sandbox/confined-files.mjs +142 -0
  97. package/dist/modules/sandbox/confined-files.mjs.map +7 -0
  98. package/dist/modules/sandbox/convenience.d.mts +10 -0
  99. package/dist/modules/sandbox/convenience.mjs +116 -0
  100. package/dist/modules/sandbox/convenience.mjs.map +7 -0
  101. package/dist/modules/sandbox/editor.d.mts +49 -0
  102. package/dist/modules/sandbox/editor.mjs +43 -0
  103. package/dist/modules/sandbox/editor.mjs.map +7 -0
  104. package/dist/modules/sandbox/frame-channel.d.mts +47 -0
  105. package/dist/modules/sandbox/frame-channel.mjs +153 -0
  106. package/dist/modules/sandbox/frame-channel.mjs.map +7 -0
  107. package/dist/modules/sandbox/index.d.mts +18 -0
  108. package/dist/modules/sandbox/index.mjs +70 -0
  109. package/dist/modules/sandbox/index.mjs.map +7 -0
  110. package/dist/modules/sandbox/mcp-channel.d.mts +9 -0
  111. package/dist/modules/sandbox/mcp-channel.mjs +77 -0
  112. package/dist/modules/sandbox/mcp-channel.mjs.map +7 -0
  113. package/dist/modules/sandbox/mcp-hosted.d.mts +46 -0
  114. package/dist/modules/sandbox/mcp-hosted.mjs +221 -0
  115. package/dist/modules/sandbox/mcp-hosted.mjs.map +7 -0
  116. package/dist/modules/sandbox/mcp-search.d.mts +51 -0
  117. package/dist/modules/sandbox/mcp-search.mjs +129 -0
  118. package/dist/modules/sandbox/mcp-search.mjs.map +7 -0
  119. package/dist/modules/sandbox/mcp-shell.d.mts +21 -0
  120. package/dist/modules/sandbox/mcp-shell.mjs +205 -0
  121. package/dist/modules/sandbox/mcp-shell.mjs.map +7 -0
  122. package/dist/modules/sandbox/mcp-testing.d.mts +11 -0
  123. package/dist/modules/sandbox/mcp-testing.mjs +33 -0
  124. package/dist/modules/sandbox/mcp-testing.mjs.map +7 -0
  125. package/dist/modules/sandbox/mcp.d.mts +83 -0
  126. package/dist/modules/sandbox/mcp.mjs +971 -0
  127. package/dist/modules/sandbox/mcp.mjs.map +7 -0
  128. package/dist/modules/sandbox/memory-sandbox.d.mts +32 -0
  129. package/dist/modules/sandbox/memory-sandbox.mjs +416 -0
  130. package/dist/modules/sandbox/memory-sandbox.mjs.map +7 -0
  131. package/dist/modules/sandbox/memory-shell.d.mts +29 -0
  132. package/dist/modules/sandbox/memory-shell.mjs +278 -0
  133. package/dist/modules/sandbox/memory-shell.mjs.map +7 -0
  134. package/dist/modules/sandbox/open-scope-node.d.mts +4 -0
  135. package/dist/modules/sandbox/open-scope-node.mjs +19 -0
  136. package/dist/modules/sandbox/open-scope-node.mjs.map +7 -0
  137. package/dist/modules/sandbox/open-scope.d.mts +16 -0
  138. package/dist/modules/sandbox/open-scope.mjs +66 -0
  139. package/dist/modules/sandbox/open-scope.mjs.map +7 -0
  140. package/dist/modules/sandbox/paths.d.mts +7 -0
  141. package/dist/modules/sandbox/paths.mjs +50 -0
  142. package/dist/modules/sandbox/paths.mjs.map +7 -0
  143. package/dist/modules/sandbox/rpc-peer.d.mts +35 -0
  144. package/dist/modules/sandbox/rpc-peer.mjs +224 -0
  145. package/dist/modules/sandbox/rpc-peer.mjs.map +7 -0
  146. package/dist/modules/sandbox/seeds.d.mts +26 -0
  147. package/dist/modules/sandbox/seeds.mjs +228 -0
  148. package/dist/modules/sandbox/seeds.mjs.map +7 -0
  149. package/dist/modules/sandbox/server.d.mts +66 -0
  150. package/dist/modules/sandbox/server.mjs +567 -0
  151. package/dist/modules/sandbox/server.mjs.map +7 -0
  152. package/dist/modules/sandbox/snapshots.d.mts +49 -0
  153. package/dist/modules/sandbox/snapshots.mjs +55 -0
  154. package/dist/modules/sandbox/snapshots.mjs.map +7 -0
  155. package/dist/modules/sandbox/stream-mux.d.mts +18 -0
  156. package/dist/modules/sandbox/stream-mux.mjs +285 -0
  157. package/dist/modules/sandbox/stream-mux.mjs.map +7 -0
  158. package/dist/modules/sandbox/testing.d.mts +25 -0
  159. package/dist/modules/sandbox/testing.mjs +64 -0
  160. package/dist/modules/sandbox/testing.mjs.map +7 -0
  161. package/dist/modules/sandbox/types.d.mts +352 -0
  162. package/dist/modules/sandbox/types.mjs +86 -0
  163. package/dist/modules/sandbox/types.mjs.map +7 -0
  164. package/dist/modules/sandbox/wire.d.mts +671 -0
  165. package/dist/modules/sandbox/wire.mjs +537 -0
  166. package/dist/modules/sandbox/wire.mjs.map +7 -0
  167. package/dist/modules/sandbox/workspace-root.d.mts +8 -0
  168. package/dist/modules/sandbox/workspace-root.mjs +79 -0
  169. package/dist/modules/sandbox/workspace-root.mjs.map +7 -0
  170. package/dist/modules/schemas.d.mts +121 -0
  171. package/dist/modules/schemas.mjs +146 -0
  172. package/dist/modules/schemas.mjs.map +7 -0
  173. package/dist/modules/semantic-events.d.mts +31 -0
  174. package/dist/modules/semantic-events.mjs +23 -0
  175. package/dist/modules/semantic-events.mjs.map +7 -0
  176. package/package.json +199 -0
@@ -0,0 +1,142 @@
1
+ import * as z from "zod";
2
+ import { concatBytes, readAllBytes } from "./bytes.mjs";
3
+ import { dirEntrySchema, statSchema } from "./wire.mjs";
4
+ const CONFINED_FILES_SCRIPT = `import json, os, stat, sys, datetime
5
+ p = json.loads(sys.argv[1])
6
+ root = os.path.normpath(p['root'])
7
+ path = os.path.normpath(p['path'])
8
+ if not root.startswith('/') or not path.startswith('/') or os.path.commonpath([root, path]) != root:
9
+ raise ValueError('path outside workspace')
10
+ fd = os.open('/', os.O_RDONLY | os.O_DIRECTORY)
11
+ try:
12
+ parts = [x for x in path.split('/') if x]
13
+ for i, part in enumerate(parts):
14
+ flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK
15
+ if i < len(parts) - 1 or p['operation'] == 'directory':
16
+ flags |= os.O_DIRECTORY
17
+ next_fd = os.open(part, flags, dir_fd=fd)
18
+ os.close(fd)
19
+ fd = next_fd
20
+ info = os.fstat(fd)
21
+ def kind(s):
22
+ return 'directory' if stat.S_ISDIR(s.st_mode) else 'file' if stat.S_ISREG(s.st_mode) else 'symlink' if stat.S_ISLNK(s.st_mode) else 'other'
23
+ if p['operation'] == 'stat':
24
+ print(json.dumps({'path': path, 'type': kind(info), 'size': info.st_size, 'modifiedAt': datetime.datetime.fromtimestamp(info.st_mtime, datetime.timezone.utc).isoformat(), 'mode': stat.S_IMODE(info.st_mode)}))
25
+ elif p['operation'] == 'directory':
26
+ entries = []
27
+ for name in os.listdir(fd):
28
+ try:
29
+ s = os.stat(name, dir_fd=fd, follow_symlinks=False)
30
+ except FileNotFoundError:
31
+ continue
32
+ entries.append({'name': name, 'path': os.path.join(path, name), 'type': kind(s), 'size': s.st_size})
33
+ print(json.dumps(entries))
34
+ else:
35
+ if not stat.S_ISREG(info.st_mode):
36
+ raise ValueError('not a regular file')
37
+ sys.stdout.buffer.write(b'OK\\n')
38
+ sys.stdout.buffer.flush()
39
+ while True:
40
+ chunk = os.read(fd, 65536)
41
+ if not chunk:
42
+ break
43
+ sys.stdout.buffer.write(chunk)
44
+ except FileNotFoundError:
45
+ if p['operation'] == 'read':
46
+ sys.stdout.buffer.write(b'MISSING\\n')
47
+ else:
48
+ print('null')
49
+ finally:
50
+ os.close(fd)
51
+ `;
52
+ const invocation = (options, operation) => ({
53
+ command: "python3",
54
+ args: [
55
+ "-c",
56
+ CONFINED_FILES_SCRIPT,
57
+ JSON.stringify({ root: options.root, path: options.path, operation })
58
+ ],
59
+ abortSignal: options.abortSignal
60
+ });
61
+ const confinedStat = async (sandbox, options) => {
62
+ const result = await sandbox.run(invocation(options, "stat"));
63
+ if (result.exitCode !== 0) throw new Error("Confined file stat failed");
64
+ return statSchema.nullable().parse(JSON.parse(result.stdout));
65
+ };
66
+ const confinedReadDir = async (sandbox, options) => {
67
+ const result = await sandbox.run(invocation(options, "directory"));
68
+ if (result.exitCode !== 0) throw new Error("Confined directory read failed");
69
+ return z.array(dirEntrySchema).parse(JSON.parse(result.stdout));
70
+ };
71
+ const confinedReadFile = async (sandbox, options) => {
72
+ const process = await sandbox.spawn(invocation(options, "read"));
73
+ const stderr = readAllBytes(process.stderr);
74
+ void stderr.catch(() => void 0);
75
+ const reader = process.stdout.getReader();
76
+ let buffered = new Uint8Array(0);
77
+ let newline = -1;
78
+ try {
79
+ while (newline === -1) {
80
+ const next = await reader.read();
81
+ if (next.done) throw new Error("Confined file read failed");
82
+ buffered = concatBytes([buffered, next.value]);
83
+ newline = buffered.indexOf(10);
84
+ if (newline === -1 && buffered.length > 128) throw new Error("Invalid confined file header");
85
+ }
86
+ const header = new TextDecoder().decode(buffered.subarray(0, newline));
87
+ if (header === "MISSING") {
88
+ await reader.cancel();
89
+ await process.wait();
90
+ await stderr;
91
+ return null;
92
+ }
93
+ if (header !== "OK") throw new Error("Invalid confined file header");
94
+ } catch (cause) {
95
+ await reader.cancel().catch(() => void 0);
96
+ await process.kill().catch(() => void 0);
97
+ await stderr.catch(() => void 0);
98
+ throw cause;
99
+ }
100
+ const first = buffered.subarray(newline + 1);
101
+ return new ReadableStream({
102
+ start: (controller) => {
103
+ if (first.length > 0) controller.enqueue(first);
104
+ },
105
+ pull: async (controller) => {
106
+ try {
107
+ const next = await reader.read();
108
+ if (!next.done) {
109
+ controller.enqueue(next.value);
110
+ return;
111
+ }
112
+ const result = await process.wait();
113
+ await stderr;
114
+ if (result.exitCode !== 0) controller.error(new Error("Confined file read failed"));
115
+ else controller.close();
116
+ } catch (cause) {
117
+ await reader.cancel().catch(() => void 0);
118
+ await process.kill().catch(() => void 0);
119
+ await stderr.catch(() => void 0);
120
+ controller.error(cause);
121
+ }
122
+ },
123
+ cancel: async () => {
124
+ await reader.cancel().catch(() => void 0);
125
+ await process.kill().catch(() => void 0);
126
+ await stderr.catch(() => void 0);
127
+ }
128
+ });
129
+ };
130
+ const createConfinedFileReader = (sandbox) => ({
131
+ readFile: (options) => confinedReadFile(sandbox, options),
132
+ stat: (options) => confinedStat(sandbox, options),
133
+ readDir: (options) => confinedReadDir(sandbox, options)
134
+ });
135
+ export {
136
+ CONFINED_FILES_SCRIPT,
137
+ confinedReadDir,
138
+ confinedReadFile,
139
+ confinedStat,
140
+ createConfinedFileReader
141
+ };
142
+ //# sourceMappingURL=confined-files.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/confined-files.ts"],
4
+ "sourcesContent": ["import * as z from 'zod';\nimport { concatBytes, readAllBytes } from \"./bytes.mjs\";\nimport type { AbortableIface, ConfinedSandboxFilesIface, SandboxSessionIface } from \"./types.mjs\";\nimport { dirEntrySchema, statSchema } from \"./wire.mjs\";\n\n// Pin each ancestor with openat(O_NOFOLLOW). A concurrent rename cannot redirect\n// a read outside the opened directory. Missing Python support fails closed.\nexport const CONFINED_FILES_SCRIPT = `import json, os, stat, sys, datetime\np = json.loads(sys.argv[1])\nroot = os.path.normpath(p['root'])\npath = os.path.normpath(p['path'])\nif not root.startswith('/') or not path.startswith('/') or os.path.commonpath([root, path]) != root:\n raise ValueError('path outside workspace')\nfd = os.open('/', os.O_RDONLY | os.O_DIRECTORY)\ntry:\n parts = [x for x in path.split('/') if x]\n for i, part in enumerate(parts):\n flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK\n if i < len(parts) - 1 or p['operation'] == 'directory':\n flags |= os.O_DIRECTORY\n next_fd = os.open(part, flags, dir_fd=fd)\n os.close(fd)\n fd = next_fd\n info = os.fstat(fd)\n def kind(s):\n return 'directory' if stat.S_ISDIR(s.st_mode) else 'file' if stat.S_ISREG(s.st_mode) else 'symlink' if stat.S_ISLNK(s.st_mode) else 'other'\n if p['operation'] == 'stat':\n print(json.dumps({'path': path, 'type': kind(info), 'size': info.st_size, 'modifiedAt': datetime.datetime.fromtimestamp(info.st_mtime, datetime.timezone.utc).isoformat(), 'mode': stat.S_IMODE(info.st_mode)}))\n elif p['operation'] == 'directory':\n entries = []\n for name in os.listdir(fd):\n try:\n s = os.stat(name, dir_fd=fd, follow_symlinks=False)\n except FileNotFoundError:\n continue\n entries.append({'name': name, 'path': os.path.join(path, name), 'type': kind(s), 'size': s.st_size})\n print(json.dumps(entries))\n else:\n if not stat.S_ISREG(info.st_mode):\n raise ValueError('not a regular file')\n sys.stdout.buffer.write(b'OK\\\\n')\n sys.stdout.buffer.flush()\n while True:\n chunk = os.read(fd, 65536)\n if not chunk:\n break\n sys.stdout.buffer.write(chunk)\nexcept FileNotFoundError:\n if p['operation'] == 'read':\n sys.stdout.buffer.write(b'MISSING\\\\n')\n else:\n print('null')\nfinally:\n os.close(fd)\n`;\n\ninterface ConfinedPathIface extends AbortableIface {\n path: string;\n root: string;\n}\n\nconst invocation = (options: ConfinedPathIface, operation: string) => ({\n command: 'python3',\n args: [\n '-c',\n CONFINED_FILES_SCRIPT,\n JSON.stringify({ root: options.root, path: options.path, operation }),\n ],\n abortSignal: options.abortSignal,\n});\n\nexport const confinedStat = async (\n sandbox: Pick<SandboxSessionIface, 'run' | 'spawn'>,\n options: ConfinedPathIface,\n) => {\n const result = await sandbox.run(invocation(options, 'stat'));\n if (result.exitCode !== 0) throw new Error('Confined file stat failed');\n return statSchema.nullable().parse(JSON.parse(result.stdout));\n};\n\nexport const confinedReadDir = async (\n sandbox: Pick<SandboxSessionIface, 'run' | 'spawn'>,\n options: ConfinedPathIface,\n) => {\n const result = await sandbox.run(invocation(options, 'directory'));\n if (result.exitCode !== 0) throw new Error('Confined directory read failed');\n return z.array(dirEntrySchema).parse(JSON.parse(result.stdout));\n};\n\nexport const confinedReadFile = async (\n sandbox: Pick<SandboxSessionIface, 'run' | 'spawn'>,\n options: ConfinedPathIface,\n) => {\n const process = await sandbox.spawn(invocation(options, 'read'));\n const stderr = readAllBytes(process.stderr);\n void stderr.catch(() => undefined);\n const reader = process.stdout.getReader();\n let buffered: Uint8Array = new Uint8Array(0);\n let newline = -1;\n try {\n while (newline === -1) {\n const next = await reader.read();\n if (next.done) throw new Error('Confined file read failed');\n buffered = concatBytes([buffered, next.value]);\n newline = buffered.indexOf(10);\n if (newline === -1 && buffered.length > 128) throw new Error('Invalid confined file header');\n }\n const header = new TextDecoder().decode(buffered.subarray(0, newline));\n if (header === 'MISSING') {\n await reader.cancel();\n await process.wait();\n await stderr;\n return null;\n }\n if (header !== 'OK') throw new Error('Invalid confined file header');\n } catch (cause) {\n await reader.cancel().catch(() => undefined);\n await process.kill().catch(() => undefined);\n await stderr.catch(() => undefined);\n throw cause;\n }\n const first = buffered.subarray(newline + 1);\n return new ReadableStream<Uint8Array>({\n start: (controller) => {\n if (first.length > 0) controller.enqueue(first);\n },\n pull: async (controller) => {\n try {\n const next = await reader.read();\n if (!next.done) {\n controller.enqueue(next.value);\n return;\n }\n const result = await process.wait();\n await stderr;\n if (result.exitCode !== 0) controller.error(new Error('Confined file read failed'));\n else controller.close();\n } catch (cause) {\n await reader.cancel().catch(() => undefined);\n await process.kill().catch(() => undefined);\n await stderr.catch(() => undefined);\n controller.error(cause);\n }\n },\n cancel: async () => {\n await reader.cancel().catch(() => undefined);\n await process.kill().catch(() => undefined);\n await stderr.catch(() => undefined);\n },\n });\n};\n\n// Bind the fixed read scripts before narrowing the public process capability.\nexport const createConfinedFileReader = (\n sandbox: Pick<SandboxSessionIface, 'run' | 'spawn'>,\n): ConfinedSandboxFilesIface => ({\n readFile: (options) => confinedReadFile(sandbox, options),\n stat: (options) => confinedStat(sandbox, options),\n readDir: (options) => confinedReadDir(sandbox, options),\n});\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ import { type SandboxProcessOptionsIface, type SandboxRunResultIface, type SandboxSessionIface } from "./types.mjs";
2
+ export declare const streamToBytes: (stream: ReadableStream<Uint8Array>) => Promise<Uint8Array>;
3
+ export declare const streamToString: (stream: ReadableStream<Uint8Array>, encoding?: string) => Promise<string>;
4
+ export declare const stringToStream: (text: string) => ReadableStream<Uint8Array>;
5
+ export declare const runCollect: (sandbox: Pick<SandboxSessionIface, "spawn">, options: SandboxProcessOptionsIface) => Promise<SandboxRunResultIface>;
6
+ export declare const readTextFileStream: (stream: ReadableStream<Uint8Array>, options?: Omit<Parameters<SandboxSessionIface["readTextFile"]>[0], "path">) => Promise<string>;
7
+ export declare const readAllText: (sandbox: Pick<SandboxSessionIface, "readFile">, path: string) => Promise<string | null>;
8
+ export declare const writeText: (sandbox: Pick<SandboxSessionIface, "writeFile">, path: string, text: string) => Promise<void>;
9
+ export declare const exists: (sandbox: Pick<SandboxSessionIface, "stat">, path: string) => Promise<boolean>;
10
+ export declare const ensureDir: (sandbox: Pick<SandboxSessionIface, "stat" | "run">, path: string) => Promise<void>;
@@ -0,0 +1,116 @@
1
+ import { bytesToStream, decodeSandboxText, readAllBytes, utf8Decode, utf8Encode } from "./bytes.mjs";
2
+ import {
3
+ SandboxEntryType
4
+ } from "./types.mjs";
5
+ const streamToBytes = (stream) => readAllBytes(stream);
6
+ const streamToString = async (stream, encoding = "utf-8") => {
7
+ const bytes = await readAllBytes(stream);
8
+ return encoding === "utf-8" || encoding === "utf8" ? utf8Decode(bytes) : new TextDecoder(encoding).decode(bytes);
9
+ };
10
+ const stringToStream = (text) => bytesToStream(utf8Encode(text));
11
+ const runCollect = async (sandbox, options) => {
12
+ const process = await sandbox.spawn({ ...options, stdin: options.stdin ?? "" });
13
+ const output = Promise.all([
14
+ streamToString(process.stdout),
15
+ streamToString(process.stderr),
16
+ process.wait()
17
+ ]);
18
+ void output.catch(() => void 0);
19
+ try {
20
+ const [stdout, stderr, result] = await output;
21
+ return { exitCode: result.exitCode, stdout, stderr };
22
+ } catch (cause) {
23
+ await process.kill().catch(() => void 0);
24
+ throw cause;
25
+ }
26
+ };
27
+ const readTextFileStream = async (stream, options = {}) => {
28
+ if (options.abortSignal?.aborted === true) {
29
+ await stream.cancel().catch(() => void 0);
30
+ options.abortSignal.throwIfAborted();
31
+ }
32
+ const encoding = options.encoding ?? "utf-8";
33
+ const name = encoding.toLowerCase().replaceAll("-", "");
34
+ if (["base64", "base64url", "hex", "ascii", "latin1", "binary"].includes(name)) {
35
+ const text = decodeSandboxText(await readAllBytes(stream, options.abortSignal), encoding);
36
+ options.abortSignal?.throwIfAborted();
37
+ if (options.startLine == null && options.endLine == null) return text;
38
+ return (text.match(/[^\n]*\n|[^\n]+$/g) ?? []).slice(Math.max(1, options.startLine ?? 1) - 1, options.endLine).join("");
39
+ }
40
+ const decoderName = name === "utf8" ? "utf-8" : name === "ucs2" || name === "utf16le" ? "utf-16le" : name === "utf16be" ? "utf-16be" : encoding;
41
+ let decoder;
42
+ try {
43
+ decoder = new TextDecoder(decoderName);
44
+ } catch (cause) {
45
+ await stream.cancel().catch(() => void 0);
46
+ throw cause;
47
+ }
48
+ const reader = stream.getReader();
49
+ const cancel = () => {
50
+ void reader.cancel().catch(() => void 0);
51
+ };
52
+ options.abortSignal?.addEventListener("abort", cancel, { once: true });
53
+ if (options.abortSignal?.aborted === true) cancel();
54
+ const result = [];
55
+ const start = Math.max(1, options.startLine ?? 1);
56
+ const end = options.endLine;
57
+ let line = 1;
58
+ let partial = "";
59
+ try {
60
+ for (; ; ) {
61
+ const next = await reader.read();
62
+ options.abortSignal?.throwIfAborted();
63
+ const text = next.done ? decoder.decode() : decoder.decode(next.value, { stream: true });
64
+ if (options.startLine == null && end == null) {
65
+ result.push(text);
66
+ if (next.done) return result.join("");
67
+ continue;
68
+ }
69
+ partial += text;
70
+ let newline = partial.indexOf("\n");
71
+ while (newline >= 0) {
72
+ if (line >= start && (end == null || line <= end))
73
+ result.push(partial.slice(0, newline + 1));
74
+ line += 1;
75
+ partial = partial.slice(newline + 1);
76
+ if (end != null && line > end) return result.join("");
77
+ newline = partial.indexOf("\n");
78
+ }
79
+ if (next.done) {
80
+ if (partial.length > 0 && line >= start && (end == null || line <= end))
81
+ result.push(partial);
82
+ return result.join("");
83
+ }
84
+ }
85
+ } finally {
86
+ options.abortSignal?.removeEventListener("abort", cancel);
87
+ await reader.cancel().catch(() => void 0);
88
+ reader.releaseLock();
89
+ }
90
+ };
91
+ const readAllText = async (sandbox, path) => {
92
+ const stream = await sandbox.readFile({ path });
93
+ return stream == null ? null : streamToString(stream);
94
+ };
95
+ const writeText = (sandbox, path, text) => sandbox.writeFile({ path, content: stringToStream(text) });
96
+ const exists = async (sandbox, path) => await sandbox.stat({ path }) != null;
97
+ const ensureDir = async (sandbox, path) => {
98
+ const stat = await sandbox.stat({ path });
99
+ if (stat?.type === SandboxEntryType.Directory) return;
100
+ if (stat != null)
101
+ throw Object.assign(new Error(`${path} exists and is not a directory`), { code: "EEXIST" });
102
+ const result = await sandbox.run({ command: "mkdir", args: ["-p", path] });
103
+ if (result.exitCode !== 0) throw new Error(`mkdir -p ${path} failed: ${result.stderr.trim()}`);
104
+ };
105
+ export {
106
+ ensureDir,
107
+ exists,
108
+ readAllText,
109
+ readTextFileStream,
110
+ runCollect,
111
+ streamToBytes,
112
+ streamToString,
113
+ stringToStream,
114
+ writeText
115
+ };
116
+ //# sourceMappingURL=convenience.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/convenience.ts"],
4
+ "sourcesContent": ["import { bytesToStream, decodeSandboxText, readAllBytes, utf8Decode, utf8Encode } from \"./bytes.mjs\";\nimport {\n SandboxEntryType,\n type SandboxProcessOptionsIface,\n type SandboxRunResultIface,\n type SandboxSessionIface,\n} from \"./types.mjs\";\n\n// Conveniences over the streaming primitives. Everything\n// here is expressed in SandboxSessionIface terms, so it works on any provider.\n\nexport const streamToBytes = (stream: ReadableStream<Uint8Array>): Promise<Uint8Array> =>\n readAllBytes(stream);\n\nexport const streamToString = async (\n stream: ReadableStream<Uint8Array>,\n encoding = 'utf-8',\n): Promise<string> => {\n const bytes = await readAllBytes(stream);\n return encoding === 'utf-8' || encoding === 'utf8'\n ? utf8Decode(bytes)\n : new TextDecoder(encoding).decode(bytes);\n};\n\nexport const stringToStream = (text: string): ReadableStream<Uint8Array> =>\n bytesToStream(utf8Encode(text));\n\n// Spawn, collect both streams and wait: `run` in terms of `spawn`.\nexport const runCollect = async (\n sandbox: Pick<SandboxSessionIface, 'spawn'>,\n options: SandboxProcessOptionsIface,\n): Promise<SandboxRunResultIface> => {\n const process = await sandbox.spawn({ ...options, stdin: options.stdin ?? '' });\n const output = Promise.all([\n streamToString(process.stdout),\n streamToString(process.stderr),\n process.wait(),\n ]);\n void output.catch(() => undefined);\n try {\n const [stdout, stderr, result] = await output;\n return { exitCode: result.exitCode, stdout, stderr };\n } catch (cause) {\n await process.kill().catch(() => undefined);\n throw cause;\n }\n};\n\n// Read line ranges from the stream, retaining their original line endings.\nexport const readTextFileStream = async (\n stream: ReadableStream<Uint8Array>,\n options: Omit<Parameters<SandboxSessionIface['readTextFile']>[0], 'path'> = {},\n): Promise<string> => {\n if (options.abortSignal?.aborted === true) {\n await stream.cancel().catch(() => undefined);\n options.abortSignal.throwIfAborted();\n }\n const encoding = options.encoding ?? 'utf-8';\n const name = encoding.toLowerCase().replaceAll('-', '');\n if (['base64', 'base64url', 'hex', 'ascii', 'latin1', 'binary'].includes(name)) {\n const text = decodeSandboxText(await readAllBytes(stream, options.abortSignal), encoding);\n options.abortSignal?.throwIfAborted();\n if (options.startLine == null && options.endLine == null) return text;\n return (text.match(/[^\\n]*\\n|[^\\n]+$/g) ?? [])\n .slice(Math.max(1, options.startLine ?? 1) - 1, options.endLine)\n .join('');\n }\n const decoderName =\n name === 'utf8'\n ? 'utf-8'\n : name === 'ucs2' || name === 'utf16le'\n ? 'utf-16le'\n : name === 'utf16be'\n ? 'utf-16be'\n : encoding;\n let decoder: TextDecoder;\n try {\n decoder = new TextDecoder(decoderName);\n } catch (cause) {\n await stream.cancel().catch(() => undefined);\n throw cause;\n }\n const reader = stream.getReader();\n const cancel = () => {\n void reader.cancel().catch(() => undefined);\n };\n options.abortSignal?.addEventListener('abort', cancel, { once: true });\n if (options.abortSignal?.aborted === true) cancel();\n const result: string[] = [];\n const start = Math.max(1, options.startLine ?? 1);\n const end = options.endLine;\n let line = 1;\n let partial = '';\n try {\n for (;;) {\n const next = await reader.read();\n options.abortSignal?.throwIfAborted();\n const text = next.done ? decoder.decode() : decoder.decode(next.value, { stream: true });\n if (options.startLine == null && end == null) {\n result.push(text);\n if (next.done) return result.join('');\n continue;\n }\n partial += text;\n let newline = partial.indexOf('\\n');\n while (newline >= 0) {\n if (line >= start && (end == null || line <= end))\n result.push(partial.slice(0, newline + 1));\n line += 1;\n partial = partial.slice(newline + 1);\n if (end != null && line > end) return result.join('');\n newline = partial.indexOf('\\n');\n }\n if (next.done) {\n if (partial.length > 0 && line >= start && (end == null || line <= end))\n result.push(partial);\n return result.join('');\n }\n }\n } finally {\n options.abortSignal?.removeEventListener('abort', cancel);\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n};\n\nexport const readAllText = async (\n sandbox: Pick<SandboxSessionIface, 'readFile'>,\n path: string,\n): Promise<string | null> => {\n const stream = await sandbox.readFile({ path });\n return stream == null ? null : streamToString(stream);\n};\n\nexport const writeText = (\n sandbox: Pick<SandboxSessionIface, 'writeFile'>,\n path: string,\n text: string,\n): Promise<void> => sandbox.writeFile({ path, content: stringToStream(text) });\n\nexport const exists = async (\n sandbox: Pick<SandboxSessionIface, 'stat'>,\n path: string,\n): Promise<boolean> => (await sandbox.stat({ path })) != null;\n\n// Creates a directory and its parents. Uses `mkdir -p`, the only portable way\n// to create an empty directory through the handle (writes create parents).\nexport const ensureDir = async (\n sandbox: Pick<SandboxSessionIface, 'stat' | 'run'>,\n path: string,\n): Promise<void> => {\n const stat = await sandbox.stat({ path });\n if (stat?.type === SandboxEntryType.Directory) return;\n if (stat != null)\n throw Object.assign(new Error(`${path} exists and is not a directory`), { code: 'EEXIST' });\n const result = await sandbox.run({ command: 'mkdir', args: ['-p', path] });\n if (result.exitCode !== 0) throw new Error(`mkdir -p ${path} failed: ${result.stderr.trim()}`);\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,49 @@
1
+ import * as z from 'zod';
2
+ export declare enum SandboxEditorOperation {
3
+ Available = "available",
4
+ Open = "open",
5
+ Refresh = "refresh",
6
+ Close = "close"
7
+ }
8
+ export declare const sandboxEditorRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
9
+ operation: z.ZodLiteral<SandboxEditorOperation.Available>;
10
+ }, z.core.$strip>, z.ZodObject<{
11
+ operation: z.ZodLiteral<SandboxEditorOperation.Open>;
12
+ }, z.core.$strip>, z.ZodObject<{
13
+ operation: z.ZodLiteral<SandboxEditorOperation.Refresh>;
14
+ id: z.ZodString;
15
+ authorizationId: z.ZodString;
16
+ }, z.core.$strip>, z.ZodObject<{
17
+ operation: z.ZodLiteral<SandboxEditorOperation.Close>;
18
+ id: z.ZodString;
19
+ authorizationId: z.ZodString;
20
+ }, z.core.$strip>], "operation">;
21
+ export type SandboxEditorRequestIface = z.infer<typeof sandboxEditorRequestSchema>;
22
+ export declare const sandboxEditorSessionSchema: z.ZodObject<{
23
+ id: z.ZodString;
24
+ authorizationId: z.ZodString;
25
+ workingDirectory: z.ZodString;
26
+ url: z.ZodString;
27
+ expiresAt: z.ZodString;
28
+ authentication: z.ZodObject<{
29
+ url: z.ZodString;
30
+ fields: z.ZodRecord<z.ZodString, z.ZodString>;
31
+ }, z.core.$strip>;
32
+ }, z.core.$strip>;
33
+ export type SandboxEditorSessionIface = z.infer<typeof sandboxEditorSessionSchema>;
34
+ export declare const sandboxEditorResultSchema: z.ZodUnion<readonly [z.ZodObject<{
35
+ id: z.ZodString;
36
+ authorizationId: z.ZodString;
37
+ workingDirectory: z.ZodString;
38
+ url: z.ZodString;
39
+ expiresAt: z.ZodString;
40
+ authentication: z.ZodObject<{
41
+ url: z.ZodString;
42
+ fields: z.ZodRecord<z.ZodString, z.ZodString>;
43
+ }, z.core.$strip>;
44
+ }, z.core.$strip>, z.ZodObject<{
45
+ available: z.ZodBoolean;
46
+ }, z.core.$strip>, z.ZodObject<{
47
+ closed: z.ZodLiteral<true>;
48
+ }, z.core.$strip>]>;
49
+ export type SandboxEditorResultIface = z.infer<typeof sandboxEditorResultSchema>;
@@ -0,0 +1,43 @@
1
+ import * as z from "zod";
2
+ var SandboxEditorOperation = /* @__PURE__ */ ((SandboxEditorOperation2) => {
3
+ SandboxEditorOperation2["Available"] = "available";
4
+ SandboxEditorOperation2["Open"] = "open";
5
+ SandboxEditorOperation2["Refresh"] = "refresh";
6
+ SandboxEditorOperation2["Close"] = "close";
7
+ return SandboxEditorOperation2;
8
+ })(SandboxEditorOperation || {});
9
+ const sandboxEditorRequestSchema = z.discriminatedUnion("operation", [
10
+ z.object({ operation: z.literal("available" /* Available */) }),
11
+ z.object({ operation: z.literal("open" /* Open */) }),
12
+ z.object({
13
+ operation: z.literal("refresh" /* Refresh */),
14
+ id: z.string().min(1).max(256),
15
+ authorizationId: z.string().min(1).max(256)
16
+ }),
17
+ z.object({
18
+ operation: z.literal("close" /* Close */),
19
+ id: z.string().min(1).max(256),
20
+ authorizationId: z.string().min(1).max(256)
21
+ })
22
+ ]);
23
+ const sandboxEditorSessionSchema = z.object({
24
+ id: z.string(),
25
+ authorizationId: z.string(),
26
+ workingDirectory: z.string(),
27
+ url: z.string().url(),
28
+ expiresAt: z.string(),
29
+ /** Post these fields to the authentication URL in the editor's browser frame. */
30
+ authentication: z.object({ url: z.string().url(), fields: z.record(z.string(), z.string()) })
31
+ });
32
+ const sandboxEditorResultSchema = z.union([
33
+ sandboxEditorSessionSchema,
34
+ z.object({ available: z.boolean() }),
35
+ z.object({ closed: z.literal(true) })
36
+ ]);
37
+ export {
38
+ SandboxEditorOperation,
39
+ sandboxEditorRequestSchema,
40
+ sandboxEditorResultSchema,
41
+ sandboxEditorSessionSchema
42
+ };
43
+ //# sourceMappingURL=editor.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/sandbox/editor.ts"],
4
+ "sourcesContent": ["import * as z from 'zod';\n\nexport enum SandboxEditorOperation {\n Available = 'available',\n Open = 'open',\n Refresh = 'refresh',\n Close = 'close',\n}\n\nexport const sandboxEditorRequestSchema = z.discriminatedUnion('operation', [\n z.object({ operation: z.literal(SandboxEditorOperation.Available) }),\n z.object({ operation: z.literal(SandboxEditorOperation.Open) }),\n z.object({\n operation: z.literal(SandboxEditorOperation.Refresh),\n id: z.string().min(1).max(256),\n authorizationId: z.string().min(1).max(256),\n }),\n z.object({\n operation: z.literal(SandboxEditorOperation.Close),\n id: z.string().min(1).max(256),\n authorizationId: z.string().min(1).max(256),\n }),\n]);\nexport type SandboxEditorRequestIface = z.infer<typeof sandboxEditorRequestSchema>;\n\nexport const sandboxEditorSessionSchema = z.object({\n id: z.string(),\n authorizationId: z.string(),\n workingDirectory: z.string(),\n url: z.string().url(),\n expiresAt: z.string(),\n /** Post these fields to the authentication URL in the editor's browser frame. */\n authentication: z.object({ url: z.string().url(), fields: z.record(z.string(), z.string()) }),\n});\nexport type SandboxEditorSessionIface = z.infer<typeof sandboxEditorSessionSchema>;\n\nexport const sandboxEditorResultSchema = z.union([\n sandboxEditorSessionSchema,\n z.object({ available: z.boolean() }),\n z.object({ closed: z.literal(true) }),\n]);\nexport type SandboxEditorResultIface = z.infer<typeof sandboxEditorResultSchema>;\n"],
5
+ "mappings": "AAAA,YAAY,OAAO;AAEZ,IAAK,yBAAL,kBAAKA,4BAAL;AACL,EAAAA,wBAAA,eAAY;AACZ,EAAAA,wBAAA,UAAO;AACP,EAAAA,wBAAA,aAAU;AACV,EAAAA,wBAAA,WAAQ;AAJE,SAAAA;AAAA,GAAA;AAOL,MAAM,6BAA6B,EAAE,mBAAmB,aAAa;AAAA,EAC1E,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,2BAAgC,EAAE,CAAC;AAAA,EACnE,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,iBAA2B,EAAE,CAAC;AAAA,EAC9D,EAAE,OAAO;AAAA,IACP,WAAW,EAAE,QAAQ,uBAA8B;AAAA,IACnD,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,IAC7B,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC5C,CAAC;AAAA,EACD,EAAE,OAAO;AAAA,IACP,WAAW,EAAE,QAAQ,mBAA4B;AAAA,IACjD,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,IAC7B,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC5C,CAAC;AACH,CAAC;AAGM,MAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,IAAI,EAAE,OAAO;AAAA,EACb,iBAAiB,EAAE,OAAO;AAAA,EAC1B,kBAAkB,EAAE,OAAO;AAAA,EAC3B,KAAK,EAAE,OAAO,EAAE,IAAI;AAAA,EACpB,WAAW,EAAE,OAAO;AAAA;AAAA,EAEpB,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC;AAC9F,CAAC;AAGM,MAAM,4BAA4B,EAAE,MAAM;AAAA,EAC/C;AAAA,EACA,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AAAA,EACnC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,CAAC;AACtC,CAAC;",
6
+ "names": ["SandboxEditorOperation"]
7
+ }
@@ -0,0 +1,47 @@
1
+ export type WireFrame = string | Uint8Array;
2
+ export interface FrameChannelCloseIface {
3
+ code: number;
4
+ reason: string;
5
+ }
6
+ export interface FrameChannelHandlersIface {
7
+ frame: (frame: WireFrame) => void;
8
+ close: (info: FrameChannelCloseIface) => void;
9
+ }
10
+ export interface FrameChannelIface {
11
+ send(frame: WireFrame): void;
12
+ close(code?: number, reason?: string): void;
13
+ listen(handlers: FrameChannelHandlersIface): void;
14
+ }
15
+ export type WebSocketMessageData = string | ArrayBuffer | ArrayBufferView | Blob | Uint8Array[];
16
+ export interface WebSocketMessageEventLikeIface {
17
+ readonly data: WebSocketMessageData;
18
+ }
19
+ export interface WebSocketEventLikeIface {
20
+ readonly type: string;
21
+ }
22
+ export interface WebSocketCloseEventLikeIface {
23
+ readonly code: number;
24
+ readonly reason: string;
25
+ }
26
+ export interface WebSocketLikeIface {
27
+ readonly readyState: number;
28
+ binaryType: string;
29
+ send(data: string | Uint8Array<ArrayBuffer>): void;
30
+ close(code?: number, reason?: string): void;
31
+ addEventListener(type: 'message', listener: (event: WebSocketMessageEventLikeIface) => void): void;
32
+ addEventListener(type: 'close', listener: (event: WebSocketCloseEventLikeIface) => void): void;
33
+ addEventListener(type: 'open', listener: (event: WebSocketEventLikeIface) => void): void;
34
+ addEventListener(type: 'error', listener: (event: WebSocketEventLikeIface) => void): void;
35
+ }
36
+ export type WebSocketConstructorIface = new (url: string, protocols?: string | string[]) => WebSocketLikeIface;
37
+ export interface WebSocketFrameChannelIface {
38
+ channel: FrameChannelIface;
39
+ opened: Promise<void>;
40
+ }
41
+ export declare const frameChannelFromWebSocket: (socket: WebSocketLikeIface) => WebSocketFrameChannelIface;
42
+ export interface FrameStreamIface {
43
+ readable: ReadableStream<WireFrame>;
44
+ writable: WritableStream<WireFrame>;
45
+ }
46
+ export declare const frameChannelFromStream: (stream: FrameStreamIface) => FrameChannelIface;
47
+ export declare const createFrameChannelPair: () => [FrameChannelIface, FrameChannelIface];
@@ -0,0 +1,153 @@
1
+ const ABNORMAL_CLOSE = 1006;
2
+ const createBufferedListener = () => {
3
+ const pending = [];
4
+ let closed = null;
5
+ let handlers = null;
6
+ return {
7
+ frame: (frame) => {
8
+ if (handlers == null) pending.push(frame);
9
+ else handlers.frame(frame);
10
+ },
11
+ close: (info) => {
12
+ if (closed != null) return;
13
+ closed = info;
14
+ handlers?.close(info);
15
+ },
16
+ listen: (next) => {
17
+ handlers = next;
18
+ for (const frame of pending.splice(0)) next.frame(frame);
19
+ if (closed != null) next.close(closed);
20
+ },
21
+ isClosed: () => closed != null
22
+ };
23
+ };
24
+ const WS_OPEN = 1;
25
+ const toWireFrame = (data) => {
26
+ if (data instanceof Uint8Array) return data;
27
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
28
+ if (ArrayBuffer.isView(data))
29
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
30
+ if (data instanceof Blob) return null;
31
+ if (Array.isArray(data)) {
32
+ let total = 0;
33
+ for (const part of data) total += part.byteLength;
34
+ const out = new Uint8Array(total);
35
+ let offset = 0;
36
+ for (const part of data) {
37
+ out.set(part, offset);
38
+ offset += part.byteLength;
39
+ }
40
+ return out;
41
+ }
42
+ return data;
43
+ };
44
+ const frameChannelFromWebSocket = (socket) => {
45
+ socket.binaryType = "arraybuffer";
46
+ const listener = createBufferedListener();
47
+ let resolveOpen = () => void 0;
48
+ let rejectOpen = () => void 0;
49
+ const opened = new Promise((resolve, reject) => {
50
+ resolveOpen = resolve;
51
+ rejectOpen = reject;
52
+ });
53
+ opened.catch(() => void 0);
54
+ if (socket.readyState === WS_OPEN) resolveOpen();
55
+ socket.addEventListener("open", () => resolveOpen());
56
+ socket.addEventListener("message", (event) => {
57
+ const frame = toWireFrame(event.data);
58
+ if (frame != null) listener.frame(frame);
59
+ });
60
+ socket.addEventListener("close", (event) => {
61
+ rejectOpen(new Error(`websocket closed before opening (${event.code})`));
62
+ listener.close({ code: event.code, reason: event.reason });
63
+ });
64
+ socket.addEventListener("error", () => {
65
+ rejectOpen(new Error("websocket failed to open"));
66
+ });
67
+ const channel = {
68
+ send: (frame) => {
69
+ if (socket.readyState !== WS_OPEN) return;
70
+ if (!(frame instanceof Uint8Array)) socket.send(frame);
71
+ else if (frame.buffer instanceof ArrayBuffer)
72
+ socket.send(new Uint8Array(frame.buffer, frame.byteOffset, frame.byteLength));
73
+ else socket.send(new Uint8Array(frame));
74
+ },
75
+ close: (code, reason) => {
76
+ try {
77
+ socket.close(code, reason);
78
+ } catch {
79
+ socket.close();
80
+ }
81
+ listener.close({ code: code ?? 1e3, reason: reason ?? "" });
82
+ },
83
+ listen: (handlers) => listener.listen(handlers)
84
+ };
85
+ return { channel, opened };
86
+ };
87
+ const frameChannelFromStream = (stream) => {
88
+ const listener = createBufferedListener();
89
+ const writer = stream.writable.getWriter();
90
+ const reader = stream.readable.getReader();
91
+ const pump = async () => {
92
+ try {
93
+ for (; ; ) {
94
+ const { done, value } = await reader.read();
95
+ if (done) break;
96
+ listener.frame(value);
97
+ }
98
+ listener.close({ code: 1e3, reason: "" });
99
+ } catch (error) {
100
+ listener.close({
101
+ code: ABNORMAL_CLOSE,
102
+ reason: error instanceof Error ? error.message : "stream error"
103
+ });
104
+ }
105
+ };
106
+ void pump();
107
+ return {
108
+ send: (frame) => {
109
+ if (listener.isClosed()) return;
110
+ writer.write(frame).catch(() => listener.close({ code: ABNORMAL_CLOSE, reason: "write failed" }));
111
+ },
112
+ close: (code, reason) => {
113
+ if (listener.isClosed()) return;
114
+ listener.close({ code: code ?? 1e3, reason: reason ?? "" });
115
+ writer.close().catch(() => void 0);
116
+ reader.cancel().catch(() => void 0);
117
+ },
118
+ listen: (handlers) => listener.listen(handlers)
119
+ };
120
+ };
121
+ const createFrameChannelPair = () => {
122
+ const left = createBufferedListener();
123
+ const right = createBufferedListener();
124
+ const deliver = (target, frame) => {
125
+ queueMicrotask(() => target.frame(frame));
126
+ };
127
+ const closeBoth = (info) => {
128
+ queueMicrotask(() => {
129
+ left.close(info);
130
+ right.close(info);
131
+ });
132
+ };
133
+ let closed = false;
134
+ const make = (peer, self) => ({
135
+ send: (frame) => {
136
+ if (closed) return;
137
+ deliver(peer, frame instanceof Uint8Array ? frame.slice() : frame);
138
+ },
139
+ close: (code, reason) => {
140
+ if (closed) return;
141
+ closed = true;
142
+ closeBoth({ code: code ?? 1e3, reason: reason ?? "" });
143
+ },
144
+ listen: (handlers) => self.listen(handlers)
145
+ });
146
+ return [make(right, left), make(left, right)];
147
+ };
148
+ export {
149
+ createFrameChannelPair,
150
+ frameChannelFromStream,
151
+ frameChannelFromWebSocket
152
+ };
153
+ //# sourceMappingURL=frame-channel.mjs.map