@logbrew/sdk 0.1.37 → 0.1.39

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 (103) hide show
  1. package/automatic-delivery.cjs +271 -0
  2. package/batch-sender.cjs +164 -0
  3. package/bounded-number.cjs +34 -0
  4. package/browser-breadcrumbs.cjs +346 -136
  5. package/capture-support.cjs +100 -0
  6. package/client-options.cjs +292 -0
  7. package/client-report.cjs +232 -0
  8. package/client.cjs +447 -0
  9. package/console-arguments.cjs +151 -0
  10. package/console-capture.cjs +194 -0
  11. package/core.cjs +35 -2984
  12. package/delivery-health.cjs +133 -0
  13. package/error-causes.cjs +130 -0
  14. package/error-details.cjs +124 -0
  15. package/error-issue.cjs +187 -0
  16. package/event-clone.cjs +55 -0
  17. package/event-metadata.cjs +40 -0
  18. package/event-pipeline.cjs +325 -0
  19. package/event-queue.cjs +306 -0
  20. package/event-store.cjs +237 -0
  21. package/event-validation.cjs +159 -0
  22. package/examples/agent-timeline.cjs +57 -46
  23. package/examples/agent-timeline.mjs +41 -35
  24. package/examples/index.mjs +122 -94
  25. package/examples/readme-example.cjs +36 -27
  26. package/examples/readme-example.mjs +21 -17
  27. package/examples/real-user-smoke.cjs +41 -32
  28. package/examples/real-user-smoke.mjs +26 -22
  29. package/exception-chain.cjs +205 -0
  30. package/index.d.ts +144 -1076
  31. package/index.js +1 -1
  32. package/issue-breadcrumbs.cjs +143 -0
  33. package/issue-diagnostics.cjs +94 -582
  34. package/issue-evidence.cjs +256 -0
  35. package/issue-exception.cjs +268 -0
  36. package/issue-stack-frames.cjs +220 -0
  37. package/issue-stack-text.cjs +101 -0
  38. package/issue-stack.cjs +188 -225
  39. package/issue-text.cjs +72 -0
  40. package/log-context.cjs +87 -67
  41. package/metric-validation.cjs +95 -0
  42. package/network-milestone.cjs +155 -0
  43. package/null-value.cjs +4 -0
  44. package/opentelemetry-bridge.cjs +330 -0
  45. package/opentelemetry-context.cjs +245 -0
  46. package/opentelemetry-exceptions.cjs +69 -0
  47. package/opentelemetry-keys.cjs +183 -0
  48. package/opentelemetry-span.cjs +286 -0
  49. package/opentelemetry-summary.cjs +213 -0
  50. package/opentelemetry-time.cjs +131 -0
  51. package/opentelemetry.cjs +99 -1068
  52. package/package.json +64 -1
  53. package/pino-destination.cjs +108 -0
  54. package/pino-record.cjs +237 -0
  55. package/product-action.cjs +163 -0
  56. package/provided-breadcrumbs.cjs +117 -0
  57. package/random-hex.cjs +29 -0
  58. package/react-native.js +1 -1
  59. package/recording-transport.cjs +68 -0
  60. package/release-artifacts-build.cjs +263 -84
  61. package/release-artifacts-common.js +347 -66
  62. package/release-artifacts-debug-id.js +178 -0
  63. package/release-artifacts-inspect.js +213 -0
  64. package/release-artifacts-issue-event.js +99 -0
  65. package/release-artifacts-manifest-artifact.js +107 -0
  66. package/release-artifacts-manifest.js +77 -0
  67. package/release-artifacts-mappings.js +265 -0
  68. package/release-artifacts-prepare.js +297 -0
  69. package/release-artifacts-source-context.js +210 -0
  70. package/release-artifacts-symbolication.js +272 -421
  71. package/release-artifacts-upload-files.js +122 -0
  72. package/release-artifacts-upload-request.js +246 -0
  73. package/release-artifacts-upload.js +155 -300
  74. package/release-artifacts-urls.js +49 -0
  75. package/release-artifacts.js +189 -662
  76. package/retry-delay.cjs +32 -0
  77. package/sdk-error.cjs +36 -0
  78. package/span-validation.cjs +191 -0
  79. package/stored-events.cjs +243 -0
  80. package/support-ticket.cjs +232 -122
  81. package/telemetry-context.cjs +343 -251
  82. package/trace-context.cjs +376 -179
  83. package/transport-error.cjs +23 -0
  84. package/types/client.d.ts +67 -0
  85. package/types/delivery.d.ts +125 -0
  86. package/types/issues.d.ts +249 -0
  87. package/types/logging.d.ts +175 -0
  88. package/types/opentelemetry.d.ts +205 -0
  89. package/types/product.d.ts +128 -0
  90. package/types/recording-transport.d.ts +15 -0
  91. package/types/sdk-error.d.ts +9 -0
  92. package/types/telemetry.d.ts +171 -0
  93. package/types/trace-context.d.ts +110 -0
  94. package/types/transport-error.d.ts +10 -0
  95. package/utf8.cjs +37 -0
  96. package/validation.cjs +327 -0
  97. package/vite-release-artifacts-config.cjs +120 -0
  98. package/vite-release-artifacts.cjs +199 -99
  99. package/vite-release-artifacts.d.cts +65 -40
  100. package/vite-release-artifacts.d.ts +65 -40
  101. package/vite-release-artifacts.js +2 -2
  102. package/winston-info.cjs +205 -0
  103. package/winston.cjs +189 -290
