@standardagents/code-web 0.14.1-issue.663.fd4c3f → 0.14.4-branch.9e48e914.ee06ef

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.
Files changed (37) hide show
  1. package/README.md +9 -1
  2. package/dist/build.json +1 -1
  3. package/dist/licenses/jco-transpile.txt +220 -0
  4. package/dist/manifest.json +1 -1
  5. package/dist/manifest.sig +1 -1
  6. package/dist/pkg/standard_web.d.ts +44 -6
  7. package/dist/pkg/standard_web.js +292 -20
  8. package/dist/pkg/standard_web_bg.wasm +0 -0
  9. package/dist/pkg/standard_web_bg.wasm.d.ts +11 -6
  10. package/dist/plugins/clock-cells/clock_cells.core.wasm +0 -0
  11. package/dist/plugins/clock-cells/clock_cells.wasm +0 -0
  12. package/dist/plugins/clock-cells/standard-plugin.json +11 -0
  13. package/dist/plugins/host/transpile.js +51 -0
  14. package/dist/plugins/host/ui-jspi.js +14851 -0
  15. package/dist/plugins/host/ui.core2.wasm +0 -0
  16. package/dist/plugins/host/ui.core3.wasm +0 -0
  17. package/dist/plugins/host/ui.js +14675 -0
  18. package/dist/plugins/host/world.js +172 -0
  19. package/dist/plugins/jco/cli.js +141 -0
  20. package/dist/plugins/jco/common.js +16 -0
  21. package/dist/plugins/jco/config.js +7 -0
  22. package/dist/plugins/jco/environment.js +24 -0
  23. package/dist/plugins/jco/filesystem.js +339 -0
  24. package/dist/plugins/jco/in-memory-filesystem.js +620 -0
  25. package/dist/plugins/jco/io.js +366 -0
  26. package/dist/plugins/jco/js-component-bindgen-component.core.wasm +0 -0
  27. package/dist/plugins/jco/js-component-bindgen-component.core2.wasm +0 -0
  28. package/dist/plugins/jco/js-component-bindgen-component.js +14272 -0
  29. package/dist/plugins/jco/opfs-filesystem.js +245 -0
  30. package/dist/plugins/jco/random.js +51 -0
  31. package/dist/plugins/plasma-pixels/plasma_pixels.core.wasm +0 -0
  32. package/dist/plugins/plasma-pixels/plasma_pixels.wasm +0 -0
  33. package/dist/plugins/plasma-pixels/standard-plugin.json +11 -0
  34. package/dist/plugins/runtime.js +1044 -0
  35. package/dist/plugins/samples.json +81 -0
  36. package/dist/plugins/worker.js +411 -0
  37. package/package.json +8 -2
