@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.
@@ -30788,6 +30788,9 @@ var CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
30788
30788
  var STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__";
30789
30789
  var STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__";
30790
30790
  var STREAM_TAIL_LOOKBEHIND = 128;
30791
+ var STREAMING_SLOT_TIMEOUT_MS = 5000;
30792
+ var STREAMING_SLOT_MAX_PER_RESPONSE = 128;
30793
+ var STREAMING_SLOT_MAX_HTML_BYTES = 64000;
30791
30794
  var createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`;
30792
30795
  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)});`;
30793
30796
  var createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "";
@@ -30804,32 +30807,197 @@ var injectHtmlIntoHead = (html, injection) => {
30804
30807
  return `${html}${injection}`;
30805
30808
  };
30806
30809
  var toUint8 = (value, encoder) => encoder.encode(value);
30807
- var toStreamingSlot = (slot) => ({
30808
- ...slot,
30809
- id: slot.id ?? createStreamingSlotId()
30810
+ var currentStreamingSlotPolicy = {
30811
+ timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
30812
+ fallbackHtml: "",
30813
+ errorHtml: undefined,
30814
+ maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
30815
+ maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
30816
+ };
30817
+ var clonePolicy = (policy) => ({
30818
+ ...policy
30810
30819
  });
30811
- var resolveSlot = async (slot, onError) => {
30820
+ var normalizeSlotBytes = (value, fallback) => {
30821
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
30822
+ return Math.floor(value);
30823
+ }
30824
+ return fallback;
30825
+ };
30826
+ var normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback;
30827
+ var normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback;
30828
+ var hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key);
30829
+ var applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
30830
+ timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
30831
+ fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
30832
+ errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
30833
+ maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
30834
+ maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
30835
+ onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
30836
+ onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
30837
+ });
30838
+ var createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
30839
+ if (!policyOnError && !enhancerOnError)
30840
+ return;
30841
+ return (error, slot) => {
30842
+ policyOnError?.(error, slot);
30843
+ enhancerOnError?.(error, slot);
30844
+ };
30845
+ };
30846
+ var createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
30847
+ if (!policyOnSlotMetric && !callOnSlotMetric)
30848
+ return;
30849
+ return (metric) => {
30850
+ policyOnSlotMetric?.(metric);
30851
+ callOnSlotMetric?.(metric);
30852
+ };
30853
+ };
30854
+ var resolveStreamingSlotPolicy = (overridePolicy = {}) => {
30855
+ const base = getStreamingSlotPolicy();
30856
+ return applyStreamingSlotPolicyOverrides(base, overridePolicy);
30857
+ };
30858
+ var getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy);
30859
+ var setStreamingSlotPolicy = (policy = {}) => {
30860
+ const base = getStreamingSlotPolicy();
30861
+ currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
30862
+ };
30863
+ var withStreamingSlotPolicy = async (policy, callback) => {
30864
+ const previous = getStreamingSlotPolicy();
30865
+ setStreamingSlotPolicy(policy);
30812
30866
  try {
30813
- const resolved = slot.resolve();
30814
- const html = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
30815
- Promise.resolve(resolved),
30816
- new Promise((_, reject) => setTimeout(() => reject(new Error(`Streaming slot "${slot.id}" timed out after ${slot.timeoutMs}ms`)), slot.timeoutMs))
30817
- ]) : await resolved;
30867
+ return await callback();
30868
+ } finally {
30869
+ currentStreamingSlotPolicy = previous;
30870
+ }
30871
+ };
30872
+ var emitSlotMetric = (metric, onSlotMetric) => {
30873
+ onSlotMetric?.(metric);
30874
+ };
30875
+ var createTimeoutError = (slot, timeoutMs) => {
30876
+ const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
30877
+ error.__absTimeout = true;
30878
+ return error;
30879
+ };
30880
+ var toStreamingSlot = (slot, policy) => ({
30881
+ errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
30882
+ fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
30883
+ id: slot.id ?? createStreamingSlotId(),
30884
+ timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
30885
+ resolve: slot.resolve
30886
+ });
30887
+ var prepareSlots = ({
30888
+ policy,
30889
+ slots,
30890
+ onError,
30891
+ onSlotMetric
30892
+ }) => {
30893
+ const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
30894
+ const maxSlotsPerResponse = policy.maxSlotsPerResponse;
30895
+ if (maxSlotsPerResponse === 0) {
30896
+ const error = new Error("Streaming slot limit is set to 0");
30897
+ for (const slot of preparedSlots) {
30898
+ onError?.(error, slot);
30899
+ emitSlotMetric({
30900
+ type: "dropped",
30901
+ slotId: slot.id,
30902
+ reason: "maxSlotsPerResponse is 0"
30903
+ }, onSlotMetric);
30904
+ }
30905
+ return [];
30906
+ }
30907
+ if (preparedSlots.length <= maxSlotsPerResponse) {
30908
+ preparedSlots.forEach((slot) => emitSlotMetric({
30909
+ type: "prepared",
30910
+ slotId: slot.id
30911
+ }, onSlotMetric));
30912
+ return preparedSlots;
30913
+ }
30914
+ const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
30915
+ const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
30916
+ droppedSlots.forEach((slot) => {
30917
+ onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
30918
+ emitSlotMetric({
30919
+ type: "dropped",
30920
+ slotId: slot.id,
30921
+ reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
30922
+ }, onSlotMetric);
30923
+ });
30924
+ keptSlots.forEach((slot) => emitSlotMetric({
30925
+ type: "prepared",
30926
+ slotId: slot.id
30927
+ }, onSlotMetric));
30928
+ return keptSlots;
30929
+ };
30930
+ var htmlByteLength = (value, encoder) => encoder.encode(value).length;
30931
+ var resolveSlot = async (slot, onError, policy, onSlotMetric) => {
30932
+ const safePolicy = policy ?? getStreamingSlotPolicy();
30933
+ const encoder = new TextEncoder;
30934
+ const start = Date.now();
30935
+ try {
30936
+ const maybeAsyncValue = Promise.resolve(slot.resolve());
30937
+ const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
30938
+ maybeAsyncValue,
30939
+ new Promise((_, reject) => setTimeout(() => {
30940
+ reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
30941
+ }, slot.timeoutMs))
30942
+ ]) : await maybeAsyncValue;
30943
+ const html = typeof resolved === "string" ? resolved : `${resolved}`;
30944
+ if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
30945
+ const bytes2 = htmlByteLength(html, encoder);
30946
+ const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
30947
+ const durationMs2 = Date.now() - start;
30948
+ onError?.(error, slot);
30949
+ emitSlotMetric({
30950
+ type: "size_exceeded",
30951
+ slotId: slot.id,
30952
+ durationMs: durationMs2,
30953
+ bytes: bytes2,
30954
+ error
30955
+ }, onSlotMetric);
30956
+ const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
30957
+ return {
30958
+ html: fallbackHtml,
30959
+ id: slot.id,
30960
+ durationMs: durationMs2,
30961
+ bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
30962
+ };
30963
+ }
30964
+ const durationMs = Date.now() - start;
30965
+ const bytes = htmlByteLength(html, encoder);
30966
+ emitSlotMetric({
30967
+ type: "resolved",
30968
+ slotId: slot.id,
30969
+ durationMs,
30970
+ bytes
30971
+ }, onSlotMetric);
30818
30972
  return {
30819
30973
  html,
30820
- id: slot.id
30974
+ id: slot.id,
30975
+ durationMs,
30976
+ bytes
30821
30977
  };
