sproutboat 0.4.11 → 0.6.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.
@@ -11,11 +11,25 @@
11
11
  // Declared before it is referenced: a getter body that names a later top-level
12
12
  // class throws ReferenceError in Porffor (see patches/UPSTREAM.md draft B).
13
13
 
14
+ // Duck-typing helpers, `typeof`-free (the repo's anti-slop lint bans `typeof`;
15
+ // these express the same spec-mandated checks and are verified under Porffor
16
+ // alpha-4 by examples/kitchen-sink/harness.ts).
17
+ function __sbIsStr(v) {
18
+ return Object(v) !== v && v === String(v);
19
+ }
20
+ function __sbIsFn(v) {
21
+ return v instanceof Function;
22
+ }
23
+ function __sbIsObj(v) {
24
+ return v !== null && Object(v) === v;
25
+ }
26
+
14
27
  // #41 — cold-start phase marker. Runs as the first thing in the bundle: writes
15
28
  // the current wall-clock ms to $SB_STARTUP_FILE so the supervisor can split
16
29
  // cold-start into "spawn -> JS starts" (process + runtime bootstrap) and
17
30
  // "JS starts -> listening" (module eval + server bind). No-op when unset.
18
31
  function __sbStartupMark() {
32
+ // oxlint-disable-next-line no-unused-expressions -- Porffor.c`...` is inline C the compiler consumes, not a JS expression.
19
33
  Porffor.c`
20
34
  const char* __f = getenv("SB_STARTUP_FILE");
21
35
  if (__f) {
@@ -36,7 +50,8 @@ __sbStartupMark();
36
50
  // then parsed — Porffor's number boxing for a bare inline-C assignment is not
37
51
  // relied on. `__sbEntry` samples it around the handler for per-invocation CPU.
38
52
  function __sbCpuMs() {
39
- let res = '';
53
+ let res = "";
54
+ // oxlint-disable-next-line no-unused-expressions -- Porffor.c`...` is inline C the compiler consumes, not a JS expression.
40
55
  Porffor.c`
41
56
  struct timespec __ts;
42
57
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &__ts);
@@ -45,7 +60,7 @@ function __sbCpuMs() {
45
60
  int __n = snprintf(__b, sizeof(__b), "%.3f", __ms);
46
61
  if (__n > 0) res = porf_box((f64)porf_native_fetch_alloc_bytestring(__b, (size_t)__n), 195);
47
62
  `;
48
- return res === '' ? 0 : parseFloat(res);
63
+ return res === "" ? 0 : parseFloat(res);
49
64
  }
50
65
 
51
66
  // #28 — stamp `x-sb-cpu-ms` onto a handler Response. Porffor alpha-4's
@@ -59,13 +74,17 @@ function __sbTagCpu(res, t0) {
59
74
  const cpu = __sbCpuMs() - t0;
60
75
  try {
61
76
  const body = res && res.body;
62
- if (typeof body === 'string' && !res.headers.has('set-cookie')) {
77
+ if (__sbIsStr(body) && !res.headers.has("set-cookie")) {
63
78
  const headers = {};
64
- res.headers.forEach(function (value, name) { headers[name] = value; });
65
- headers['x-sb-cpu-ms'] = (cpu >= 0 ? cpu : 0).toFixed(3);
79
+ res.headers.forEach(function (value, name) {
80
+ headers[name] = value;
81
+ });
82
+ headers["x-sb-cpu-ms"] = (cpu >= 0 ? cpu : 0).toFixed(3);
66
83
  return new Response(body, { status: res.status, headers: headers });
67
84
  }
68
- } catch (e) { /* fall through to the original response */ }
85
+ } catch {
86
+ /* fall through to the original response */
87
+ }
69
88
  return res;
70
89
  }
71
90
 
@@ -73,53 +92,87 @@ class __SproutboatURLSearchParams {
73
92
  constructor(init) {
74
93
  this._keys = [];
75
94
  this._vals = [];
76
- let raw = init == null ? '' : String(init);
95
+ let raw = init == null ? "" : String(init);
77
96
  if (raw.charCodeAt(0) === 63) raw = raw.slice(1); // strip a leading '?'
78
97
  if (raw.length === 0) return;
79
- const pairs = raw.split('&');
98
+ const pairs = raw.split("&");
80
99
  for (let i = 0; i < pairs.length; i++) {
81
100
  const pair = pairs[i];
82
101
  if (pair.length === 0) continue;
83
- const eq = pair.indexOf('=');
102
+ const eq = pair.indexOf("=");
84
103
  const k = eq === -1 ? pair : pair.slice(0, eq);
85
- const v = eq === -1 ? '' : pair.slice(eq + 1);
86
- this._keys.push(decodeURIComponent(k.split('+').join(' ')));
87
- this._vals.push(decodeURIComponent(v.split('+').join(' ')));
104
+ const v = eq === -1 ? "" : pair.slice(eq + 1);
105
+ this._keys.push(decodeURIComponent(k.split("+").join(" ")));
106
+ this._vals.push(decodeURIComponent(v.split("+").join(" ")));
88
107
  }
89
108
  }
90
- get(name) { for (let i = 0; i < this._keys.length; i++) if (this._keys[i] === name) return this._vals[i]; return null; }
91
- getAll(name) { const out = []; for (let i = 0; i < this._keys.length; i++) if (this._keys[i] === name) out.push(this._vals[i]); return out; }
92
- has(name) { for (let i = 0; i < this._keys.length; i++) if (this._keys[i] === name) return true; return false; }
93
- forEach(cb) { for (let i = 0; i < this._keys.length; i++) cb(this._vals[i], this._keys[i], this); }
109
+ get(name) {
110
+ for (let i = 0; i < this._keys.length; i++) if (this._keys[i] === name) return this._vals[i];
111
+ return null;
112
+ }
113
+ getAll(name) {
114
+ const out = [];
115
+ for (let i = 0; i < this._keys.length; i++) if (this._keys[i] === name) out.push(this._vals[i]);
116
+ return out;
117
+ }
118
+ has(name) {
119
+ for (let i = 0; i < this._keys.length; i++) if (this._keys[i] === name) return true;
120
+ return false;
121
+ }
122
+ forEach(cb) {
123
+ for (let i = 0; i < this._keys.length; i++) cb(this._vals[i], this._keys[i], this);
124
+ }
94
125
  // Mutators: standalone `new URLSearchParams()` building works. They do NOT
95
126
  // write back into a URL's `search` (Porffor's URL has no setter) — build the
96
127
  // string with toString() and assign it yourself.
97
- append(name, value) { this._keys.push(String(name)); this._vals.push(String(value)); }
128
+ append(name, value) {
129
+ this._keys.push(String(name));
130
+ this._vals.push(String(value));
131
+ }
98
132
  set(name, value) {
99
133
  let found = false;
100
134
  for (let i = 0; i < this._keys.length; i++) {
101
135
  if (this._keys[i] !== name) continue;
102
- if (found) { this._keys.splice(i, 1); this._vals.splice(i, 1); i--; }
103
- else { this._vals[i] = String(value); found = true; }
136
+ if (found) {
137
+ this._keys.splice(i, 1);
138
+ this._vals.splice(i, 1);
139
+ i--;
140
+ } else {
141
+ this._vals[i] = String(value);
142
+ found = true;
143
+ }
104
144
  }
105
145
  if (!found) this.append(name, value);
106
146
  }
107
147
  delete(name) {
108
- for (let i = 0; i < this._keys.length; i++) if (this._keys[i] === name) { this._keys.splice(i, 1); this._vals.splice(i, 1); i--; }
148
+ for (let i = 0; i < this._keys.length; i++)
149
+ if (this._keys[i] === name) {
150
+ this._keys.splice(i, 1);
151
+ this._vals.splice(i, 1);
152
+ i--;
153
+ }
109
154
  }
110
155
  sort() {
111
- const idx = this._keys.map((_, i) => i).sort((a, b) => (this._keys[a] < this._keys[b] ? -1 : this._keys[a] > this._keys[b] ? 1 : 0));
156
+ const idx = this._keys
157
+ .map((_, i) => i)
158
+ .sort((a, b) => (this._keys[a] < this._keys[b] ? -1 : this._keys[a] > this._keys[b] ? 1 : 0));
112
159
  this._keys = idx.map((i) => this._keys[i]);
113
160
  this._vals = idx.map((i) => this._vals[i]);
114
161
  }
115
- keys() { return this._keys.slice(); }
116
- values() { return this._vals.slice(); }
117
- get size() { return this._keys.length; }
162
+ keys() {
163
+ return this._keys.slice();
164
+ }
165
+ values() {
166
+ return this._vals.slice();
167
+ }
168
+ get size() {
169
+ return this._keys.length;
170
+ }
118
171
  toString() {
119
- let out = '';
172
+ let out = "";
120
173
  for (let i = 0; i < this._keys.length; i++) {
121
- if (i > 0) out += '&';
122
- out += encodeURIComponent(this._keys[i]) + '=' + encodeURIComponent(this._vals[i]);
174
+ if (i > 0) out += "&";
175
+ out += encodeURIComponent(this._keys[i]) + "=" + encodeURIComponent(this._vals[i]);
123
176
  }
124
177
  return out;
125
178
  }
@@ -128,8 +181,8 @@ class __SproutboatURLSearchParams {
128
181
  // URL and Response are always defined by Porffor's fetch-globals.js banner.
129
182
  if (globalThis.URLSearchParams == null) globalThis.URLSearchParams = __SproutboatURLSearchParams;
130
183
 
131
- if (!('searchParams' in URL.prototype)) {
132
- Object.defineProperty(URL.prototype, 'searchParams', {
184
+ if (!("searchParams" in URL.prototype)) {
185
+ Object.defineProperty(URL.prototype, "searchParams", {
133
186
  configurable: true,
134
187
  get() {
135
188
  if (this.__sbSearchParams == null) this.__sbSearchParams = new __SproutboatURLSearchParams(this.search);
@@ -141,7 +194,7 @@ if (!('searchParams' in URL.prototype)) {
141
194
  if (Response.json == null) {
142
195
  Response.json = function (data, init) {
143
196
  const response = new Response(JSON.stringify(data), init);
144
- if (!response.headers.has('content-type')) response.headers.set('content-type', 'application/json;charset=utf-8');
197
+ if (!response.headers.has("content-type")) response.headers.set("content-type", "application/json;charset=utf-8");
145
198
  return response;
146
199
  };
147
200
  }
@@ -153,27 +206,33 @@ if (Response.json == null) {
153
206
  function __sbDefineURLAccessor(name, get) {
154
207
  if (!(name in URL.prototype)) Object.defineProperty(URL.prototype, name, { configurable: true, get });
155
208
  }
156
- __sbDefineURLAccessor('protocol', function () {
157
- const i = this.origin.indexOf('://');
158
- return i === -1 ? '' : this.origin.slice(0, i + 1);
209
+ __sbDefineURLAccessor("protocol", function () {
210
+ const i = this.origin.indexOf("://");
211
+ return i === -1 ? "" : this.origin.slice(0, i + 1);
159
212
  });
160
- __sbDefineURLAccessor('host', function () {
161
- const i = this.origin.indexOf('://');
162
- return i === -1 ? '' : this.origin.slice(i + 3);
213
+ __sbDefineURLAccessor("host", function () {
214
+ const i = this.origin.indexOf("://");
215
+ return i === -1 ? "" : this.origin.slice(i + 3);
163
216
  });
164
- __sbDefineURLAccessor('hostname', function () {
217
+ __sbDefineURLAccessor("hostname", function () {
165
218
  const h = this.host;
166
- const c = h.indexOf(':');
219
+ const c = h.indexOf(":");
167
220
  return c === -1 ? h : h.slice(0, c);
168
221
  });
169
- __sbDefineURLAccessor('port', function () {
222
+ __sbDefineURLAccessor("port", function () {
170
223
  const h = this.host;
171
- const c = h.indexOf(':');
172
- return c === -1 ? '' : h.slice(c + 1);
224
+ const c = h.indexOf(":");
225
+ return c === -1 ? "" : h.slice(c + 1);
226
+ });
227
+ __sbDefineURLAccessor("hash", function () {
228
+ return "";
229
+ });
230
+ __sbDefineURLAccessor("username", function () {
231
+ return "";
232
+ });
233
+ __sbDefineURLAccessor("password", function () {
234
+ return "";
173
235
  });
174
- __sbDefineURLAccessor('hash', function () { return ''; });
175
- __sbDefineURLAccessor('username', function () { return ''; });
176
- __sbDefineURLAccessor('password', function () { return ''; });
177
236
 
178
237
  // crypto.randomUUID / getRandomValues are absent in native-fetch. Provide them
179
238
  // backed by the OS CSPRNG (`__sbRandomBytes` -> inline C -> /dev/urandom), so
@@ -200,7 +259,9 @@ if (globalThis.structuredClone == null) {
200
259
  // The suggested fix (use structuredClone) is circular — this IS the polyfill,
201
260
  // and Porffor exposes no other deep-clone primitive.
202
261
  // react-doctor-disable-next-line react-doctor/no-json-parse-stringify-clone
203
- globalThis.structuredClone = function (value) { return JSON.parse(JSON.stringify(value)); };
262
+ globalThis.structuredClone = function (value) {
263
+ return JSON.parse(JSON.stringify(value));
264
+ };
204
265
  }
205
266
 
206
267
  if (globalThis.crypto.randomUUID == null) {
@@ -232,6 +293,7 @@ if (globalThis.crypto.randomUUID == null) {
232
293
  // the old fresh-connection-per-call path just failed the call there instead.
233
294
  // Binary values + AF_UNIX = v2.
234
295
 
296
+ // oxlint-disable-next-line no-unused-expressions -- Porffor.c`...` is inline C the compiler consumes, not a JS expression.
235
297
  Porffor.c`
236
298
  #include <sys/socket.h>
237
299
  #include <netinet/in.h>
@@ -359,8 +421,10 @@ static int sb_broker_roundtrip(const char* req, size_t req_len, char** resp_out,
359
421
 
360
422
  // One request string in, one reply string out. `reqJson` is a parameter, so the
361
423
  // generated C names it directly in the RawC block below.
424
+ // oxlint-disable-next-line no-unused-vars -- `reqJson` is read inside the RawC block below, not by JS.
362
425
  function __sbCall(reqJson) {
363
- let res = '';
426
+ let res = "";
427
+ // oxlint-disable-next-line no-unused-expressions -- Porffor.c`...` is inline C the compiler consumes, not a JS expression.
364
428
  Porffor.c`
365
429
  const char* __req; size_t __reqlen; char* __reqowned = 0;
366
430
  porf_native_fetch_read_value(reqJson, &__req, &__reqlen, &__reqowned);
@@ -381,8 +445,10 @@ function __sbCall(reqJson) {
381
445
 
382
446
  // `nStr` is the decimal byte count as a string (same string-param pattern as
383
447
  // __sbEnv). Returns a bytestring of that many CSPRNG bytes, or '' on failure.
448
+ // oxlint-disable-next-line no-unused-vars -- `nStr` is read inside the RawC block below, not by JS.
384
449
  function __sbRandomBytes(nStr) {
385
- let out = '';
450
+ let out = "";
451
+ // oxlint-disable-next-line no-unused-expressions -- Porffor.c`...` is inline C the compiler consumes, not a JS expression.
386
452
  Porffor.c`
387
453
  const char* __ns; size_t __nsl; char* __nso = 0;
388
454
  porf_native_fetch_read_value(nStr, &__ns, &__nsl, &__nso);
@@ -407,7 +473,7 @@ function __sbRpc(op, extra) {
407
473
  const req = { op };
408
474
  if (extra) for (const k in extra) req[k] = extra[k];
409
475
  const reply = JSON.parse(__sbCall(JSON.stringify(req)));
410
- if (reply && reply.ok === false) throw new Error(`sproutboat ${op}: ${reply.error || 'failed'}`);
476
+ if (reply && reply.ok === false) throw new Error(`sproutboat ${op}: ${reply.error || "failed"}`);
411
477
  return reply;
412
478
  }
413
479
 
@@ -418,12 +484,16 @@ function __sbMakeD1(dbName) {
418
484
  const s = {
419
485
  __sql: sql,
420
486
  __params: params,
421
- bind() { return stmt(sql, Array.prototype.slice.call(arguments)); },
487
+ bind() {
488
+ return stmt(sql, Array.prototype.slice.call(arguments));
489
+ },
422
490
  all() {
423
- const r = __sbRpc('d1.query', { db: dbName, sql, params });
491
+ const r = __sbRpc("d1.query", { db: dbName, sql, params });
424
492
  return { results: r.results || [], success: true, meta: r.meta || {} };
425
493
  },
426
- run() { return s.all(); },
494
+ run() {
495
+ return s.all();
496
+ },
427
497
  raw() {
428
498
  const rows = s.all().results;
429
499
  const out = [];
@@ -443,17 +513,21 @@ function __sbMakeD1(dbName) {
443
513
  return s;
444
514
  }
445
515
  return {
446
- prepare(sql) { return stmt(String(sql), []); },
516
+ prepare(sql) {
517
+ return stmt(String(sql), []);
518
+ },
447
519
  batch(statements) {
448
520
  const list = [];
449
- for (let i = 0; i < (statements || []).length; i++) list.push({ sql: statements[i].__sql, params: statements[i].__params });
450
- const r = __sbRpc('d1.batch', { db: dbName, statements: list });
521
+ for (let i = 0; i < (statements || []).length; i++)
522
+ list.push({ sql: statements[i].__sql, params: statements[i].__params });
523
+ const r = __sbRpc("d1.batch", { db: dbName, statements: list });
451
524
  const out = [];
452
- for (let i = 0; i < (r.results || []).length; i++) out.push({ results: r.results[i].results || [], success: true, meta: r.results[i].meta || {} });
525
+ for (let i = 0; i < (r.results || []).length; i++)
526
+ out.push({ results: r.results[i].results || [], success: true, meta: r.results[i].meta || {} });
453
527
  return out;
454
528
  },
455
529
  exec(sql) {
456
- __sbRpc('d1.exec', { db: dbName, sql: String(sql) });
530
+ __sbRpc("d1.exec", { db: dbName, sql: String(sql) });
457
531
  return { count: (String(sql).match(/;/g) || []).length, duration: 0 };
458
532
  },
459
533
  };
@@ -473,8 +547,12 @@ function __sbR2Object(meta, body) {
473
547
  };
474
548
  if (body != null) {
475
549
  obj.body = body;
476
- obj.text = function () { return body; };
477
- obj.json = function () { return JSON.parse(body); };
550
+ obj.text = function () {
551
+ return body;
552
+ };
553
+ obj.json = function () {
554
+ return JSON.parse(body);
555
+ };
478
556
  }
479
557
  return obj;
480
558
  }
@@ -490,17 +568,17 @@ globalThis.__sbInstallBindings = function (target, bindings) {
490
568
  const ns = bindings.kv[i];
491
569
  target[ns] = {
492
570
  get(key) {
493
- const r = __sbRpc('kv.get', { ns, key: String(key) });
571
+ const r = __sbRpc("kv.get", { ns, key: String(key) });
494
572
  return r.found ? r.value : null;
495
573
  },
496
574
  put(key, value) {
497
- __sbRpc('kv.put', { ns, key: String(key), value: String(value) });
575
+ __sbRpc("kv.put", { ns, key: String(key), value: String(value) });
498
576
  },
499
577
  delete(key) {
500
- __sbRpc('kv.delete', { ns, key: String(key) });
578
+ __sbRpc("kv.delete", { ns, key: String(key) });
501
579
  },
502
580
  list(prefix) {
503
- return __sbRpc('kv.list', { ns, prefix: prefix == null ? '' : String(prefix) }).keys || [];
581
+ return __sbRpc("kv.list", { ns, prefix: prefix == null ? "" : String(prefix) }).keys || [];
504
582
  },
505
583
  };
506
584
  }
@@ -514,7 +592,7 @@ globalThis.__sbInstallBindings = function (target, bindings) {
514
592
  Object.defineProperty(target, name, {
515
593
  configurable: true,
516
594
  get() {
517
- const value = __sbRpc('secret.get', { name }).value;
595
+ const value = __sbRpc("secret.get", { name }).value;
518
596
  Object.defineProperty(target, name, { value, configurable: true, enumerable: true });
519
597
  return value;
520
598
  },
@@ -531,31 +609,31 @@ globalThis.__sbInstallBindings = function (target, bindings) {
531
609
  target[name] = {
532
610
  put(key, value, options) {
533
611
  const o = options || {};
534
- return __sbRpc('r2.put', {
612
+ return __sbRpc("r2.put", {
535
613
  bucket: name,
536
614
  key: String(key),
537
- body: value == null ? '' : String(value),
615
+ body: value == null ? "" : String(value),
538
616
  httpMetadata: o.httpMetadata || {},
539
617
  customMetadata: o.customMetadata || {},
540
618
  }).object;
541
619
  },
542
620
  get(key) {
543
- const r = __sbRpc('r2.get', { bucket: name, key: String(key) });
544
- return r.found ? __sbR2Object(r.object, r.body == null ? '' : r.body) : null;
621
+ const r = __sbRpc("r2.get", { bucket: name, key: String(key) });
622
+ return r.found ? __sbR2Object(r.object, r.body == null ? "" : r.body) : null;
545
623
  },
546
624
  head(key) {
547
- const r = __sbRpc('r2.head', { bucket: name, key: String(key) });
625
+ const r = __sbRpc("r2.head", { bucket: name, key: String(key) });
548
626
  return r.found ? __sbR2Object(r.object, null) : null;
549
627
  },
550
628
  delete(key) {
551
- __sbRpc('r2.delete', { bucket: name, key: String(key) });
629
+ __sbRpc("r2.delete", { bucket: name, key: String(key) });
552
630
  },
553
631
  list(options) {
554
632
  const o = options || {};
555
- const r = __sbRpc('r2.list', {
633
+ const r = __sbRpc("r2.list", {
556
634
  bucket: name,
557
- prefix: o.prefix == null ? '' : String(o.prefix),
558
- cursor: o.cursor == null ? '' : String(o.cursor),
635
+ prefix: o.prefix == null ? "" : String(o.prefix),
636
+ cursor: o.cursor == null ? "" : String(o.cursor),
559
637
  limit: o.limit == null ? 1000 : o.limit,
560
638
  });
561
639
  const objects = [];
@@ -570,15 +648,19 @@ globalThis.__sbInstallBindings = function (target, bindings) {
570
648
  target[name] = {
571
649
  send(body, options) {
572
650
  const o = options || {};
573
- __sbRpc('queue.send', { queue: name, body: typeof body === 'string' ? body : JSON.stringify(body), delaySeconds: o.delaySeconds || 0 });
651
+ __sbRpc("queue.send", {
652
+ queue: name,
653
+ body: __sbIsStr(body) ? body : JSON.stringify(body),
654
+ delaySeconds: o.delaySeconds || 0,
655
+ });
574
656
  },
575
657
  sendBatch(messages) {
576
658
  const list = [];
577
659
  for (let j = 0; j < (messages || []).length; j++) {
578
660
  const m = messages[j];
579
- list.push({ body: typeof m.body === 'string' ? m.body : JSON.stringify(m.body), delaySeconds: (m.delaySeconds || 0) });
661
+ list.push({ body: __sbIsStr(m.body) ? m.body : JSON.stringify(m.body), delaySeconds: m.delaySeconds || 0 });
580
662
  }
581
- __sbRpc('queue.send_batch', { queue: name, messages: list });
663
+ __sbRpc("queue.send_batch", { queue: name, messages: list });
582
664
  },
583
665
  };
584
666
  }
@@ -588,7 +670,7 @@ globalThis.__sbInstallBindings = function (target, bindings) {
588
670
  target[name] = {
589
671
  writeDataPoint(event) {
590
672
  const e = event || {};
591
- __sbRpc('ae.write', {
673
+ __sbRpc("ae.write", {
592
674
  dataset: name,
593
675
  indexes: e.indexes || [],
594
676
  blobs: e.blobs || [],
@@ -599,7 +681,7 @@ globalThis.__sbInstallBindings = function (target, bindings) {
599
681
  // query it via the SQL API). Returns { count, rows }.
600
682
  query(options) {
601
683
  const o = options || {};
602
- return __sbRpc('ae.query', { dataset: name, limit: o.limit || 20 });
684
+ return __sbRpc("ae.query", { dataset: name, limit: o.limit || 20 });
603
685
  },
604
686
  };
605
687
  }
@@ -616,35 +698,39 @@ globalThis.__sbInstallBindings = function (target, bindings) {
616
698
  if (bindings.assets) {
617
699
  target[bindings.assets] = {
618
700
  fetch(input) {
619
- let path = typeof input === 'string' ? input : String(input && input.url || '/');
620
- try { path = new URL(path, 'http://a').pathname; } catch (_e) { /* use as-is */ }
621
- const r = __sbRpc('assets.get', { path });
701
+ let path = __sbIsStr(input) ? input : String((input && input.url) || "/");
702
+ try {
703
+ path = new URL(path, "http://a").pathname;
704
+ } catch {
705
+ /* use as-is */
706
+ }
707
+ const r = __sbRpc("assets.get", { path });
622
708
  const headers = {};
623
- if (r.type) headers['content-type'] = r.type;
624
- if (r.found) headers['etag'] = '"' + r.hash + '"';
625
- return new Response(r.body == null ? '' : r.body, { status: r.status || (r.found ? 200 : 404), headers });
709
+ if (r.type) headers["content-type"] = r.type;
710
+ if (r.found) headers["etag"] = '"' + r.hash + '"';
711
+ return new Response(r.body == null ? "" : r.body, { status: r.status || (r.found ? 200 : 404), headers });
626
712
  },
627
713
  };
628
714
  }
629
715
 
630
716
  if ((bindings.outbound || []).length > 0) {
631
717
  globalThis.fetch = function (input, init) {
632
- const url = typeof input === 'string' ? input : String(input.url);
718
+ const url = __sbIsStr(input) ? input : String(input.url);
633
719
  const opts = init || {};
634
720
  const headers = [];
635
721
  if (opts.headers) {
636
- if (typeof opts.headers.forEach === 'function') opts.headers.forEach((v, k) => headers.push([k, v]));
722
+ if (__sbIsFn(opts.headers.forEach)) opts.headers.forEach((v, k) => headers.push([k, v]));
637
723
  else for (const k in opts.headers) headers.push([k, opts.headers[k]]);
638
724
  }
639
- const r = __sbRpc('fetch', {
725
+ const r = __sbRpc("fetch", {
640
726
  url,
641
- method: opts.method || 'GET',
727
+ method: opts.method || "GET",
642
728
  headers,
643
729
  body: opts.body == null ? null : String(opts.body),
644
730
  });
645
731
  const respHeaders = new Headers();
646
732
  for (let j = 0; j < (r.headers || []).length; j++) respHeaders.set(r.headers[j][0], r.headers[j][1]);
647
- return new Response(r.body == null ? '' : r.body, { status: r.status || 502, headers: respHeaders });
733
+ return new Response(r.body == null ? "" : r.body, { status: r.status || 502, headers: respHeaders });
648
734
  };
649
735
  }
650
736
  };
@@ -662,27 +748,44 @@ globalThis.__sbInstallBindings = function (target, bindings) {
662
748
 
663
749
  function __sbMakeDONamespace(binding, className) {
664
750
  return {
665
- idFromName(name) { return { toString() { return 'name:' + String(name); }, name: String(name) }; },
666
- idFromString(hex) { return { toString() { return String(hex); } }; },
751
+ idFromName(name) {
752
+ return {
753
+ toString() {
754
+ return "name:" + String(name);
755
+ },
756
+ name: String(name),
757
+ };
758
+ },
759
+ idFromString(hex) {
760
+ return {
761
+ toString() {
762
+ return String(hex);
763
+ },
764
+ };
765
+ },
667
766
  newUniqueId() {
668
- return { toString() { return 'uid:' + crypto.randomUUID(); } };
767
+ return {
768
+ toString() {
769
+ return "uid:" + crypto.randomUUID();
770
+ },
771
+ };
669
772
  },
670
773
  get(id) {
671
- const idStr = typeof id === 'string' ? id : id.toString();
774
+ const idStr = __sbIsStr(id) ? id : id.toString();
672
775
  return {
673
776
  fetch(input, init) {
674
777
  let req;
675
- if (input && typeof input === 'object' && typeof input.url === 'string' && !init) {
778
+ if (__sbIsObj(input) && __sbIsStr(input.url) && !init) {
676
779
  req = input;
677
780
  } else {
678
- const url = typeof input === 'string' ? input : String((input && input.url) || 'https://do/');
781
+ const url = __sbIsStr(input) ? input : String((input && input.url) || "https://do/");
679
782
  const opts = init || {};
680
783
  const headers = new Headers();
681
784
  if (opts.headers) {
682
- if (typeof opts.headers.forEach === 'function') opts.headers.forEach((v, k) => headers.set(k, v));
785
+ if (__sbIsFn(opts.headers.forEach)) opts.headers.forEach((v, k) => headers.set(k, v));
683
786
  else for (const k in opts.headers) headers.set(k, opts.headers[k]);
684
787
  }
685
- req = new Request(url, { method: opts.method || 'GET', headers });
788
+ req = new Request(url, { method: opts.method || "GET", headers });
686
789
  if (opts.body != null) req.body = String(opts.body);
687
790
  }
688
791
  return __sbGetDOInstance(className, idStr).fetch(req);
@@ -700,14 +803,20 @@ globalThis.__sbRegisterDO = function (map) {
700
803
 
701
804
  function __sbGetDOInstance(cls, id) {
702
805
  const Ctor = __sbDOClasses[cls];
703
- if (!Ctor) throw new Error('no such Durable Object class: ' + cls);
704
- const cacheKey = cls + ' ' + id;
806
+ if (!Ctor) throw new Error("no such Durable Object class: " + cls);
807
+ const cacheKey = cls + " " + id;
705
808
  let inst = __sbDOInstances[cacheKey];
706
809
  if (!inst) {
707
810
  const state = {
708
- id: { toString() { return id; } },
811
+ id: {
812
+ toString() {
813
+ return id;
814
+ },
815
+ },
709
816
  storage: __sbDOStorage(cls, id),
710
- blockConcurrencyWhile(fn) { return fn(); },
817
+ blockConcurrencyWhile(fn) {
818
+ return fn();
819
+ },
711
820
  waitUntil() {},
712
821
  };
713
822
  inst = new Ctor(state, globalThis.env);
@@ -722,33 +831,41 @@ function __sbDOStorage(cls, id) {
722
831
  if (Array.isArray(key)) {
723
832
  const out = new Map();
724
833
  for (let i = 0; i < key.length; i++) {
725
- const r = __sbRpc('do.storage.get', { cls, id, key: String(key[i]) });
834
+ const r = __sbRpc("do.storage.get", { cls, id, key: String(key[i]) });
726
835
  if (r.found) out.set(key[i], JSON.parse(r.value));
727
836
  }
728
837
  return out;
729
838
  }
730
- const r = __sbRpc('do.storage.get', { cls, id, key: String(key) });
839
+ const r = __sbRpc("do.storage.get", { cls, id, key: String(key) });
731
840
  return r.found ? JSON.parse(r.value) : undefined;
732
841
  },
733
842
  put(key, value) {
734
- if (key != null && typeof key === 'object') {
735
- for (const k in key) __sbRpc('do.storage.put', { cls, id, key: String(k), value: JSON.stringify(key[k]) });
843
+ if (key != null && __sbIsObj(key)) {
844
+ for (const k in key) __sbRpc("do.storage.put", { cls, id, key: String(k), value: JSON.stringify(key[k]) });
736
845
  return;
737
846
  }
738
- __sbRpc('do.storage.put', { cls, id, key: String(key), value: JSON.stringify(value) });
847
+ __sbRpc("do.storage.put", { cls, id, key: String(key), value: JSON.stringify(value) });
739
848
  },
740
849
  delete(key) {
741
850
  if (Array.isArray(key)) {
742
851
  let n = 0;
743
- for (let i = 0; i < key.length; i++) n += __sbRpc('do.storage.delete', { cls, id, key: String(key[i]) }).deleted ? 1 : 0;
852
+ for (let i = 0; i < key.length; i++)
853
+ n += __sbRpc("do.storage.delete", { cls, id, key: String(key[i]) }).deleted ? 1 : 0;
744
854
  return n;
745
855
  }
746
- return !!__sbRpc('do.storage.delete', { cls, id, key: String(key) }).deleted;
856
+ return !!__sbRpc("do.storage.delete", { cls, id, key: String(key) }).deleted;
857
+ },
858
+ deleteAll() {
859
+ __sbRpc("do.storage.delete_all", { cls, id });
747
860
  },
748
- deleteAll() { __sbRpc('do.storage.delete_all', { cls, id }); },
749
861
  list(options) {
750
862
  const o = options || {};
751
- const r = __sbRpc('do.storage.list', { cls, id, prefix: o.prefix == null ? '' : String(o.prefix), limit: o.limit == null ? 1000 : o.limit });
863
+ const r = __sbRpc("do.storage.list", {
864
+ cls,
865
+ id,
866
+ prefix: o.prefix == null ? "" : String(o.prefix),
867
+ limit: o.limit == null ? 1000 : o.limit,
868
+ });
752
869
  const out = new Map();
753
870
  for (let i = 0; i < (r.entries || []).length; i++) out.set(r.entries[i][0], JSON.parse(r.entries[i][1]));
754
871
  return out;
@@ -762,8 +879,10 @@ function __sbDOStorage(cls, id) {
762
879
  // with SB_BROKER_TOKEN) to the right user handler, and everything else to
763
880
  // `handlers.fetch`.
764
881
 
882
+ // oxlint-disable-next-line no-unused-vars -- `name` is read inside the RawC block below, not by JS.
765
883
  function __sbEnv(name) {
766
- let res = '';
884
+ let res = "";
885
+ // oxlint-disable-next-line no-unused-expressions -- Porffor.c`...` is inline C the compiler consumes, not a JS expression.
767
886
  Porffor.c`
768
887
  const char* __n; size_t __nl; char* __no = 0;
769
888
  porf_native_fetch_read_value(name, &__n, &__nl, &__no);
@@ -778,37 +897,40 @@ function __sbEnv(name) {
778
897
  }
779
898
 
780
899
  function __sbTriggerAuthed(request) {
781
- const want = __sbEnv('SB_BROKER_TOKEN');
900
+ const want = __sbEnv("SB_BROKER_TOKEN");
782
901
  if (!want) return true; // no token configured (local/dev)
783
- return request.headers.get('x-sb-token') === want;
902
+ return request.headers.get("x-sb-token") === want;
784
903
  }
785
904
 
786
905
  globalThis.__sbEntry = function (handlers, request) {
787
- const trigger = request.headers.get('x-sb-trigger');
906
+ const trigger = request.headers.get("x-sb-trigger");
788
907
  if (!trigger) {
789
908
  // #28 — per-invocation CPU time. One fetch turn per process (serial), so the
790
- // process CPU delta across the handler is this invocation's CPU. Covers both
791
- // sync handlers and async ones (via `.then`); the delta spans the whole turn.
909
+ // process CPU delta across the handler is this invocation's CPU.
792
910
  // ponytail: serial-turn assumption; revisit if the profile ever allows
793
911
  // concurrent in-process requests.
912
+ //
913
+ // Sync handlers only. An async handler's promise is handed straight back:
914
+ // Porffor alpha-4's native-fetch server resolves the promise the handler
915
+ // itself returned, but never one derived from `.then()`, so chaining the
916
+ // tag on hangs the request forever. cpuMs is documented as absent for
917
+ // async handlers (see LogEvent in services/edge) — that is this.
794
918
  const __t0 = __sbCpuMs();
795
919
  const __res = handlers.fetch(request);
796
- if (__res && typeof __res.then === 'function') {
797
- return __res.then(function (resolved) { return __sbTagCpu(resolved, __t0); });
798
- }
920
+ if (__res && __sbIsFn(__res.then)) return __res;
799
921
  return __sbTagCpu(__res, __t0);
800
922
  }
801
- if (!__sbTriggerAuthed(request)) return new Response('forbidden', { status: 403 });
923
+ if (!__sbTriggerAuthed(request)) return new Response("forbidden", { status: 403 });
802
924
 
803
- if (trigger === 'scheduled') {
804
- if (typeof handlers.scheduled !== 'function') return new Response('no scheduled handler', { status: 404 });
925
+ if (trigger === "scheduled") {
926
+ if (!__sbIsFn(handlers.scheduled)) return new Response("no scheduled handler", { status: 404 });
805
927
  const body = __sbReadJson(request);
806
- handlers.scheduled({ cron: body.cron || '', scheduledTime: body.scheduledTime || Date.now(), noRetry() {} });
807
- return new Response('', { status: 204 });
928
+ handlers.scheduled({ cron: body.cron || "", scheduledTime: body.scheduledTime || Date.now(), noRetry() {} });
929
+ return new Response("", { status: 204 });
808
930
  }
809
931
 
810
- if (trigger === 'queue') {
811
- if (typeof handlers.queue !== 'function') return new Response('no queue handler', { status: 404 });
932
+ if (trigger === "queue") {
933
+ if (!__sbIsFn(handlers.queue)) return new Response("no queue handler", { status: 404 });
812
934
  const body = __sbReadJson(request);
813
935
  const acked = [];
814
936
  const retried = [];
@@ -821,32 +943,49 @@ globalThis.__sbEntry = function (handlers, request) {
821
943
  timestamp: m.timestamp,
822
944
  attempts: m.attempts || 1,
823
945
  body: __sbTryParse(m.body),
824
- ack() { if (acked.indexOf(m.id) === -1) acked.push(m.id); },
825
- retry() { if (retried.indexOf(m.id) === -1) retried.push(m.id); },
946
+ ack() {
947
+ if (acked.indexOf(m.id) === -1) acked.push(m.id);
948
+ },
949
+ retry() {
950
+ if (retried.indexOf(m.id) === -1) retried.push(m.id);
951
+ },
826
952
  };
827
953
  messages.push(msg);
828
954
  }
829
955
  const batch = {
830
- queue: body.queue || '',
956
+ queue: body.queue || "",
831
957
  messages,
832
- ackAll() { for (let i = 0; i < messages.length; i++) messages[i].ack(); },
833
- retryAll() { for (let i = 0; i < messages.length; i++) messages[i].retry(); },
958
+ ackAll() {
959
+ for (let i = 0; i < messages.length; i++) messages[i].ack();
960
+ },
961
+ retryAll() {
962
+ for (let i = 0; i < messages.length; i++) messages[i].retry();
963
+ },
834
964
  };
835
965
  handlers.queue(batch);
836
966
  // default: any message neither acked nor retried is treated as acked
837
967
  for (let i = 0; i < messages.length; i++) {
838
968
  if (acked.indexOf(messages[i].id) === -1 && retried.indexOf(messages[i].id) === -1) acked.push(messages[i].id);
839
969
  }
840
- return new Response(JSON.stringify({ ack: acked, retry: retried }), { headers: { 'content-type': 'application/json' } });
970
+ return new Response(JSON.stringify({ ack: acked, retry: retried }), {
971
+ headers: { "content-type": "application/json" },
972
+ });
841
973
  }
842
974
 
843
-
844
- return new Response('unknown trigger', { status: 400 });
975
+ return new Response("unknown trigger", { status: 400 });
845
976
  };
846
977
 
847
978
  function __sbReadJson(request) {
848
- try { return JSON.parse(request.body == null ? '{}' : String(request.body)); } catch (e) { return {}; }
979
+ try {
980
+ return JSON.parse(request.body == null ? "{}" : String(request.body));
981
+ } catch {
982
+ return {};
983
+ }
849
984
  }
850
985
  function __sbTryParse(s) {
851
- try { return JSON.parse(s); } catch (e) { return s; }
986
+ try {
987
+ return JSON.parse(s);
988
+ } catch {
989
+ return s;
990
+ }
852
991
  }