@@ -0,0 +1,32 @@
1
+ const MAX_DELIVERY_INTERVAL_MS = 60_000;
2
+ const MIN_RATE_LIMIT_WAIT_MS = 1000;
3
+ const MAX_RATE_LIMIT_WAIT_MS = 3_600_000;
4
+ const MAX_BACKOFF_EXPONENT = 30;
5
+ const FIRST_FAILURE = 1;
6
+ const BACKOFF_BASE = 2;
7
+ const HALF = 2;
8
+ const INCLUSIVE = 1;
9
+ const JITTER_DIVISOR = 10;
10
+
11
+ /**
12
+ * @param {number} deliveryIntervalMs
13
+ * @param {number} consecutiveFailures
14
+ * @returns {number}
15
+ */
16
+ function automaticRetryDelayMs(deliveryIntervalMs, consecutiveFailures) {
17
+ const exponent = Math.min(consecutiveFailures - FIRST_FAILURE, MAX_BACKOFF_EXPONENT);
18
+ const maximumDelay = Math.min(MAX_DELIVERY_INTERVAL_MS, deliveryIntervalMs * (BACKOFF_BASE ** exponent));
19
+ const minimumDelay = Math.ceil(maximumDelay / HALF);
20
+ return minimumDelay + Math.floor(Math.random() * (maximumDelay - minimumDelay + INCLUSIVE));
21
+ }
22
+
23
+ /**
24
+ * @param {number} delayMs
25
+ * @returns {number}
26
+ */
27
+ function rateLimitWaitMs(delayMs) {
28
+ const bounded = Math.min(MAX_RATE_LIMIT_WAIT_MS, Math.max(MIN_RATE_LIMIT_WAIT_MS, delayMs));
29
+ return Math.min(MAX_RATE_LIMIT_WAIT_MS, bounded + Math.floor(Math.random() * (Math.floor(bounded / JITTER_DIVISOR) + INCLUSIVE)));
30
+ }
31
+
32
+ module.exports = { MAX_DELIVERY_INTERVAL_MS, automaticRetryDelayMs, rateLimitWaitMs };
package/sdk-error.cjs ADDED
@@ -0,0 +1,36 @@
1
+ const MIN_RETRY_AFTER_MS = 0;
2
+
3
+ /**
4
+ * @param {unknown} value
5
+ * @returns {number | undefined}
6
+ */
7
+ function retryAfterMsOrUndefined(value) {
8
+ if (value === undefined) {
9
+ return undefined;
10
+ }
11
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= MIN_RETRY_AFTER_MS ? value : undefined;
12
+ }
13
+
14
+ class SdkError extends Error {
15
+ /**
16
+ * @param {string} code
17
+ * @param {string} message
18
+ * @param {Readonly<{ retryAfterMs?: unknown, retryable?: unknown }> | null} [details]
19
+ */
20
+ constructor(code, message, details = {}) {
21
+ super(message);
22
+ this.name = "SdkError";
23
+ this.code = code;
24
+ const retryAfterMs = retryAfterMsOrUndefined(details?.retryAfterMs);
25
+ if (retryAfterMs !== undefined) {
26
+ /** @type {number | undefined} */
27
+ this.retryAfterMs = retryAfterMs;
28
+ }
29
+ if (details?.retryable === true) {
30
+ /** @type {boolean | undefined} */
31
+ this.retryable = true;
32
+ }
33
+ }
34
+ }
35
+
36
+ module.exports = { SdkError, retryAfterMsOrUndefined };
@@ -0,0 +1,191 @@
1
+ const { withMetadata } = require("./event-metadata.cjs");
2
+ const { compactMetadata } = require("./log-context.cjs");
3
+ const { SdkError } = require("./sdk-error.cjs");
4
+ const { parseTraceparent } = require("./trace-context.cjs");
5
+ const {
6
+ isRecord,
7
+ optionalEntry,
8
+ requireAllowedValue,
9
+ requireNonEmpty,
10
+ requireSpanId,
11
+ requireTimestamp,
12
+ requireTraceId
13
+ } = require("./validation.cjs");
14
+
15
+ /**
16
+ * @import { Metadata, SpanAttributes, SpanEventSummary, SpanLinkSummary } from "./index.cjs"
17
+ * @import { EventAttributes } from "./event-validation.cjs"
18
+ */
19
+
20
+ const MAX_SPAN_EVENTS = 8;
21
+ const MAX_SPAN_LINKS = 8;
22
+ const NO_ENTRIES = 0;
23
+ const MIN_DURATION_MS = 0;
24
+ /** @type {ReadonlySet<SpanAttributes["status"]>} */
25
+ const SPAN_STATUSES = new Set(["ok", "error"]);
26
+
27
+ /**
28
+ * @param {unknown} metadata
29
+ * @returns {Metadata | undefined}
30
+ */
31
+ function nonEmptyMetadata(metadata) {
32
+ if (metadata === undefined) {
33
+ return undefined;
34
+ }
35
+ const compacted = compactMetadata(metadata);
36
+ return Object.keys(compacted).length > NO_ENTRIES ? compacted : undefined;
37
+ }
38
+
39
+ /**
40
+ * @param {unknown} durationMs
41
+ * @returns {number | undefined}
42
+ */
43
+ function optionalSpanDuration(durationMs) {
44
+ if (durationMs === undefined) {
45
+ return undefined;
46
+ }
47
+ if (typeof durationMs !== "number" || Number.isNaN(durationMs) || durationMs < MIN_DURATION_MS) {
48
+ throw new SdkError("validation_error", "span durationMs must be non-negative");
49
+ }
50
+ return durationMs;
51
+ }
52
+
53
+ /**
54
+ * @param {unknown} list
55
+ * @param {string} label
56
+ * @param {number} maxEntries
57
+ * @returns {readonly unknown[] | undefined}
58
+ */
59
+ function optionalBoundedList(list, label, maxEntries) {
60
+ if (list === undefined) {
61
+ return undefined;
62
+ }
63
+ if (!Array.isArray(list)) {
64
+ throw new SdkError("validation_error", `${label} must be an array`);
65
+ }
66
+ if (list.length > maxEntries) {
67
+ throw new SdkError("validation_error", `${label} must contain at most ${maxEntries} entries`);
68
+ }
69
+ return list.length === NO_ENTRIES ? undefined : list;
70
+ }
71
+
72
+ /**
73
+ * @param {unknown} event
74
+ * @returns {SpanEventSummary}
75
+ */
76
+ function validateSpanEvent(event) {
77
+ if (!isRecord(event)) {
78
+ throw new SdkError("validation_error", "span event must be an object");
79
+ }
80
+ const name = requireNonEmpty("span event name", event["name"]);
81
+ const timestamp = event["timestamp"] === undefined ? undefined : requireTimestamp(event["timestamp"]);
82
+ return {
83
+ name,
84
+ ...optionalEntry("timestamp", timestamp),
85
+ ...optionalEntry("metadata", nonEmptyMetadata(event["metadata"]))
86
+ };
87
+ }
88
+
89
+ /**
90
+ * @param {unknown} events
91
+ * @returns {SpanEventSummary[] | undefined}
92
+ */
93
+ function validateSpanEvents(events) {
94
+ return optionalBoundedList(events, "span events", MAX_SPAN_EVENTS)?.map((event) => validateSpanEvent(event));
95
+ }
96
+
97
+ /**
98
+ * @param {unknown} link
99
+ * @returns {SpanLinkSummary}
100
+ */
101
+ function validateSpanLink(link) {
102
+ if (!isRecord(link)) {
103
+ throw new SdkError("validation_error", "span link must be an object");
104
+ }
105
+ const traceId = requireTraceId(link["traceId"]);
106
+ const spanId = requireSpanId("span link spanId", link["spanId"]);
107
+ const { sampled } = link;
108
+ if (sampled !== undefined && typeof sampled !== "boolean") {
109
+ throw new SdkError("validation_error", "span link sampled must be a boolean");
110
+ }
111
+ return {
112
+ traceId: traceId.toLowerCase(),
113
+
114
+ spanId: spanId.toLowerCase(),
115
+ ...optionalEntry("sampled", sampled),
116
+ ...optionalEntry("metadata", nonEmptyMetadata(link["metadata"]))
117
+ };
118
+ }
119
+
120
+ /**
121
+ * @param {unknown} links
122
+ * @returns {SpanLinkSummary[] | undefined}
123
+ */
124
+ function validateSpanLinks(links) {
125
+ return optionalBoundedList(links, "span links", MAX_SPAN_LINKS)?.map((link) => validateSpanLink(link));
126
+ }
127
+
128
+ /**
129
+ * @param {Readonly<Record<string, unknown>>} attributes
130
+ * @returns {EventAttributes}
131
+ */
132
+ function validateSpan(attributes) {
133
+ const name = requireNonEmpty("span name", attributes["name"]);
134
+ const traceId = requireNonEmpty("span traceId", attributes["traceId"]);
135
+ const spanId = requireNonEmpty("span spanId", attributes["spanId"]);
136
+ const status = requireAllowedValue("span status", attributes["status"], SPAN_STATUSES);
137
+ const parentSpanId = attributes["parentSpanId"] === undefined
138
+ ? undefined
139
+ : requireNonEmpty("span parentSpanId", attributes["parentSpanId"]);
140
+ const durationMs = optionalSpanDuration(attributes["durationMs"]);
141
+ const events = validateSpanEvents(attributes["events"]);
142
+ const links = validateSpanLinks(attributes["links"]);
143
+ return withMetadata({
144
+ name,
145
+
146
+ traceId,
147
+
148
+ spanId,
149
+
150
+ status,
151
+ ...optionalEntry("parentSpanId", parentSpanId),
152
+ ...optionalEntry("durationMs", durationMs),
153
+ ...optionalEntry("events", events),
154
+ ...optionalEntry("links", links)
155
+ }, attributes["metadata"], attributes["context"]);
156
+ }
157
+
158
+ /**
159
+ * @param {unknown} traceparent
160
+ * @param {unknown} attributes
161
+ * @returns {SpanAttributes}
162
+ */
163
+ function spanAttributesFromTraceparent(traceparent, attributes) {
164
+ if (!isRecord(attributes)) {
165
+ throw new SdkError("validation_error", "span attributes must be an object");
166
+ }
167
+ const context = parseTraceparent(traceparent);
168
+ const name = requireNonEmpty("span name", attributes["name"]);
169
+ const spanId = requireSpanId("spanId", attributes["spanId"]);
170
+ const status = requireAllowedValue("span status", attributes["status"], SPAN_STATUSES);
171
+ const durationMs = optionalSpanDuration(attributes["durationMs"]);
172
+ const events = validateSpanEvents(attributes["events"]);
173
+ const links = validateSpanLinks(attributes["links"]);
174
+ return {
175
+ name,
176
+
177
+ traceId: context.traceId,
178
+
179
+ spanId: spanId.toLowerCase(),
180
+
181
+ parentSpanId: context.parentSpanId,
182
+
183
+ status,
184
+ ...optionalEntry("durationMs", durationMs),
185
+ ...optionalEntry("events", events),
186
+ ...optionalEntry("links", links),
187
+ ...optionalEntry("metadata", attributes["metadata"] === undefined ? undefined : compactMetadata(attributes["metadata"]))
188
+ };
189
+ }
190
+
191
+ module.exports = { spanAttributesFromTraceparent, validateSpan };
@@ -0,0 +1,243 @@
1
+ const { isObjectLike } = require("./error-details.cjs");
2
+ const { cloneEvent } = require("./event-clone.cjs");
3
+ const { EVENT_VALIDATORS, isEventType } = require("./event-validation.cjs");
4
+ const { SdkError } = require("./sdk-error.cjs");
5
+ const { utf8ByteLength } = require("./utf8.cjs");
6
+ const { isRecord, requireNonEmpty, requireTimestamp } = require("./validation.cjs");
7
+
8
+ /**
9
+ * @import { EventType, EventValidator, SdkEvent } from "./event-validation.cjs"
10
+ * @import { DeepReadonly } from "./validation.cjs"
11
+ * @typedef {{ event: DeepReadonly<SdkEvent>, eventBytes: number, serializedEvent: string }} StoredRecord
12
+ * @typedef {Readonly<Record<string, unknown>>} UnknownRecord
13
+ * @typedef {{
14
+ * attributes: UnknownRecord,
15
+ * event: UnknownRecord,
16
+ * eventBytes: number,
17
+ * header: { type: EventType, id: string, timestamp: string },
18
+ * serializedEvent: string,
19
+ * validator: EventValidator
20
+ * }} RecordParts
21
+ */
22
+
23
+ const MIN_EVENT_BYTES = 0;
24
+ const CONTEXT_SCHEMA_VERSION = 1;
25
+ const KEY_INDEX = 0;
26
+ const STEP = 1;
27
+
28
+ /**
29
+ * @returns {InstanceType<typeof SdkError>}
30
+ */
31
+ function invalidStoredRecord() {
32
+ return new SdkError("persistence_error", "event store returned an invalid record");
33
+ }
34
+
35
+ /**
36
+ * @param {string} operation
37
+ * @param {unknown} result
38
+ * @returns {unknown}
39
+ */
40
+ function requireSynchronousStoreResult(operation, result) {
41
+ if (isObjectLike(result) && typeof Reflect.get(result, "then") === "function") {
42
+ throw new SdkError("persistence_error", `eventStore.${operation} must complete synchronously`);
43
+ }
44
+ return result;
45
+ }
46
+
47
+ /**
48
+ * @param {readonly [string, unknown]} left
49
+ * @param {readonly [string, unknown]} right
50
+ * @returns {number}
51
+ */
52
+ function byKey(left, right) {
53
+ return left[KEY_INDEX].localeCompare(right[KEY_INDEX]);
54
+ }
55
+
56
+ /**
57
+ * @param {string} _key
58
+ * @param {unknown} entry
59
+ * @returns {unknown}
60
+ */
61
+ function sortedEntries(_key, entry) {
62
+ if (!isRecord(entry)) {
63
+ return entry;
64
+ }
65
+ const entries = Object.entries(entry);
66
+ entries.sort(byKey);
67
+ return Object.fromEntries(entries);
68
+ }
69
+
70
+ /**
71
+ * @param {unknown} value
72
+ * @returns {string}
73
+ */
74
+ function structuralJson(value) {
75
+ return JSON.stringify(value, sortedEntries);
76
+ }
77
+
78
+ /**
79
+ * @param {UnknownRecord} event
80
+ * @param {UnknownRecord} attributes
81
+ * @returns {{ event: UnknownRecord, attributes: UnknownRecord }}
82
+ */
83
+ function recoveredEvent(event, attributes) {
84
+ const fatalAttributes = event["type"] === "issue" && attributes["level"] === "fatal"
85
+ ? { ...attributes, level: "critical" }
86
+ : attributes;
87
+ const { context } = fatalAttributes;
88
+ const recoveredAttributes = isRecord(context) && context["schemaVersion"] === undefined
89
+ ? { ...fatalAttributes, context: { schemaVersion: CONTEXT_SCHEMA_VERSION, ...context } }
90
+ : fatalAttributes;
91
+ return { attributes: recoveredAttributes, event: { ...event, attributes: recoveredAttributes } };
92
+ }
93
+
94
+ /**
95
+ * @param {unknown} eventBytes
96
+ * @param {unknown} serializedEvent
97
+ * @returns {Readonly<{ eventBytes: number, serializedEvent: string }>}
98
+ */
99
+ function storedPayload(eventBytes, serializedEvent) {
100
+ if (typeof eventBytes !== "number" || !Number.isSafeInteger(eventBytes) || eventBytes <= MIN_EVENT_BYTES || typeof serializedEvent !== "string") {
101
+ throw invalidStoredRecord();
102
+ }
103
+ return { eventBytes, serializedEvent };
104
+ }
105
+
106
+ /**
107
+ * @param {UnknownRecord} event
108
+ * @param {Readonly<{ eventBytes: number, serializedEvent: string }>} payload
109
+ * @returns {RecordParts}
110
+ */
111
+ function storedEventParts(event, payload) {
112
+ const { type } = event;
113
+ const validator = isEventType(type) ? EVENT_VALIDATORS.get(type) : undefined;
114
+ if (!isEventType(type) || validator === undefined) {
115
+ throw invalidStoredRecord();
116
+ }
117
+ const header = {
118
+ type,
119
+
120
+ id: requireNonEmpty("event id", event["id"]),
121
+
122
+ timestamp: requireTimestamp(event["timestamp"])
123
+ };
124
+ const { attributes } = event;
125
+ if (!isRecord(attributes)) {
126
+ throw invalidStoredRecord();
127
+ }
128
+ return { ...payload, attributes, event, header, validator };
129
+ }
130
+
131
+ /**
132
+ * @param {UnknownRecord} record
133
+ * @returns {RecordParts}
134
+ */
135
+ function storedRecordParts(record) {
136
+ const { event, eventBytes, serializedEvent } = record;
137
+ if (!isRecord(event)) {
138
+ throw invalidStoredRecord();
139
+ }
140
+ return storedEventParts(event, storedPayload(eventBytes, serializedEvent));
141
+ }
142
+
143
+ /**
144
+ * @param {Readonly<RecordParts["header"]>} header
145
+ * @param {Readonly<{ event: UnknownRecord, attributes: UnknownRecord }>} recovered
146
+ * @param {EventValidator} validator
147
+ * @returns {StoredRecord}
148
+ */
149
+ function normalizedRecord(header, recovered, validator) {
150
+ /** @type {SdkEvent} */
151
+ const normalizedEvent = { ...header, attributes: validator(recovered.attributes) };
152
+ const normalizedSerializedEvent = JSON.stringify(normalizedEvent);
153
+ if (structuralJson(normalizedEvent) !== structuralJson(recovered.event)) {
154
+ throw invalidStoredRecord();
155
+ }
156
+ return {
157
+ event: cloneEvent(normalizedEvent),
158
+ eventBytes: utf8ByteLength(normalizedSerializedEvent),
159
+ serializedEvent: normalizedSerializedEvent
160
+ };
161
+ }
162
+
163
+ /**
164
+ * @param {unknown} record
165
+ * @returns {StoredRecord}
166
+ */
167
+ function normalizeRecord(record) {
168
+ if (!isRecord(record)) {
169
+ throw invalidStoredRecord();
170
+ }
171
+ const { attributes, event, eventBytes, header, serializedEvent, validator } = storedRecordParts(record);
172
+ if (JSON.stringify(event) !== serializedEvent || utf8ByteLength(serializedEvent) !== eventBytes) {
173
+ throw invalidStoredRecord();
174
+ }
175
+ return normalizedRecord(header, recoveredEvent(event, attributes), validator);
176
+ }
177
+
178
+ /**
179
+ * @param {unknown} record
180
+ * @returns {StoredRecord}
181
+ */
182
+ function normalizeStoredRecord(record) {
183
+ try {
184
+ return normalizeRecord(record);
185
+ } catch (error) {
186
+ if (error instanceof SdkError && error.code === "persistence_error") {
187
+ throw error;
188
+ }
189
+ throw invalidStoredRecord();
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @param {string} serializedEvent
195
+ * @returns {unknown}
196
+ */
197
+ function parseStoredEvent(serializedEvent) {
198
+ try {
199
+ /** @type {unknown} */
200
+ const event = JSON.parse(serializedEvent);
201
+ return event;
202
+ } catch {
203
+ throw invalidStoredRecord();
204
+ }
205
+ }
206
+
207
+ /**
208
+ * @param {unknown} serializedEvent
209
+ * @returns {DeepReadonly<SdkEvent>}
210
+ */
211
+ function restoreStoredEvent(serializedEvent) {
212
+ if (typeof serializedEvent !== "string" || serializedEvent === "") {
213
+ throw invalidStoredRecord();
214
+ }
215
+ return normalizeStoredRecord({
216
+ event: parseStoredEvent(serializedEvent),
217
+ eventBytes: utf8ByteLength(serializedEvent),
218
+ serializedEvent
219
+ }).event;
220
+ }
221
+
222
+ /**
223
+ * @param {readonly string[]} expected
224
+ * @param {readonly string[]} actual
225
+ * @returns {boolean}
226
+ */
227
+ function isOrderedSubsequence(expected, actual) {
228
+ let index = 0;
229
+ for (const value of actual) {
230
+ if (value === expected[index]) {
231
+ index += STEP;
232
+ }
233
+ }
234
+ return index === expected.length;
235
+ }
236
+
237
+ module.exports = {
238
+ invalidStoredRecord,
239
+ isOrderedSubsequence,
240
+ normalizeStoredRecord,
241
+ requireSynchronousStoreResult,
242
+ restoreStoredEvent
243
+ };