@absolutejs/absolute 0.19.0-beta.350 → 0.19.0-beta.352

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.
@@ -39873,6 +39873,9 @@ var CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
39873
39873
  var STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__";
39874
39874
  var STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__";
39875
39875
  var STREAM_TAIL_LOOKBEHIND = 128;
39876
+ var STREAMING_SLOT_TIMEOUT_MS = 5000;
39877
+ var STREAMING_SLOT_MAX_PER_RESPONSE = 128;
39878
+ var STREAMING_SLOT_MAX_HTML_BYTES = 64000;
39876
39879
  var createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`;
39877
39880
  var createSlotPatchStatement = (id, html) => `(window.${STREAMING_RUNTIME_GLOBAL}||function(i,h){window.${STREAMING_PENDING_GLOBAL}=window.${STREAMING_PENDING_GLOBAL}||{};window.${STREAMING_PENDING_GLOBAL}[i]=h;})(${JSON.stringify(id)},${JSON.stringify(html)});`;
39878
39881
  var createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "";
@@ -39889,32 +39892,197 @@ var injectHtmlIntoHead = (html, injection) => {
39889
39892
  return `${html}${injection}`;
39890
39893
  };
39891
39894
  var toUint8 = (value, encoder) => encoder.encode(value);
39892
- var toStreamingSlot = (slot) => ({
39893
- ...slot,
39894
- id: slot.id ?? createStreamingSlotId()
39895
+ var currentStreamingSlotPolicy = {
39896
+ timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
39897
+ fallbackHtml: "",
39898
+ errorHtml: undefined,
39899
+ maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
39900
+ maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
39901
+ };
39902
+ var clonePolicy = (policy) => ({
39903
+ ...policy
39904
+ });
39905
+ var normalizeSlotBytes = (value, fallback) => {
39906
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
39907
+ return Math.floor(value);
39908
+ }
39909
+ return fallback;
39910
+ };
39911
+ var normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback;
39912
+ var normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback;
39913
+ var hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key);
39914
+ var applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
39915
+ timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
39916
+ fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
39917
+ errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
39918
+ maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
39919
+ maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
39920
+ onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
39921
+ onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
39922
+ });
39923
+ var createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
39924
+ if (!policyOnError && !enhancerOnError)
39925
+ return;
39926
+ return (error, slot) => {
39927
+ policyOnError?.(error, slot);
39928
+ enhancerOnError?.(error, slot);
39929
+ };
39930
+ };
39931
+ var createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
39932
+ if (!policyOnSlotMetric && !callOnSlotMetric)
39933
+ return;
39934
+ return (metric) => {
39935
+ policyOnSlotMetric?.(metric);
39936
+ callOnSlotMetric?.(metric);
39937
+ };
39938
+ };
39939
+ var resolveStreamingSlotPolicy = (overridePolicy = {}) => {
39940
+ const base = getStreamingSlotPolicy();
39941
+ return applyStreamingSlotPolicyOverrides(base, overridePolicy);
39942
+ };
39943
+ var getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy);
39944
+ var setStreamingSlotPolicy = (policy = {}) => {
39945
+ const base = getStreamingSlotPolicy();
39946
+ currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
39947
+ };
39948
+ var withStreamingSlotPolicy = async (policy, callback) => {
39949
+ const previous = getStreamingSlotPolicy();
39950
+ setStreamingSlotPolicy(policy);
39951
+ try {
39952
+ return await callback();
39953
+ } finally {
39954
+ currentStreamingSlotPolicy = previous;
39955
+ }
39956
+ };
39957
+ var emitSlotMetric = (metric, onSlotMetric) => {
39958
+ onSlotMetric?.(metric);
39959
+ };
39960
+ var createTimeoutError = (slot, timeoutMs) => {
39961
+ const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
39962
+ error.__absTimeout = true;
39963
+ return error;
39964
+ };
39965
+ var toStreamingSlot = (slot, policy) => ({
39966
+ errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
39967
+ fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
39968
+ id: slot.id ?? createStreamingSlotId(),
39969
+ timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
39970
+ resolve: slot.resolve
39895
39971
  });
39896
- var resolveSlot = async (slot, onError) => {
39972
+ var prepareSlots = ({
39973
+ policy,
39974
+ slots,
39975
+ onError,
39976
+ onSlotMetric
39977
+ }) => {
39978
+ const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
39979
+ const maxSlotsPerResponse = policy.maxSlotsPerResponse;
39980
+ if (maxSlotsPerResponse === 0) {
39981
+ const error = new Error("Streaming slot limit is set to 0");
39982
+ for (const slot of preparedSlots) {
39983
+ onError?.(error, slot);
39984
+ emitSlotMetric({
39985
+ type: "dropped",
39986
+ slotId: slot.id,
39987
+ reason: "maxSlotsPerResponse is 0"
39988
+ }, onSlotMetric);
39989
+ }
39990
+ return [];
39991
+ }
39992
+ if (preparedSlots.length <= maxSlotsPerResponse) {
39993
+ preparedSlots.forEach((slot) => emitSlotMetric({
39994
+ type: "prepared",
39995
+ slotId: slot.id
39996
+ }, onSlotMetric));
39997
+ return preparedSlots;
39998
+ }
39999
+ const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
40000
+ const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
40001
+ droppedSlots.forEach((slot) => {
40002
+ onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
40003
+ emitSlotMetric({
40004
+ type: "dropped",
40005
+ slotId: slot.id,
40006
+ reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
40007
+ }, onSlotMetric);
40008
+ });
40009
+ keptSlots.forEach((slot) => emitSlotMetric({
40010
+ type: "prepared",
40011
+ slotId: slot.id
40012
+ }, onSlotMetric));
40013
+ return keptSlots;
40014
+ };
40015
+ var htmlByteLength = (value, encoder) => encoder.encode(value).length;
40016
+ var resolveSlot = async (slot, onError, policy, onSlotMetric) => {
40017
+ const safePolicy = policy ?? getStreamingSlotPolicy();
40018
+ const encoder = new TextEncoder;
40019
+ const start = Date.now();
39897
40020
  try {
39898
- const resolved = slot.resolve();
39899
- const html = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
39900
- Promise.resolve(resolved),
39901
- new Promise((_, reject) => setTimeout(() => reject(new Error(`Streaming slot "${slot.id}" timed out after ${slot.timeoutMs}ms`)), slot.timeoutMs))
39902
- ]) : await resolved;
40021
+ const maybeAsyncValue = Promise.resolve(slot.resolve());
40022
+ const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
40023
+ maybeAsyncValue,
40024
+ new Promise((_, reject) => setTimeout(() => {
40025
+ reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
40026
+ }, slot.timeoutMs))
40027
+ ]) : await maybeAsyncValue;
40028
+ const html = typeof resolved === "string" ? resolved : `${resolved}`;
40029
+ if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
40030
+ const bytes2 = htmlByteLength(html, encoder);
40031
+ const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
40032
+ const durationMs2 = Date.now() - start;
40033
+ onError?.(error, slot);
40034
+ emitSlotMetric({
40035
+ type: "size_exceeded",
40036
+ slotId: slot.id,
40037
+ durationMs: durationMs2,
40038
+ bytes: bytes2,
40039
+ error
40040
+ }, onSlotMetric);
40041
+ const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
40042
+ return {
40043
+ html: fallbackHtml,
40044
+ id: slot.id,
40045
+ durationMs: durationMs2,
40046
+ bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
40047
+ };
40048
+ }
40049
+ const durationMs = Date.now() - start;
40050
+ const bytes = htmlByteLength(html, encoder);
40051
+ emitSlotMetric({
40052
+ type: "resolved",
40053
+ slotId: slot.id,
40054
+ durationMs,
40055
+ bytes
40056
+ }, onSlotMetric);
39903
40057
  return {
39904
40058
  html,
39905
- id: slot.id
40059
+ id: slot.id,
40060
+ durationMs,
40061
+ bytes
39906
40062
  };
39907
40063
  } catch (error) {
40064
+ const durationMs = Date.now() - start;
39908
40065
  onError?.(error, slot);
40066
+ emitSlotMetric({
40067
+ type: error?.__absTimeout === true ? "timeout" : "error",
40068
+ slotId: slot.id,
40069
+ durationMs,
40070
+ error
40071
+ }, onSlotMetric);
39909
40072
  if (typeof slot.errorHtml === "string") {
40073
+ const html = slot.errorHtml;
39910
40074
  return {
39911
- html: slot.errorHtml,
39912
- id: slot.id
40075
+ html,
40076
+ id: slot.id,
40077
+ durationMs,
40078
+ bytes: htmlByteLength(html, encoder)
39913
40079
  };
39914
40080
  }
39915
40081
  return {
39916
40082
  html: null,
39917
- id: slot.id
40083
+ id: slot.id,
40084
+ durationMs,
40085
+ bytes: 0
39918
40086
  };
39919
40087
  }
39920
40088
  };
@@ -39930,23 +40098,37 @@ var streamOutOfOrderSlots = ({
39930
40098
  footerHtml = "",
39931
40099
  headerHtml = "",
39932
40100
  nonce,
40101
+ policy,
40102
+ onSlotMetric,
39933
40103
  onError,
39934
40104
  slots
39935
40105
  }) => {
40106
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
40107
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
40108
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
40109
+ const effectivePolicy = {
40110
+ ...resolvedPolicy,
40111
+ onSlotMetric: combinedOnSlotMetric
40112
+ };
40113
+ const preparedSlots = prepareSlots({
40114
+ policy: effectivePolicy,
40115
+ slots,
40116
+ onError: combinedOnError,
40117
+ onSlotMetric: combinedOnSlotMetric
40118
+ });
39936
40119
  const encoder = new TextEncoder;
39937
40120
  return new ReadableStream({
39938
40121
  async start(controller) {
39939
40122
  try {
39940
40123
  let header = headerHtml;
39941
- if (!header.includes(STREAMING_RUNTIME_GLOBAL)) {
40124
+ if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
39942
40125
  header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
39943
40126
  }
39944
40127
  controller.enqueue(toUint8(header, encoder));
39945
- const pending = slots.map((slot) => {
39946
- const resolvedSlot = toStreamingSlot(slot);
39947
- const fallback = renderStreamingSlotPlaceholder(resolvedSlot.id, resolvedSlot.fallbackHtml ?? "");
40128
+ const pending = preparedSlots.map((slot) => {
40129
+ const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
39948
40130
  controller.enqueue(toUint8(fallback, encoder));
39949
- return resolveSlot(resolvedSlot, onError);
40131
+ return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
39950
40132
  });
39951
40133
  while (pending.length > 0) {
39952
40134
  const { original, result } = await nextResolvedSlot(pending);
@@ -39955,6 +40137,12 @@ var streamOutOfOrderSlots = ({
39955
40137
  pending.splice(index, 1);
39956
40138
  if (result.html === null)
39957
40139
  continue;
40140
+ emitSlotMetric({
40141
+ type: "patched",
40142
+ slotId: result.id,
40143
+ durationMs: result.durationMs,
40144
+ bytes: result.bytes
40145
+ }, combinedOnSlotMetric);
39958
40146
  controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
39959
40147
  }
39960
40148
  if (footerHtml.length > 0) {
@@ -40018,12 +40206,33 @@ var injectStreamingRuntimeIntoStream = (stream, nonce) => {
40018
40206
  }
40019
40207
  });
40020
40208
  };
40021
- var appendStreamingSlotPatchesToStream = (stream, slots = [], { injectRuntime = true, nonce, onError } = {}) => {
40209
+ var appendStreamingSlotPatchesToStream = (stream, slots = [], {
40210
+ injectRuntime = true,
40211
+ nonce,
40212
+ onError,
40213
+ onSlotMetric,
40214
+ policy
40215
+ } = {}) => {
40216
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
40217
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
40218
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
40219
+ const effectivePolicy = {
40220
+ ...resolvedPolicy,
40221
+ onSlotMetric: combinedOnSlotMetric
40222
+ };
40223
+ const preparedSlots = prepareSlots({
40224
+ policy: effectivePolicy,
40225
+ slots,
40226
+ onError: combinedOnError,
40227
+ onSlotMetric: combinedOnSlotMetric
40228
+ });
40229
+ if (preparedSlots.length === 0)
40230
+ return stream;
40022
40231
  const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
40023
40232
  const encoder = new TextEncoder;
40024
40233
  const decoder = new TextDecoder;
40025
40234
  const reader = source.getReader();
40026
- const pending = slots.map((slot) => resolveSlot(slot, onError));
40235
+ const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
40027
40236
  return new ReadableStream({
40028
40237
  async start(controller) {
40029
40238
  let baseDone = false;
@@ -40080,6 +40289,12 @@ var appendStreamingSlotPatchesToStream = (stream, slots = [], { injectRuntime =
40080
40289
  pending.splice(index, 1);
40081
40290
  if (winner.result.html === null)
40082
40291
  continue;
40292
+ emitSlotMetric({
40293
+ type: "patched",
40294
+ slotId: winner.result.id,
40295
+ durationMs: winner.result.durationMs,
40296
+ bytes: winner.result.bytes
40297
+ }, combinedOnSlotMetric);
40083
40298
  controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
40084
40299
  }
40085
40300
  if (footer.length > 0)
@@ -40138,13 +40353,14 @@ var cloneHeaders = (response) => {
40138
40353
  const headers = new Headers(response.headers);
40139
40354
  return headers;
40140
40355
  };
40141
- var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [] } = {}) => {
40356
+ var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
40142
40357
  if (!response.body || streamingSlots.length === 0) {
40143
40358
  return response;
40144
40359
  }
40145
40360
  const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
40146
40361
  nonce,
40147
- onError
40362
+ onError,
40363
+ policy
40148
40364
  });
40149
40365
  return new Response(body, {
40150
40366
  headers: cloneHeaders(response),
@@ -40375,5 +40591,5 @@ export {
40375
40591
  Island
40376
40592
  };
40377
40593
 
40378
- //# debugId=EFC6BBC6A7E9C77864756E2164756E21
40594
+ //# debugId=87DA1ED8BAE8A55064756E2164756E21
40379
40595
  //# sourceMappingURL=index.js.map