@enbox/dwn-sdk-js 0.3.6 → 0.3.7
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/dist/browser.mjs +1 -1
- package/dist/browser.mjs.map +3 -3
- package/dist/esm/src/handlers/records-write.js +33 -9
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +15 -0
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +1 -0
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/handlers/records-write.ts +59 -9
|
@@ -15,6 +15,7 @@ export declare class RecordsWriteHandler implements MethodHandler {
|
|
|
15
15
|
*/
|
|
16
16
|
cloneAndAddEncodedData(message: RecordsWriteMessage, dataBytes: Uint8Array): Promise<RecordsQueryReplyEntry>;
|
|
17
17
|
private processMessageWithDataStream;
|
|
18
|
+
private existingInitialWriteLacksData;
|
|
18
19
|
private processMessageWithoutDataStream;
|
|
19
20
|
/**
|
|
20
21
|
* Validates the expected `dataCid` and `dataSize` in the descriptor vs the received data.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records-write.d.ts","sourceRoot":"","sources":["../../../../src/handlers/records-write.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAqB7F,KAAK,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAC;IAAC,UAAU,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;CAAC,CAAC;AAE5G,qBAAa,mBAAoB,YAAW,aAAa;IAE3C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,mBAAmB;IAEzC,MAAM,CAAC,EAClB,MAAM,EACN,OAAO,EACP,UAAU,EACX,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"records-write.d.ts","sourceRoot":"","sources":["../../../../src/handlers/records-write.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAqB7F,KAAK,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAC;IAAC,UAAU,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;CAAC,CAAC;AAE5G,qBAAa,mBAAoB,YAAW,aAAa;IAE3C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,mBAAmB;IAEzC,MAAM,CAAC,EAClB,MAAM,EACN,OAAO,EACP,UAAU,EACX,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA+N7C;;OAEG;IACU,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,UAAU,GAAE,OAAO,CAAC,sBAAsB,CAAC;YAM1G,4BAA4B;YA8C5B,6BAA6B;YA2B7B,+BAA+B;IAyC7C;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAqBpC;;;;;;OAMG;YACW,qBAAqB;YA4ErB,qBAAqB;CAqCpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records-write.spec.d.ts","sourceRoot":"","sources":["../../../../tests/handlers/records-write.spec.ts"],"names":[],"mappings":"AAmDA,wBAAgB,uBAAuB,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"records-write.spec.d.ts","sourceRoot":"","sources":["../../../../tests/handlers/records-write.spec.ts"],"names":[],"mappings":"AAmDA,wBAAgB,uBAAuB,IAAI,IAAI,CA6gJ9C"}
|
package/package.json
CHANGED
|
@@ -58,6 +58,31 @@ export class RecordsWriteHandler implements MethodHandler {
|
|
|
58
58
|
};
|
|
59
59
|
const { messages: existingMessages } = await this.deps.messageStore.query(tenant, [ query ]);
|
|
60
60
|
|
|
61
|
+
// If the exact same message already exists, return 409 immediately.
|
|
62
|
+
// This prevents duplicate delivery races from re-processing large data
|
|
63
|
+
// streams and hitting unique constraints in SQL-backed data stores.
|
|
64
|
+
//
|
|
65
|
+
// Exception: an initial write may have been stored earlier without data
|
|
66
|
+
// (204). A later delivery of the same message with data must be allowed
|
|
67
|
+
// to complete the record.
|
|
68
|
+
const incomingCid = await Message.getCid(message);
|
|
69
|
+
for (const existingMessage of existingMessages) {
|
|
70
|
+
if (await Message.getCid(existingMessage) !== incomingCid) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const canCompleteMissingData = await this.existingInitialWriteLacksData(
|
|
75
|
+
tenant,
|
|
76
|
+
existingMessage as RecordsWriteMessage,
|
|
77
|
+
message,
|
|
78
|
+
dataStream !== undefined,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (!canCompleteMissingData) {
|
|
82
|
+
return { status: { code: 409, detail: 'Conflict' } };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
61
86
|
// if the incoming write is not the initial write, then it must not modify any immutable properties defined by the initial write
|
|
62
87
|
const newMessageIsInitialWrite = await recordsWrite.isInitialWrite();
|
|
63
88
|
let initialWrite: RecordsWriteMessage | undefined;
|
|
@@ -101,15 +126,13 @@ export class RecordsWriteHandler implements MethodHandler {
|
|
|
101
126
|
// message is an initial write that lacks both inline encodedData and
|
|
102
127
|
// DataStore data — indicating it was stored without data.
|
|
103
128
|
let existingLacksData = false;
|
|
104
|
-
if (newestExistingMessage
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
existingLacksData = !hasInlineData && !hasStoredData;
|
|
112
|
-
}
|
|
129
|
+
if (newestExistingMessage) {
|
|
130
|
+
existingLacksData = await this.existingInitialWriteLacksData(
|
|
131
|
+
tenant,
|
|
132
|
+
newestExistingMessage as RecordsWriteMessage,
|
|
133
|
+
message,
|
|
134
|
+
dataStream !== undefined,
|
|
135
|
+
);
|
|
113
136
|
}
|
|
114
137
|
|
|
115
138
|
if (!existingLacksData) {
|
|
@@ -288,6 +311,33 @@ export class RecordsWriteHandler implements MethodHandler {
|
|
|
288
311
|
return messageWithOptionalEncodedData;
|
|
289
312
|
}
|
|
290
313
|
|
|
314
|
+
private async existingInitialWriteLacksData(
|
|
315
|
+
tenant: string,
|
|
316
|
+
existingMessage: RecordsWriteMessage,
|
|
317
|
+
incomingMessage: RecordsWriteMessage,
|
|
318
|
+
incomingHasData: boolean,
|
|
319
|
+
): Promise<boolean> {
|
|
320
|
+
if (!incomingHasData) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const isInitial = await RecordsWrite.isInitialWrite(existingMessage);
|
|
325
|
+
if (!isInitial) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const hasInlineData = !!(existingMessage as RecordsQueryReplyEntry).encodedData;
|
|
330
|
+
const hasStoredData = this.deps.dataStore
|
|
331
|
+
? !!(await this.deps.dataStore.get(
|
|
332
|
+
tenant,
|
|
333
|
+
existingMessage.recordId,
|
|
334
|
+
incomingMessage.descriptor.dataCid,
|
|
335
|
+
))
|
|
336
|
+
: false;
|
|
337
|
+
|
|
338
|
+
return !hasInlineData && !hasStoredData;
|
|
339
|
+
}
|
|
340
|
+
|
|
291
341
|
private async processMessageWithoutDataStream(
|
|
292
342
|
tenant: string,
|
|
293
343
|
message: RecordsWriteMessage,
|