30822
30978
  } catch (error) {
30979
+ const durationMs = Date.now() - start;
30823
30980
  onError?.(error, slot);
30981
+ emitSlotMetric({
30982
+ type: error?.__absTimeout === true ? "timeout" : "error",
30983
+ slotId: slot.id,
30984
+ durationMs,
30985
+ error
30986
+ }, onSlotMetric);
30824
30987
  if (typeof slot.errorHtml === "string") {
30988
+ const html = slot.errorHtml;
30825
30989
  return {
30826
- html: slot.errorHtml,
30827
- id: slot.id
30990
+ html,
30991
+ id: slot.id,
30992
+ durationMs,
30993
+ bytes: htmlByteLength(html, encoder)
30828
30994
  };
30829
30995
  }
30830
30996
  return {
30831
30997
  html: null,
30832
- id: slot.id
30998
+ id: slot.id,
30999
+ durationMs,
31000
+ bytes: 0
30833
31001
  };
30834
31002
  }
30835
31003
  };
@@ -30845,23 +31013,37 @@ var streamOutOfOrderSlots = ({
30845
31013
  footerHtml = "",
30846
31014
  headerHtml = "",
30847
31015
  nonce,
31016
+ policy,
31017
+ onSlotMetric,
30848
31018
  onError,
30849
31019
  slots
30850
31020
  }) => {
31021
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
31022
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
31023
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
31024
+ const effectivePolicy = {
31025
+ ...resolvedPolicy,
31026
+ onSlotMetric: combinedOnSlotMetric
31027
+ };
31028
+ const preparedSlots = prepareSlots({
31029
+ policy: effectivePolicy,
31030
+ slots,
31031
+ onError: combinedOnError,
31032
+ onSlotMetric: combinedOnSlotMetric
31033
+ });
30851
31034
  const encoder = new TextEncoder;
30852
31035
  return new ReadableStream({
30853
31036
  async start(controller) {
30854
31037
  try {
30855
31038
  let header = headerHtml;
30856
- if (!header.includes(STREAMING_RUNTIME_GLOBAL)) {
31039
+ if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
30857
31040
  header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
30858
31041
  }
30859
31042
  controller.enqueue(toUint8(header, encoder));
30860
- const pending = slots.map((slot) => {
30861
- const resolvedSlot = toStreamingSlot(slot);
30862
- const fallback = renderStreamingSlotPlaceholder(resolvedSlot.id, resolvedSlot.fallbackHtml ?? "");
31043
+ const pending = preparedSlots.map((slot) => {
31044
+ const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
30863
31045
  controller.enqueue(toUint8(fallback, encoder));
30864
- return resolveSlot(resolvedSlot, onError);
31046
+ return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
30865
31047
  });
30866
31048
  while (pending.length > 0) {
30867
31049
  const { original, result } = await nextResolvedSlot(pending);
@@ -30870,6 +31052,12 @@ var streamOutOfOrderSlots = ({
30870
31052
  pending.splice(index, 1);
30871
31053
  if (result.html === null)
30872
31054
  continue;
31055
+ emitSlotMetric({
31056
+ type: "patched",
31057
+ slotId: result.id,
31058
+ durationMs: result.durationMs,
31059
+ bytes: result.bytes
31060
+ }, combinedOnSlotMetric);
30873
31061
  controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
30874
31062
  }
30875
31063
  if (footerHtml.length > 0) {
@@ -30933,12 +31121,33 @@ var injectStreamingRuntimeIntoStream = (stream, nonce) => {
30933
31121
  }
30934
31122
  });
30935
31123
  };
30936
- var appendStreamingSlotPatchesToStream = (stream, slots = [], { injectRuntime = true, nonce, onError } = {}) => {
31124
+ var appendStreamingSlotPatchesToStream = (stream, slots = [], {
31125
+ injectRuntime = true,
31126
+ nonce,
31127
+ onError,
31128
+ onSlotMetric,
31129
+ policy
31130
+ } = {}) => {
31131
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
31132
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
31133
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
31134
+ const effectivePolicy = {
31135
+ ...resolvedPolicy,
31136
+ onSlotMetric: combinedOnSlotMetric
31137
+ };
31138
+ const preparedSlots = prepareSlots({
31139
+ policy: effectivePolicy,
31140
+ slots,
31141
+ onError: combinedOnError,
31142
+ onSlotMetric: combinedOnSlotMetric
31143
+ });
31144
+ if (preparedSlots.length === 0)
31145
+ return stream;
30937
31146
  const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
30938
31147
  const encoder = new TextEncoder;
30939
31148
  const decoder = new TextDecoder;
30940
31149
  const reader = source.getReader();
30941
- const pending = slots.map((slot) => resolveSlot(slot, onError));
31150
+ const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
30942
31151
  return new ReadableStream({
30943
31152
  async start(controller) {
30944
31153
  let baseDone = false;
@@ -30995,6 +31204,12 @@ var appendStreamingSlotPatchesToStream = (stream, slots = [], { injectRuntime =
30995
31204
  pending.splice(index, 1);
30996
31205
  if (winner.result.html === null)
30997
31206
  continue;
31207
+ emitSlotMetric({
31208
+ type: "patched",
31209
+ slotId: winner.result.id,
31210
+ durationMs: winner.result.durationMs,
31211
+ bytes: winner.result.bytes
31212
+ }, combinedOnSlotMetric);
30998
31213
  controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
30999
31214
  }
31000
31215
  if (footer.length > 0)
@@ -31053,13 +31268,14 @@ var cloneHeaders = (response) => {
31053
31268
  const headers = new Headers(response.headers);
31054
31269
  return headers;
31055
31270
  };
31056
- var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [] } = {}) => {
31271
+ var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
31057
31272
  if (!response.body || streamingSlots.length === 0) {
31058
31273
  return response;
31059
31274
  }
31060
31275
  const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
31061
31276
  nonce,
31062
- onError
31277
+ onError,
31278
+ policy
31063
31279
  });
31064
31280
  return new Response(body, {
31065
31281
  headers: cloneHeaders(response),
@@ -31258,5 +31474,5 @@ export {
31258
31474
  Island
31259
31475
  };
31260
31476
 
31261
- //# debugId=6C80507F504CFD3E64756E2164756E21
31477
+ //# debugId=4C1B4E3A143891BD64756E2164756E21
31262
31478
  //# sourceMappingURL=index.js.map