octane 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -117,10 +117,7 @@ function createStreamedRendererFrameStream(frames, options = {}) {
117
117
  let totalBytes = 0;
118
118
  let finished = false;
119
119
  let controller;
120
- const timer = setTimeout(
121
- () => fail(new Error("Streamed renderer response timed out.")),
122
- budget.timeoutMs
123
- );
120
+ let timer;
124
121
  const cleanup = () => {
125
122
  clearTimeout(timer);
126
123
  options.signal?.removeEventListener("abort", abort);
@@ -149,8 +146,13 @@ function createStreamedRendererFrameStream(frames, options = {}) {
149
146
  },
150
147
  async pull() {
151
148
  if (finished) return;
149
+ timer = setTimeout(
150
+ () => fail(new Error("Streamed renderer response timed out.")),
151
+ budget.timeoutMs
152
+ );
152
153
  try {
153
154
  const next = await iterator.next();
155
+ clearTimeout(timer);
154
156
  if (finished) return;
155
157
  if (next.done) {
156
158
  finished = true;
@@ -191,10 +193,15 @@ function streamedSignalSelectionScript(identity, nonce) {
191
193
  return `<script ${import_stream_protocol.STREAM_SCRIPT_ATTR}${nonce === void 0 ? "" : ` nonce="${escapeAttribute(nonce)}"`}>(function(g){var k="__octaneStreamedRenderer",e=g[k];if(!e){var q=[];g[k]={version:1,frames:q,receive:function(f){if(q.length>=512){this.overflow=true;return;}q.push(f);}};}var z="__octaneStreamedSignalSelections",v=g[z];if(!v){var a=[];v=g[z]={version:1,identities:a,register:function(i){if(a.length>=256){this.overflow=true;return;}a.push(i);}};}v.register(${encoded});})(globalThis);</script>`;
192
194
  }
193
195
  function createStreamedSignalInjection(identity, result, options = {}) {
196
+ return createSignalInjection(identity, result, options, "Streamed renderer injection timed out.");
197
+ }
198
+ function createSignalInjection(identity, result, options, timeoutMessage) {
194
199
  const budget = limits(options);
195
200
  const iterator = createStreamedSignalResultFrames(identity, result, options)[Symbol.asyncIterator]();
196
201
  let queued = options.announceSelection ? streamedSignalSelectionScript(identity, options.nonce) : "";
197
202
  let pending = false;
203
+ let awaitingAcceptance = false;
204
+ let queuedBytes = queued === "" ? 0 : new TextEncoder().encode(queued).byteLength;
198
205
  let finished = false;
199
206
  let subscribed = false;
200
207
  let totalBytes = 0;
@@ -225,11 +232,13 @@ function createStreamedSignalInjection(identity, result, options = {}) {
225
232
  rejectDone(error);
226
233
  };
227
234
  const pump = () => {
228
- if (!subscribed || pending || queued !== "" || finished) return;
235
+ if (!subscribed || pending || awaitingAcceptance || queued !== "" || finished) return;
229
236
  pending = true;
237
+ timer = setTimeout(() => fail(new Error(timeoutMessage)), budget.timeoutMs);
230
238
  void iterator.next().then(
231
239
  (next) => {
232
240
  pending = false;
241
+ clearTimeout(timer);
233
242
  if (finished) return;
234
243
  if (next.done) {
235
244
  finished = true;
@@ -245,6 +254,7 @@ function createStreamedSignalInjection(identity, result, options = {}) {
245
254
  }
246
255
  totalBytes += bytes;
247
256
  queued = html;
257
+ queuedBytes = bytes;
248
258
  notify?.();
249
259
  },
250
260
  (error) => {
@@ -256,17 +266,17 @@ function createStreamedSignalInjection(identity, result, options = {}) {
256
266
  const abort = () => {
257
267
  fail(options.signal?.reason ?? new DOMException("Aborted", "AbortError"));
258
268
  };
259
- timer = setTimeout(
260
- () => fail(new Error("Streamed renderer injection timed out.")),
261
- budget.timeoutMs
262
- );
263
269
  options.signal?.addEventListener("abort", abort, { once: true });
264
270
  if (options.signal?.aborted) abort();
265
271
  return {
266
272
  streamedRenderer: true,
273
+ get queuedBytes() {
274
+ return queuedBytes;
275
+ },
267
276
  take() {
268
277
  const html = queued;
269
278
  queued = "";
279
+ if (html !== "") awaitingAcceptance = true;
270
280
  return html;
271
281
  },
272
282
  subscribe(callback) {
@@ -278,6 +288,7 @@ function createStreamedSignalInjection(identity, result, options = {}) {
278
288
  };
279
289
  },
280
290
  accepted() {
291
+ awaitingAcceptance = false;
281
292
  pump();
282
293
  },
283
294
  cancel: fail,
@@ -296,17 +307,20 @@ function createAutomaticStreamedSignalInjection(options, external) {
296
307
  if (!Number.isSafeInteger(selectionGeneration) || selectionGeneration < 0) {
297
308
  throw new RangeError("selectionGeneration must be a nonnegative safe integer.");
298
309
  }
299
- const children = [];
310
+ const children = /* @__PURE__ */ new Set();
311
+ const ready = /* @__PURE__ */ new Set();
300
312
  const observedAttempts = /* @__PURE__ */ new Set();
301
313
  let initialSelections = [];
302
314
  let queued = "";
303
315
  let active;
316
+ let awaitingAcceptance = false;
317
+ let hasSignalAttempts = false;
318
+ let pumping = false;
304
319
  let subscribed = false;
305
320
  let renderComplete = false;
306
321
  let finished = false;
307
322
  let totalBytes = 0;
308
323
  let notify;
309
- let timer;
310
324
  let resolveDone;
311
325
  let rejectDone;
312
326
  const done = new Promise((resolve, reject) => {
@@ -314,6 +328,8 @@ function createAutomaticStreamedSignalInjection(options, external) {
314
328
  rejectDone = reject;
315
329
  });
316
330
  const cleanupChild = (child) => {
331
+ if (!children.delete(child)) return;
332
+ ready.delete(child);
317
333
  try {
318
334
  child.unsubscribe();
319
335
  } catch {
@@ -326,7 +342,6 @@ function createAutomaticStreamedSignalInjection(options, external) {
326
342
  const fail = (error) => {
327
343
  if (finished) return;
328
344
  finished = true;
329
- if (timer !== void 0) clearTimeout(timer);
330
345
  for (const child of children) {
331
346
  try {
332
347
  child.source.cancel?.(error);
@@ -337,11 +352,9 @@ function createAutomaticStreamedSignalInjection(options, external) {
337
352
  rejectDone(error);
338
353
  };
339
354
  const maybeDone = () => {
340
- if (finished || !renderComplete || active !== void 0 || queued !== "") return;
341
- for (const child of children) if (!child.done) return;
355
+ if (finished || !renderComplete || active !== void 0 || awaitingAcceptance || queued !== "" || children.size !== 0)
356
+ return;
342
357
  finished = true;
343
- if (timer !== void 0) clearTimeout(timer);
344
- for (const child of children) cleanupChild(child);
345
358
  resolveDone();
346
359
  };
347
360
  const takeInitialSelections = () => {
@@ -363,33 +376,41 @@ function createAutomaticStreamedSignalInjection(options, external) {
363
376
  return html;
364
377
  };
365
378
  const pump = () => {
366
- if (!subscribed || finished || active !== void 0 || queued !== "") return;
367
- if (initialSelections !== void 0) {
368
- try {
369
- queued = takeInitialSelections();
370
- } catch {
371
- return;
379
+ if (pumping || !subscribed || finished || active !== void 0 || awaitingAcceptance || queued !== "")
380
+ return;
381
+ pumping = true;
382
+ try {
383
+ if (initialSelections !== void 0) {
384
+ try {
385
+ queued = takeInitialSelections();
386
+ } catch {
387
+ return;
388
+ }
389
+ if (queued !== "") notify?.();
372
390
  }
373
- if (queued !== "") {
391
+ for (const child of ready) {
392
+ if (finished || active !== void 0 || awaitingAcceptance || queued !== "") return;
393
+ ready.delete(child);
394
+ const html = child.source.take();
395
+ if (finished) return;
396
+ if (html === "") {
397
+ if (child.done && (child.attempt !== void 0 || renderComplete)) cleanupChild(child);
398
+ continue;
399
+ }
400
+ const bytes = child.attempt === void 0 ? new TextEncoder().encode(html).byteLength : child.source.queuedBytes;
401
+ if (bytes > budget.maxFrameBytes || totalBytes + bytes > budget.maxTotalBytes) {
402
+ fail(new Error("Automatic streamed signals exceeded their byte budget."));
403
+ return;
404
+ }
405
+ totalBytes += bytes;
406
+ active = child;
407
+ queued = html;
374
408
  notify?.();
375
- return;
376
- }
377
- }
378
- for (const child of children) {
379
- const html = child.source.take();
380
- if (html === "") continue;
381
- const bytes = new TextEncoder().encode(html).byteLength;
382
- if (bytes > budget.maxFrameBytes || totalBytes + bytes > budget.maxTotalBytes) {
383
- fail(new Error("Automatic streamed signals exceeded their byte budget."));
384
- return;
385
409
  }
386
- totalBytes += bytes;
387
- active = child;
388
- queued = html;
389
- notify?.();
390
- return;
410
+ maybeDone();
411
+ } finally {
412
+ pumping = false;
391
413
  }
392
- maybeDone();
393
414
  };
394
415
  const addChild = (source, release, attempt) => {
395
416
  const child = {
@@ -400,17 +421,23 @@ function createAutomaticStreamedSignalInjection(options, external) {
400
421
  ...attempt === void 0 ? {} : { attempt },
401
422
  done: false
402
423
  };
403
- children.push(child);
404
- child.unsubscribe = source.subscribe(pump);
424
+ children.add(child);
425
+ const available = () => {
426
+ if (finished || !children.has(child)) return;
427
+ ready.add(child);
428
+ pump();
429
+ };
430
+ ready.add(child);
431
+ child.unsubscribe = source.subscribe(available);
405
432
  source.done.then(
406
433
  () => {
407
434
  child.done = true;
408
- pump();
435
+ available();
409
436
  },
410
437
  (error) => {
411
438
  if (child.attempt !== void 0 && !child.attempt.isCurrent()) {
412
439
  child.done = true;
413
- pump();
440
+ available();
414
441
  } else fail(error);
415
442
  }
416
443
  );
@@ -421,7 +448,7 @@ function createAutomaticStreamedSignalInjection(options, external) {
421
448
  takeInitialSelections,
422
449
  createSignalAttemptObservations: import_signal_query_observation.createServerSignalQueryAttemptObservations,
423
450
  get streamedRenderer() {
424
- return external?.streamedRenderer === true || children.some((child) => child.attempt !== void 0) ? true : void 0;
451
+ return external?.streamedRenderer === true || hasSignalAttempts ? true : void 0;
425
452
  },
426
453
  observeSignalAttempt(attempt, run) {
427
454
  if (finished || !attempt.isCurrent()) {
@@ -439,18 +466,13 @@ function createAutomaticStreamedSignalInjection(options, external) {
439
466
  attempt.release();
440
467
  return;
441
468
  }
442
- if (children.length >= 256) {
469
+ if (children.size >= 256) {
443
470
  attempt.release();
444
471
  fail(new Error("Automatic streamed signals exceeded their channel budget."));
445
472
  return;
446
473
  }
447
474
  observedAttempts.add(attemptKey);
448
- if (timer === void 0) {
449
- timer = setTimeout(
450
- () => fail(new Error("Automatic streamed signals timed out.")),
451
- budget.timeoutMs
452
- );
453
- }
475
+ hasSignalAttempts = true;
454
476
  const identity = {
455
477
  protocol: 1,
456
478
  buildId: options.buildId,
@@ -463,21 +485,27 @@ function createAutomaticStreamedSignalInjection(options, external) {
463
485
  attempt: attempt.attempt
464
486
  };
465
487
  initialSelections?.push({ identity, attempt });
466
- const source = createStreamedSignalInjection(identity, attempt.result, {
467
- signal: attempt.signal,
468
- run,
469
- nonce: options.nonce,
470
- announceSelection: initialSelections === void 0,
471
- maxFrameBytes: budget.maxFrameBytes,
472
- maxTotalBytes: budget.maxTotalBytes,
473
- timeoutMs: budget.timeoutMs
474
- });
488
+ const source = createSignalInjection(
489
+ identity,
490
+ attempt.result,
491
+ {
492
+ signal: attempt.signal,
493
+ run,
494
+ nonce: options.nonce,
495
+ announceSelection: initialSelections === void 0,
496
+ maxFrameBytes: budget.maxFrameBytes,
497
+ maxTotalBytes: budget.maxTotalBytes,
498
+ timeoutMs: budget.timeoutMs
499
+ },
500
+ "Automatic streamed signals timed out."
501
+ );
475
502
  addChild(source, attempt.release, attempt);
476
503
  pump();
477
504
  },
478
505
  take() {
479
506
  const html = queued;
480
507
  queued = "";
508
+ if (html !== "") awaitingAcceptance = true;
481
509
  return html;
482
510
  },
483
511
  subscribe(callback) {
@@ -490,15 +518,22 @@ function createAutomaticStreamedSignalInjection(options, external) {
490
518
  },
491
519
  accepted() {
492
520
  const child = active;
493
- active = void 0;
494
521
  child?.source.accepted?.();
522
+ if (child !== void 0 && children.has(child)) ready.add(child);
523
+ active = void 0;
524
+ awaitingAcceptance = false;
495
525
  pump();
496
526
  },
497
527
  cancel: fail,
498
528
  done,
499
529
  renderComplete() {
500
530
  renderComplete = true;
501
- for (const child of children) child.source.renderComplete?.();
531
+ external?.renderComplete?.();
532
+ for (const child of children) {
533
+ if (child.attempt !== void 0) child.source.renderComplete?.();
534
+ if (child.done) ready.add(child);
535
+ }
536
+ pump();
502
537
  maybeDone();
503
538
  }
504
539
  };
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  version: () => version
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const version = "0.3.0";
24
+ const version = "0.3.1";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  version