@@ -0,0 +1,245 @@
1
+ import { InMemoryFilesystemAdapter, UNWRAP_DESCRIPTOR, _onTouch } from "./in-memory-filesystem.js";
2
+ // OPFS has no native symlink concept. All symlinks under a root are recorded in a
3
+ // single hidden sidecar file at that root, keyed by their path relative to it, so
4
+ // they survive a flush + reload instead of only living for the lifetime of one
5
+ // capability.
6
+ const SYMLINKS_FILE = ".__wasi_symlinks__.json";
7
+ async function readSymlinksFile(handle) {
8
+ try {
9
+ const fileHandle = await handle.getFileHandle(SYMLINKS_FILE);
10
+ const file = await fileHandle.getFile();
11
+ return JSON.parse(await file.text());
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ async function writeSymlinksFile(handle, symlinks) {
18
+ if (Object.keys(symlinks).length === 0) {
19
+ try {
20
+ await handle.removeEntry(SYMLINKS_FILE);
21
+ }
22
+ catch {
23
+ // no sidecar to remove
24
+ }
25
+ return;
26
+ }
27
+ const fileHandle = await handle.getFileHandle(SYMLINKS_FILE, { create: true });
28
+ const writable = await fileHandle.createWritable();
29
+ await writable.write(JSON.stringify(symlinks));
30
+ await writable.close();
31
+ }
32
+ /** Collect every symlink under `dir` into a flat path -> target map, relative to `dir` itself. */
33
+ function collectSymlinks(dir, prefix, out) {
34
+ for (const [name, entry] of Object.entries(dir)) {
35
+ const path = prefix ? `${prefix}/${name}` : name;
36
+ if (entry.symlink !== undefined) {
37
+ out[path] = entry.symlink;
38
+ }
39
+ else if (entry.dir) {
40
+ collectSymlinks(entry.dir, path, out);
41
+ }
42
+ }
43
+ }
44
+ /** Place each recorded symlink back into the tree at its path, skipping any path whose parent is missing. */
45
+ function injectSymlinks(root, symlinks) {
46
+ for (const [path, target] of Object.entries(symlinks)) {
47
+ const parts = path.split("/").filter(Boolean);
48
+ const name = parts.pop();
49
+ if (!name) {
50
+ continue;
51
+ }
52
+ let dir = root;
53
+ for (const part of parts) {
54
+ dir = dir[part]?.dir;
55
+ if (!dir) {
56
+ break;
57
+ }
58
+ }
59
+ if (dir && !(name in dir)) {
60
+ dir[name] = { symlink: target };
61
+ }
62
+ }
63
+ }
64
+ async function readEntries(handle) {
65
+ const dir = {};
66
+ for await (const [name, child] of handle.entries()) {
67
+ if (name === SYMLINKS_FILE) {
68
+ continue;
69
+ }
70
+ if (child.kind === "directory") {
71
+ dir[name] = { dir: await readEntries(child) };
72
+ }
73
+ else {
74
+ const file = await child.getFile();
75
+ dir[name] = { source: new Uint8Array(await file.arrayBuffer()) };
76
+ }
77
+ }
78
+ return dir;
79
+ }
80
+ async function writeEntries(handle, dir) {
81
+ const seen = new Set(Object.keys(dir));
82
+ seen.add(SYMLINKS_FILE);
83
+ for await (const [name] of handle.entries()) {
84
+ if (!seen.has(name)) {
85
+ await handle.removeEntry(name, { recursive: true });
86
+ }
87
+ }
88
+ for (const [name, entry] of Object.entries(dir)) {
89
+ if (entry.symlink !== undefined) {
90
+ // Recorded separately in the root's symlink sidecar file.
91
+ continue;
92
+ }
93
+ if (entry.dir) {
94
+ await writeEntries(await handle.getDirectoryHandle(name, { create: true }), entry.dir);
95
+ continue;
96
+ }
97
+ const fileHandle = await handle.getFileHandle(name, { create: true });
98
+ const writable = await fileHandle.createWritable();
99
+ const source = entry.source ?? new Uint8Array();
100
+ const bytes = typeof source === "string" ? new TextEncoder().encode(source) : source;
101
+ await writable.write(bytes.slice());
102
+ await writable.close();
103
+ }
104
+ }
105
+ /** Load an OPFS directory into an in-memory tree, ready to hand to `OpfsFilesystemAdapter.getRoot`. */
106
+ export async function loadOpfsCapability(handle) {
107
+ const dir = await readEntries(handle);
108
+ injectSymlinks(dir, await readSymlinksFile(handle));
109
+ return { handle, data: { dir } };
110
+ }
111
+ const LOCK_METHODS = new Set([
112
+ "lockShared",
113
+ "lockExclusive",
114
+ "tryLockShared",
115
+ "tryLockExclusive",
116
+ "unlock",
117
+ ]);
118
+ function lockModeOf(prop) {
119
+ return prop.endsWith("Exclusive") ? "exclusive" : "shared";
120
+ }
121
+ /** Join an OPFS-relative path onto an existing one, for naming a cross-tab lock. */
122
+ function joinLockPath(base, segment) {
123
+ let path = base;
124
+ for (const part of segment.split("/")) {
125
+ if (part === "" || part === ".") {
126
+ continue;
127
+ }
128
+ if (part === "..") {
129
+ path = path.slice(0, path.lastIndexOf("/"));
130
+ continue;
131
+ }
132
+ path = path ? `${path}/${part}` : part;
133
+ }
134
+ return path;
135
+ }
136
+ /**
137
+ * Wrap a descriptor so its advisory locking also makes a best-effort request against
138
+ * `BrowserLockManager`, in addition to the default same-process reader/writer lock.
139
+ * The Web Locks request is fire-and-forget - there's no way to block a synchronous
140
+ * `lockExclusive()` on a cross-tab grant without JSPI, so this only coordinates
141
+ * tabs that are already idle/cooperating.
142
+ */
143
+ function withCrossTabLocking(descriptor, lockName, lockManager) {
144
+ let release = null;
145
+ function requestCrossTabLock(mode) {
146
+ if (!lockManager) {
147
+ return;
148
+ }
149
+ lockManager
150
+ .request(lockName, { mode }, () => new Promise((resolve) => (release = resolve)))
151
+ .catch(() => {
152
+ // best-effort only; local same-process locking already enforced correctness
153
+ });
154
+ }
155
+ function releaseCrossTabLock() {
156
+ release?.();
157
+ release = null;
158
+ }
159
+ return new Proxy(descriptor, {
160
+ get(target, prop, receiver) {
161
+ if (prop === UNWRAP_DESCRIPTOR) {
162
+ return target;
163
+ }
164
+ const value = Reflect.get(target, prop, receiver);
165
+ if (prop === "openAt") {
166
+ return (...args) => {
167
+ const child = Reflect.apply(value, target, args);
168
+ return withCrossTabLocking(child, joinLockPath(lockName, args[1]), lockManager);
169
+ };
170
+ }
171
+ if (typeof prop === "string" && LOCK_METHODS.has(prop) && typeof value === "function") {
172
+ return (...args) => {
173
+ const result = Reflect.apply(value, target, args);
174
+ if (prop === "unlock") {
175
+ releaseCrossTabLock();
176
+ }
177
+ else if (prop.startsWith("tryLock")) {
178
+ if (result) {
179
+ requestCrossTabLock(lockModeOf(prop));
180
+ }
181
+ }
182
+ else {
183
+ requestCrossTabLock(lockModeOf(prop));
184
+ }
185
+ return result;
186
+ };
187
+ }
188
+ return typeof value === "function" ? value.bind(target) : value;
189
+ },
190
+ });
191
+ }
192
+ /**
193
+ * Browser filesystem adapter backed by the Origin Private File System.
194
+ *
195
+ * Every guest-facing `Descriptor` operation - including advisory locking, which
196
+ * comes for free as `InMemoryFilesystemAdapter`'s same-process reader/writer lock -
197
+ * is delegated to `InMemoryFilesystemAdapter` and stays synchronous. Persistence to
198
+ * OPFS happens automatically shortly after any mutation (debounced to a microtask,
199
+ * so a burst of writes only triggers one round-trip); `flush()`/`dispose()` remain
200
+ * available to force it explicitly, e.g. before navigating away.
201
+ *
202
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system
203
+ */
204
+ export class OpfsFilesystemAdapter {
205
+ #inMemory = new InMemoryFilesystemAdapter();
206
+ #roots = [];
207
+ #lockManager;
208
+ #flushScheduled = false;
209
+ #unsubscribeTouch;
210
+ constructor(options = {}) {
211
+ this.#lockManager = options.lockManager;
212
+ this.#unsubscribeTouch = _onTouch(() => this.#scheduleFlush());
213
+ }
214
+ getRoot(capability) {
215
+ this.#roots.push(capability);
216
+ const descriptor = this.#inMemory.getRoot(capability.data);
217
+ return this.#lockManager
218
+ ? withCrossTabLocking(descriptor, capability.handle.name, this.#lockManager)
219
+ : descriptor;
220
+ }
221
+ #scheduleFlush() {
222
+ if (this.#flushScheduled) {
223
+ return;
224
+ }
225
+ this.#flushScheduled = true;
226
+ queueMicrotask(() => {
227
+ this.#flushScheduled = false;
228
+ void this.flush();
229
+ });
230
+ }
231
+ /** Persist every loaded root's current in-memory state back to OPFS. */
232
+ async flush() {
233
+ await Promise.all(this.#roots.map(async (root) => {
234
+ const dir = root.data.dir ?? {};
235
+ await writeEntries(root.handle, dir);
236
+ const symlinks = {};
237
+ collectSymlinks(dir, "", symlinks);
238
+ await writeSymlinksFile(root.handle, symlinks);
239
+ }));
240
+ }
241
+ dispose() {
242
+ this.#unsubscribeTouch();
243
+ void this.flush();
244
+ }
245
+ }
@@ -0,0 +1,51 @@
1
+ import { checkedU64AsNumber } from "./common.js";
2
+ const MAX_BYTES = 65536;
3
+ let insecureRandomValue1, insecureRandomValue2;
4
+ export const insecure = {
5
+ getInsecureRandomBytes(len) {
6
+ return random.getRandomBytes(len);
7
+ },
8
+ getInsecureRandomU64() {
9
+ return random.getRandomU64();
10
+ },
11
+ };
12
+ let insecureSeedValue1, insecureSeedValue2;
13
+ export const insecureSeed = {
14
+ insecureSeed() {
15
+ if (insecureSeedValue1 === undefined || insecureSeedValue2 === undefined) {
16
+ insecureSeedValue1 = random.getRandomU64();
17
+ insecureSeedValue2 = random.getRandomU64();
18
+ }
19
+ return [insecureSeedValue1, insecureSeedValue2];
20
+ },
21
+ };
22
+ export const random = {
23
+ getRandomBytes(len) {
24
+ const byteLength = checkedU64AsNumber(len, "random byte length");
25
+ const bytes = new Uint8Array(byteLength);
26
+ if (byteLength > MAX_BYTES) {
27
+ // this is the max bytes crypto.getRandomValues
28
+ // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues
29
+ for (let generated = 0; generated < byteLength; generated += MAX_BYTES) {
30
+ // buffer.slice automatically checks if the end is past the end of
31
+ // the buffer so we don't have to here
32
+ crypto.getRandomValues(bytes.subarray(generated, generated + MAX_BYTES));
33
+ }
34
+ }
35
+ else {
36
+ crypto.getRandomValues(bytes);
37
+ }
38
+ return bytes;
39
+ },
40
+ getRandomU64() {
41
+ return crypto.getRandomValues(new BigUint64Array(1))[0];
42
+ },
43
+ // @ts-expect-error Not defined in WIT
44
+ insecureRandom() {
45
+ if (insecureRandomValue1 === undefined || insecureRandomValue2 === undefined) {
46
+ insecureRandomValue1 = random.getRandomU64();
47
+ insecureRandomValue2 = random.getRandomU64();
48
+ }
49
+ return [insecureRandomValue1, insecureRandomValue2];
50
+ },
51
+ };
@@ -0,0 +1,11 @@
1
+ {
2
+ "apiVersion": 2,
3
+ "kind": "ui",
4
+ "id": "plasma-pixels",
5
+ "version": "0.1.0",
6
+ "module": "plasma_pixels.wasm",
7
+ "surfaces": [
8
+ { "id": "stage", "anchor": "stage", "model": "pixels" }
9
+ ],
10
+ "grants": {}
11
+ }