@form-engine-ts/storage-d1 8.0.0 → 8.0.2
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/index.cjs +55 -0
- package/dist/index.js +57 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -240,6 +240,61 @@ function createD1Storage(options) {
|
|
|
240
240
|
[stored.id, stored.formId, stored.formVersion, stored.locale, stored.submittedAt, JSON.stringify(stored)]
|
|
241
241
|
);
|
|
242
242
|
},
|
|
243
|
+
async saveSubmissionWithinLimit(submission, maxResponses, saveOptions = {}) {
|
|
244
|
+
if (!Number.isInteger(maxResponses) || maxResponses < 1)
|
|
245
|
+
throw new RangeError("maxResponses must be a positive integer.");
|
|
246
|
+
await ensureReady();
|
|
247
|
+
const stored = parseSubmission(submission, `input "${String(submission?.id)}"`);
|
|
248
|
+
const payloadHash = await (0, import_core.hashFormSubmissionPayload)(stored);
|
|
249
|
+
const idempotent = saveOptions.idempotent === true;
|
|
250
|
+
const existingRow = await boundStatement(
|
|
251
|
+
options.db,
|
|
252
|
+
`SELECT response_id, form_id, form_version, locale, submitted_at, submission_json
|
|
253
|
+
FROM ${responsesTable} WHERE response_id = ? LIMIT 1`,
|
|
254
|
+
[stored.id]
|
|
255
|
+
).first();
|
|
256
|
+
if (existingRow !== null) {
|
|
257
|
+
if (saveOptions.idempotent !== true) throw new Error(`A submission with ID "${stored.id}" already exists.`);
|
|
258
|
+
const existing = parseSubmissionRow(existingRow, 0);
|
|
259
|
+
const existingPayloadHash = await (0, import_core.hashFormSubmissionPayload)(existing);
|
|
260
|
+
if (existingPayloadHash === payloadHash) return { status: "duplicate", submission: existing, payloadHash };
|
|
261
|
+
return { status: "conflict", submissionId: stored.id, payloadHash, existingPayloadHash };
|
|
262
|
+
}
|
|
263
|
+
const inserted = await boundStatement(
|
|
264
|
+
options.db,
|
|
265
|
+
`INSERT INTO ${responsesTable}
|
|
266
|
+
(response_id, form_id, form_version, locale, submitted_at, submission_json)
|
|
267
|
+
SELECT ?, ?, ?, ?, ?, ?
|
|
268
|
+
WHERE (SELECT COUNT(*) FROM ${responsesTable} WHERE form_id = ? AND form_version = ?) < ?
|
|
269
|
+
RETURNING response_id`,
|
|
270
|
+
[
|
|
271
|
+
stored.id,
|
|
272
|
+
stored.formId,
|
|
273
|
+
stored.formVersion,
|
|
274
|
+
stored.locale,
|
|
275
|
+
stored.submittedAt,
|
|
276
|
+
JSON.stringify(stored),
|
|
277
|
+
stored.formId,
|
|
278
|
+
stored.formVersion,
|
|
279
|
+
maxResponses
|
|
280
|
+
]
|
|
281
|
+
).first();
|
|
282
|
+
if (inserted === null) {
|
|
283
|
+
const racedRow = await boundStatement(
|
|
284
|
+
options.db,
|
|
285
|
+
`SELECT response_id, form_id, form_version, locale, submitted_at, submission_json
|
|
286
|
+
FROM ${responsesTable} WHERE response_id = ? LIMIT 1`,
|
|
287
|
+
[stored.id]
|
|
288
|
+
).first();
|
|
289
|
+
if (racedRow === null) return { status: "limit_reached" };
|
|
290
|
+
if (!idempotent) throw new Error(`A submission with ID "${stored.id}" already exists.`);
|
|
291
|
+
const raced = parseSubmissionRow(racedRow, 0);
|
|
292
|
+
const existingPayloadHash = await (0, import_core.hashFormSubmissionPayload)(raced);
|
|
293
|
+
if (existingPayloadHash === payloadHash) return { status: "duplicate", submission: raced, payloadHash };
|
|
294
|
+
return { status: "conflict", submissionId: stored.id, payloadHash, existingPayloadHash };
|
|
295
|
+
}
|
|
296
|
+
if (saveOptions.idempotent === true) return { status: "created", submission: stored, payloadHash };
|
|
297
|
+
},
|
|
243
298
|
async listSubmissions(formId, formVersion, queryOptions) {
|
|
244
299
|
await ensureReady();
|
|
245
300
|
const conditions = ["form_id = ?"];
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
3
|
assertValidFormSchema,
|
|
4
|
-
createFormLifecycleAdapter
|
|
4
|
+
createFormLifecycleAdapter,
|
|
5
|
+
hashFormSubmissionPayload
|
|
5
6
|
} from "@form-engine-ts/core";
|
|
6
7
|
function isRecord(value) {
|
|
7
8
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -219,6 +220,61 @@ function createD1Storage(options) {
|
|
|
219
220
|
[stored.id, stored.formId, stored.formVersion, stored.locale, stored.submittedAt, JSON.stringify(stored)]
|
|
220
221
|
);
|
|
221
222
|
},
|
|
223
|
+
async saveSubmissionWithinLimit(submission, maxResponses, saveOptions = {}) {
|
|
224
|
+
if (!Number.isInteger(maxResponses) || maxResponses < 1)
|
|
225
|
+
throw new RangeError("maxResponses must be a positive integer.");
|
|
226
|
+
await ensureReady();
|
|
227
|
+
const stored = parseSubmission(submission, `input "${String(submission?.id)}"`);
|
|
228
|
+
const payloadHash = await hashFormSubmissionPayload(stored);
|
|
229
|
+
const idempotent = saveOptions.idempotent === true;
|
|
230
|
+
const existingRow = await boundStatement(
|
|
231
|
+
options.db,
|
|
232
|
+
`SELECT response_id, form_id, form_version, locale, submitted_at, submission_json
|
|
233
|
+
FROM ${responsesTable} WHERE response_id = ? LIMIT 1`,
|
|
234
|
+
[stored.id]
|
|
235
|
+
).first();
|
|
236
|
+
if (existingRow !== null) {
|
|
237
|
+
if (saveOptions.idempotent !== true) throw new Error(`A submission with ID "${stored.id}" already exists.`);
|
|
238
|
+
const existing = parseSubmissionRow(existingRow, 0);
|
|
239
|
+
const existingPayloadHash = await hashFormSubmissionPayload(existing);
|
|
240
|
+
if (existingPayloadHash === payloadHash) return { status: "duplicate", submission: existing, payloadHash };
|
|
241
|
+
return { status: "conflict", submissionId: stored.id, payloadHash, existingPayloadHash };
|
|
242
|
+
}
|
|
243
|
+
const inserted = await boundStatement(
|
|
244
|
+
options.db,
|
|
245
|
+
`INSERT INTO ${responsesTable}
|
|
246
|
+
(response_id, form_id, form_version, locale, submitted_at, submission_json)
|
|
247
|
+
SELECT ?, ?, ?, ?, ?, ?
|
|
248
|
+
WHERE (SELECT COUNT(*) FROM ${responsesTable} WHERE form_id = ? AND form_version = ?) < ?
|
|
249
|
+
RETURNING response_id`,
|
|
250
|
+
[
|
|
251
|
+
stored.id,
|
|
252
|
+
stored.formId,
|
|
253
|
+
stored.formVersion,
|
|
254
|
+
stored.locale,
|
|
255
|
+
stored.submittedAt,
|
|
256
|
+
JSON.stringify(stored),
|
|
257
|
+
stored.formId,
|
|
258
|
+
stored.formVersion,
|
|
259
|
+
maxResponses
|
|
260
|
+
]
|
|
261
|
+
).first();
|
|
262
|
+
if (inserted === null) {
|
|
263
|
+
const racedRow = await boundStatement(
|
|
264
|
+
options.db,
|
|
265
|
+
`SELECT response_id, form_id, form_version, locale, submitted_at, submission_json
|
|
266
|
+
FROM ${responsesTable} WHERE response_id = ? LIMIT 1`,
|
|
267
|
+
[stored.id]
|
|
268
|
+
).first();
|
|
269
|
+
if (racedRow === null) return { status: "limit_reached" };
|
|
270
|
+
if (!idempotent) throw new Error(`A submission with ID "${stored.id}" already exists.`);
|
|
271
|
+
const raced = parseSubmissionRow(racedRow, 0);
|
|
272
|
+
const existingPayloadHash = await hashFormSubmissionPayload(raced);
|
|
273
|
+
if (existingPayloadHash === payloadHash) return { status: "duplicate", submission: raced, payloadHash };
|
|
274
|
+
return { status: "conflict", submissionId: stored.id, payloadHash, existingPayloadHash };
|
|
275
|
+
}
|
|
276
|
+
if (saveOptions.idempotent === true) return { status: "created", submission: stored, payloadHash };
|
|
277
|
+
},
|
|
222
278
|
async listSubmissions(formId, formVersion, queryOptions) {
|
|
223
279
|
await ensureReady();
|
|
224
280
|
const conditions = ["form_id = ?"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/storage-d1",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"typescript"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@form-engine-ts/core": "8.0.
|
|
44
|
+
"@form-engine-ts/core": "8.0.2"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@cloudflare/workers-types": "^4.20240000.0"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@cloudflare/workers-types": "^4.20240000.0",
|
|
56
|
-
"@form-engine-ts/storage": "8.0.
|
|
56
|
+
"@form-engine-ts/storage": "8.0.2"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean --external @form-engine-ts/core",
|