@gmickel/gno 1.27.0 → 1.28.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.
- package/README.md +2 -2
- package/assets/skill/SKILL.md +26 -1
- package/browser-extension/artifacts/{gno-browser-clipper-v1.27.0.zip → gno-browser-clipper-v1.28.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.28.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +2 -2
- package/spec/cli.md +41 -4
- package/spec/db/schema.sql +12 -0
- package/spec/mcp.md +5 -0
- package/spec/output-schemas/ask.schema.json +125 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +94 -1
- package/spec/output-schemas/get.schema.json +94 -0
- package/spec/output-schemas/mcp-job-status.schema.json +28 -0
- package/spec/output-schemas/multi-get.schema.json +128 -0
- package/spec/output-schemas/record-import.schema.json +193 -0
- package/spec/output-schemas/search-result.schema.json +94 -0
- package/spec/output-schemas/search-results.schema.json +125 -0
- package/spec/project-profile.schema.json +66 -0
- package/src/app/context-format.ts +1 -1
- package/src/cli/commands/get.ts +12 -2
- package/src/cli/commands/index-cmd.ts +13 -0
- package/src/cli/commands/multi-get.ts +9 -2
- package/src/cli/commands/shared.ts +28 -0
- package/src/cli/commands/update.ts +5 -0
- package/src/cli/program.ts +4 -0
- package/src/config/project-profile.ts +2 -0
- package/src/config/types.ts +16 -0
- package/src/converters/adapters/browser-export/adapter.ts +199 -0
- package/src/converters/adapters/browser-export/formats.ts +358 -0
- package/src/converters/adapters/email/adapter.ts +429 -0
- package/src/converters/adapters/email/html.ts +162 -0
- package/src/converters/adapters/email/mime.ts +454 -0
- package/src/converters/adapters/email/parameters.ts +77 -0
- package/src/converters/adapters/ical/adapter.ts +475 -0
- package/src/converters/adapters/ical/recurrence.ts +186 -0
- package/src/converters/adapters/jsonl/adapter.ts +238 -0
- package/src/converters/adapters/jsonl/config.ts +105 -0
- package/src/converters/adapters/shared/html-text.ts +165 -0
- package/src/converters/adapters/shared/record-utils.ts +79 -0
- package/src/converters/adapters/shared/utf8-lines.ts +141 -0
- package/src/converters/adapters/transcript/adapter.ts +295 -0
- package/src/converters/adapters/transcript/json.ts +184 -0
- package/src/converters/adapters/transcript/model.ts +171 -0
- package/src/converters/adapters/transcript/text.ts +58 -0
- package/src/converters/adapters/transcript/timed.ts +152 -0
- package/src/converters/index.ts +11 -1
- package/src/converters/mime.ts +8 -0
- package/src/converters/pipeline.ts +15 -1
- package/src/converters/registry.ts +37 -1
- package/src/converters/types.ts +136 -0
- package/src/core/context-capsule-schema.ts +87 -0
- package/src/core/context-capsule.ts +20 -0
- package/src/core/context-evidence.ts +7 -1
- package/src/core/document-capabilities.ts +12 -0
- package/src/core/project-profile-apply-state.ts +5 -0
- package/src/core/project-profile.ts +4 -0
- package/src/core/record-metadata.ts +49 -0
- package/src/ingestion/record-adapter-canonical.ts +433 -0
- package/src/ingestion/record-adapter.ts +437 -0
- package/src/ingestion/record-container.ts +688 -0
- package/src/ingestion/record-path.ts +20 -0
- package/src/ingestion/record-sync.ts +70 -0
- package/src/ingestion/sync.ts +228 -36
- package/src/ingestion/types.ts +69 -1
- package/src/ingestion/walker.ts +31 -11
- package/src/mcp/tools/get.ts +10 -2
- package/src/mcp/tools/multi-get.ts +9 -2
- package/src/mcp/tools/workspace-write.ts +2 -0
- package/src/pipeline/filters.ts +1 -1
- package/src/pipeline/graph-retrieval.ts +7 -4
- package/src/pipeline/hybrid.ts +7 -4
- package/src/pipeline/result-context.ts +12 -4
- package/src/pipeline/search.ts +11 -4
- package/src/pipeline/types.ts +3 -0
- package/src/pipeline/vsearch.ts +26 -8
- package/src/sdk/documents.ts +13 -5
- package/src/serve/browse-tree.ts +4 -2
- package/src/serve/routes/api.ts +63 -65
- package/src/store/migrations/022-record-export-lineage.ts +42 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/sqlite/adapter.ts +114 -7
- package/src/store/types.ts +40 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.27.0.zip.sha256 +0 -1
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RecordAdapter,
|
|
3
|
+
RecordAdapterFailure,
|
|
4
|
+
RecordAdapterFailureCode,
|
|
5
|
+
RecordAdapterEvent,
|
|
6
|
+
RecordAdapterInput,
|
|
7
|
+
} from "../converters/types";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
type AccountedCanonicalRecord,
|
|
11
|
+
adapterFailureMessage,
|
|
12
|
+
canonicalRecord,
|
|
13
|
+
type CanonicalRecord,
|
|
14
|
+
normalizeRecordAdapterIdentity,
|
|
15
|
+
recordKeyFor,
|
|
16
|
+
recordAdapterFingerprint,
|
|
17
|
+
safeFailure,
|
|
18
|
+
} from "./record-adapter-canonical";
|
|
19
|
+
|
|
20
|
+
export type { CanonicalRecord } from "./record-adapter-canonical";
|
|
21
|
+
export {
|
|
22
|
+
recordAdapterFingerprint,
|
|
23
|
+
recordKeyFor,
|
|
24
|
+
} from "./record-adapter-canonical";
|
|
25
|
+
|
|
26
|
+
export interface RecordAdapterRunResult {
|
|
27
|
+
adapterId: string;
|
|
28
|
+
adapterVersion: string;
|
|
29
|
+
adapterFingerprint: string;
|
|
30
|
+
records: CanonicalRecord[];
|
|
31
|
+
failures: RecordAdapterFailure[];
|
|
32
|
+
failedRecordKeys: string[];
|
|
33
|
+
snapshotState: "complete" | "partial";
|
|
34
|
+
authoritative: boolean;
|
|
35
|
+
stoppedByCap: boolean;
|
|
36
|
+
sourceBytesRead: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class SourceLimitError extends Error {
|
|
40
|
+
constructor() {
|
|
41
|
+
super("Record adapter source exceeded its byte limit.");
|
|
42
|
+
this.name = "SourceLimitError";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const TIMEOUT = Symbol("record-adapter-timeout");
|
|
47
|
+
|
|
48
|
+
const nextBeforeDeadline = async <T>(
|
|
49
|
+
iterator: AsyncIterator<T>,
|
|
50
|
+
deadlineMs: number
|
|
51
|
+
): Promise<IteratorResult<T> | typeof TIMEOUT> => {
|
|
52
|
+
const remainingMs = deadlineMs - Date.now();
|
|
53
|
+
if (remainingMs <= 0) return TIMEOUT;
|
|
54
|
+
return await new Promise<IteratorResult<T> | typeof TIMEOUT>(
|
|
55
|
+
(resolve, reject) => {
|
|
56
|
+
const timer = setTimeout(() => resolve(TIMEOUT), remainingMs);
|
|
57
|
+
void iterator.next().then(
|
|
58
|
+
(value) => {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
resolve(value);
|
|
61
|
+
},
|
|
62
|
+
(error: unknown) => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
reject(error);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const closeIterator = async (
|
|
72
|
+
iterator: AsyncIterator<unknown>
|
|
73
|
+
): Promise<boolean> => {
|
|
74
|
+
if (!iterator.return) return true;
|
|
75
|
+
try {
|
|
76
|
+
await iterator.return();
|
|
77
|
+
return true;
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** Consume one adapter snapshot while centrally enforcing every global cap. */
|
|
84
|
+
export async function runRecordAdapter(
|
|
85
|
+
adapter: RecordAdapter,
|
|
86
|
+
input: RecordAdapterInput
|
|
87
|
+
): Promise<RecordAdapterRunResult> {
|
|
88
|
+
const adapterIdentity = normalizeRecordAdapterIdentity(adapter);
|
|
89
|
+
const adapterFingerprint = recordAdapterFingerprint(adapter);
|
|
90
|
+
const accepted = new Map<string, AccountedCanonicalRecord>();
|
|
91
|
+
const seenKeys = new Set<string>();
|
|
92
|
+
const failedRecordKeys = new Set<string>();
|
|
93
|
+
const failures: RecordAdapterFailure[] = [];
|
|
94
|
+
let totalChars = 0;
|
|
95
|
+
let sourceBytesRead = 0;
|
|
96
|
+
let sourceOpened = false;
|
|
97
|
+
let sourceLimitExceeded = false;
|
|
98
|
+
let sourceReadFailed = false;
|
|
99
|
+
let stoppedByCap = false;
|
|
100
|
+
let terminalCount = 0;
|
|
101
|
+
let terminalState: "complete" | "partial" = "partial";
|
|
102
|
+
let iteratorCloseFailed = false;
|
|
103
|
+
let stoppedByFailureLimit = false;
|
|
104
|
+
let timedOut = false;
|
|
105
|
+
const abortController = new AbortController();
|
|
106
|
+
const deadlineMs = Date.now() + (input.limits.timeoutMs ?? 60_000);
|
|
107
|
+
|
|
108
|
+
const appendFailure = (failure: RecordAdapterFailure): boolean => {
|
|
109
|
+
if (stoppedByFailureLimit) return true;
|
|
110
|
+
if (input.limits.maxFailures > 0) failures.push(failure);
|
|
111
|
+
if (failures.length < input.limits.maxFailures) return false;
|
|
112
|
+
failures.push(
|
|
113
|
+
safeFailure(
|
|
114
|
+
input,
|
|
115
|
+
"FAILURE_LIMIT",
|
|
116
|
+
"Record snapshot reached its failure limit.",
|
|
117
|
+
true
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
stoppedByFailureLimit = true;
|
|
121
|
+
stoppedByCap = true;
|
|
122
|
+
return true;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const boundedOpen = (): AsyncIterable<Uint8Array> => ({
|
|
126
|
+
[Symbol.asyncIterator](): AsyncIterator<Uint8Array> {
|
|
127
|
+
if (sourceOpened)
|
|
128
|
+
throw new Error("Record adapter source may be opened once.");
|
|
129
|
+
sourceOpened = true;
|
|
130
|
+
let iterator: AsyncIterator<Uint8Array>;
|
|
131
|
+
try {
|
|
132
|
+
iterator = input.open(abortController.signal)[Symbol.asyncIterator]();
|
|
133
|
+
} catch {
|
|
134
|
+
sourceReadFailed = true;
|
|
135
|
+
throw new Error("Record adapter source could not be opened.");
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
async next() {
|
|
139
|
+
let next: IteratorResult<Uint8Array>;
|
|
140
|
+
try {
|
|
141
|
+
next = await iterator.next();
|
|
142
|
+
} catch {
|
|
143
|
+
sourceReadFailed = true;
|
|
144
|
+
throw new Error("Record adapter source read failed.");
|
|
145
|
+
}
|
|
146
|
+
if (next.done) return next;
|
|
147
|
+
sourceBytesRead += next.value.byteLength;
|
|
148
|
+
if (sourceBytesRead > input.limits.maxSourceBytes) {
|
|
149
|
+
sourceLimitExceeded = true;
|
|
150
|
+
iteratorCloseFailed ||= !(await closeIterator(iterator));
|
|
151
|
+
throw new SourceLimitError();
|
|
152
|
+
}
|
|
153
|
+
return next;
|
|
154
|
+
},
|
|
155
|
+
async return() {
|
|
156
|
+
iteratorCloseFailed ||= !(await closeIterator(iterator));
|
|
157
|
+
return { done: true, value: undefined };
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
let iterator: AsyncIterator<RecordAdapterEvent> | undefined;
|
|
164
|
+
try {
|
|
165
|
+
iterator = adapter
|
|
166
|
+
.records({
|
|
167
|
+
...input,
|
|
168
|
+
open: boundedOpen,
|
|
169
|
+
signal: abortController.signal,
|
|
170
|
+
})
|
|
171
|
+
[Symbol.asyncIterator]();
|
|
172
|
+
while (true) {
|
|
173
|
+
const next = await nextBeforeDeadline(iterator, deadlineMs);
|
|
174
|
+
if (next === TIMEOUT) {
|
|
175
|
+
timedOut = true;
|
|
176
|
+
stoppedByCap = true;
|
|
177
|
+
abortController.abort();
|
|
178
|
+
failures.push(
|
|
179
|
+
safeFailure(
|
|
180
|
+
input,
|
|
181
|
+
"TIMEOUT",
|
|
182
|
+
"Record adapter exceeded its time limit.",
|
|
183
|
+
true
|
|
184
|
+
)
|
|
185
|
+
);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
if (next.done) break;
|
|
189
|
+
const event = next.value;
|
|
190
|
+
if (event.type === "snapshot") {
|
|
191
|
+
terminalCount += 1;
|
|
192
|
+
if (terminalCount === 1) terminalState = event.state;
|
|
193
|
+
else {
|
|
194
|
+
const reachedFailureLimit = appendFailure(
|
|
195
|
+
safeFailure(
|
|
196
|
+
input,
|
|
197
|
+
"INVALID_SNAPSHOT",
|
|
198
|
+
"Record adapter emitted more than one terminal snapshot.",
|
|
199
|
+
false
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
if (reachedFailureLimit) break;
|
|
203
|
+
}
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (terminalCount > 0) {
|
|
207
|
+
const reachedFailureLimit = appendFailure(
|
|
208
|
+
safeFailure(
|
|
209
|
+
input,
|
|
210
|
+
"INVALID_SNAPSHOT",
|
|
211
|
+
"Record adapter emitted data after its terminal snapshot.",
|
|
212
|
+
false
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
if (reachedFailureLimit) break;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (event.type === "failure") {
|
|
219
|
+
const reachedFailureLimit = appendFailure(
|
|
220
|
+
safeFailure(
|
|
221
|
+
input,
|
|
222
|
+
event.failure.code,
|
|
223
|
+
adapterFailureMessage(event.failure.code),
|
|
224
|
+
event.failure.retryable,
|
|
225
|
+
event.failure.stableId,
|
|
226
|
+
event.failure.sourceLocator
|
|
227
|
+
)
|
|
228
|
+
);
|
|
229
|
+
if (reachedFailureLimit) break;
|
|
230
|
+
} else {
|
|
231
|
+
let record: AccountedCanonicalRecord;
|
|
232
|
+
try {
|
|
233
|
+
record = canonicalRecord(adapter, event.record);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const code: RecordAdapterFailureCode =
|
|
236
|
+
error instanceof Error && error.message === "invalid source locator"
|
|
237
|
+
? "INVALID_LOCATOR"
|
|
238
|
+
: error instanceof Error &&
|
|
239
|
+
error.message === "invalid source hash"
|
|
240
|
+
? "INVALID_SOURCE_HASH"
|
|
241
|
+
: error instanceof Error &&
|
|
242
|
+
error.message === "record metadata out of bounds"
|
|
243
|
+
? "RECORD_TOO_LARGE"
|
|
244
|
+
: "MISSING_ID";
|
|
245
|
+
const reachedFailureLimit = appendFailure(
|
|
246
|
+
safeFailure(
|
|
247
|
+
input,
|
|
248
|
+
code,
|
|
249
|
+
"Record identity or provenance was invalid.",
|
|
250
|
+
false,
|
|
251
|
+
event.record.stableId
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
if (reachedFailureLimit) break;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (seenKeys.has(record.recordKey)) {
|
|
258
|
+
const previous = accepted.get(record.recordKey);
|
|
259
|
+
if (previous) totalChars -= previous.accountingChars;
|
|
260
|
+
accepted.delete(record.recordKey);
|
|
261
|
+
failedRecordKeys.add(record.recordKey);
|
|
262
|
+
const reachedFailureLimit = appendFailure(
|
|
263
|
+
safeFailure(
|
|
264
|
+
input,
|
|
265
|
+
"DUPLICATE_ID",
|
|
266
|
+
"Duplicate stable record identity; every duplicate was isolated.",
|
|
267
|
+
false,
|
|
268
|
+
record.stableId,
|
|
269
|
+
record.sourceLocator
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
if (reachedFailureLimit) break;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
seenKeys.add(record.recordKey);
|
|
276
|
+
if (
|
|
277
|
+
record.markdown.length > input.limits.maxRecordChars ||
|
|
278
|
+
record.metadataChars > input.limits.maxMetadataChars
|
|
279
|
+
) {
|
|
280
|
+
const reachedFailureLimit = appendFailure(
|
|
281
|
+
safeFailure(
|
|
282
|
+
input,
|
|
283
|
+
"RECORD_TOO_LARGE",
|
|
284
|
+
"Record exceeded the canonical character limit.",
|
|
285
|
+
false,
|
|
286
|
+
record.stableId,
|
|
287
|
+
record.sourceLocator
|
|
288
|
+
)
|
|
289
|
+
);
|
|
290
|
+
failedRecordKeys.add(record.recordKey);
|
|
291
|
+
if (reachedFailureLimit) break;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (accepted.size >= input.limits.maxRecords) {
|
|
295
|
+
failures.push(
|
|
296
|
+
safeFailure(
|
|
297
|
+
input,
|
|
298
|
+
"RECORD_LIMIT",
|
|
299
|
+
"Record snapshot exceeded its record limit.",
|
|
300
|
+
true
|
|
301
|
+
)
|
|
302
|
+
);
|
|
303
|
+
stoppedByCap = true;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
if (totalChars + record.accountingChars > input.limits.maxTotalChars) {
|
|
307
|
+
failures.push(
|
|
308
|
+
safeFailure(
|
|
309
|
+
input,
|
|
310
|
+
"RECORD_LIMIT",
|
|
311
|
+
"Record snapshot exceeded its total character limit.",
|
|
312
|
+
true
|
|
313
|
+
)
|
|
314
|
+
);
|
|
315
|
+
stoppedByCap = true;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
accepted.set(record.recordKey, record);
|
|
319
|
+
totalChars += record.accountingChars;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} catch (error) {
|
|
323
|
+
failures.push(
|
|
324
|
+
safeFailure(
|
|
325
|
+
input,
|
|
326
|
+
error instanceof SourceLimitError
|
|
327
|
+
? "SOURCE_TOO_LARGE"
|
|
328
|
+
: "ADAPTER_FAILURE",
|
|
329
|
+
error instanceof SourceLimitError
|
|
330
|
+
? error.message
|
|
331
|
+
: "Record adapter stopped before a terminal snapshot.",
|
|
332
|
+
true
|
|
333
|
+
)
|
|
334
|
+
);
|
|
335
|
+
stoppedByCap ||= error instanceof SourceLimitError;
|
|
336
|
+
} finally {
|
|
337
|
+
if (iterator) {
|
|
338
|
+
if (timedOut) {
|
|
339
|
+
void closeIterator(iterator);
|
|
340
|
+
} else {
|
|
341
|
+
iteratorCloseFailed ||= !(await closeIterator(iterator));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (iteratorCloseFailed) {
|
|
347
|
+
failures.push(
|
|
348
|
+
safeFailure(
|
|
349
|
+
input,
|
|
350
|
+
"ADAPTER_FAILURE",
|
|
351
|
+
"Record adapter iterator did not close cleanly.",
|
|
352
|
+
true
|
|
353
|
+
)
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (
|
|
358
|
+
sourceLimitExceeded &&
|
|
359
|
+
!failures.some((failure) => failure.code === "SOURCE_TOO_LARGE")
|
|
360
|
+
) {
|
|
361
|
+
failures.push(
|
|
362
|
+
safeFailure(
|
|
363
|
+
input,
|
|
364
|
+
"SOURCE_TOO_LARGE",
|
|
365
|
+
"Record adapter source exceeded its byte limit.",
|
|
366
|
+
true
|
|
367
|
+
)
|
|
368
|
+
);
|
|
369
|
+
stoppedByCap = true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (
|
|
373
|
+
sourceReadFailed &&
|
|
374
|
+
!failures.some((failure) => failure.code === "ADAPTER_FAILURE")
|
|
375
|
+
) {
|
|
376
|
+
failures.push(
|
|
377
|
+
safeFailure(
|
|
378
|
+
input,
|
|
379
|
+
"ADAPTER_FAILURE",
|
|
380
|
+
"Record adapter source could not be read completely.",
|
|
381
|
+
true
|
|
382
|
+
)
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (terminalCount !== 1) {
|
|
387
|
+
failures.push(
|
|
388
|
+
safeFailure(
|
|
389
|
+
input,
|
|
390
|
+
"INVALID_SNAPSHOT",
|
|
391
|
+
"Record adapter did not emit exactly one terminal snapshot.",
|
|
392
|
+
true
|
|
393
|
+
)
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const authoritative =
|
|
398
|
+
terminalCount === 1 &&
|
|
399
|
+
terminalState === "complete" &&
|
|
400
|
+
failures.length === 0 &&
|
|
401
|
+
!stoppedByCap;
|
|
402
|
+
const failureLimit = Math.max(0, input.limits.maxFailures);
|
|
403
|
+
const boundedFailures =
|
|
404
|
+
failures.length <= failureLimit
|
|
405
|
+
? failures
|
|
406
|
+
: failureLimit === 0
|
|
407
|
+
? []
|
|
408
|
+
: [
|
|
409
|
+
...failures.slice(0, failureLimit - 1),
|
|
410
|
+
safeFailure(
|
|
411
|
+
input,
|
|
412
|
+
"FAILURE_LIMIT",
|
|
413
|
+
"Record snapshot reached its failure limit.",
|
|
414
|
+
true
|
|
415
|
+
),
|
|
416
|
+
];
|
|
417
|
+
return {
|
|
418
|
+
adapterId: adapterIdentity.id,
|
|
419
|
+
adapterVersion: adapterIdentity.version,
|
|
420
|
+
adapterFingerprint,
|
|
421
|
+
records: [...accepted.values()]
|
|
422
|
+
.sort((left, right) => left.recordKey.localeCompare(right.recordKey))
|
|
423
|
+
.map(
|
|
424
|
+
({
|
|
425
|
+
accountingChars: _accountingChars,
|
|
426
|
+
metadataChars: _metadataChars,
|
|
427
|
+
...record
|
|
428
|
+
}) => record
|
|
429
|
+
),
|
|
430
|
+
failures: boundedFailures,
|
|
431
|
+
failedRecordKeys: [...failedRecordKeys].sort(),
|
|
432
|
+
snapshotState: terminalState,
|
|
433
|
+
authoritative,
|
|
434
|
+
stoppedByCap,
|
|
435
|
+
sourceBytesRead,
|
|
436
|
+
};
|
|
437
|
+
}
|