@odla-ai/chapter 0.25.2 → 0.25.4

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.d.cts CHANGED
@@ -903,8 +903,10 @@ type SubmitResult = {
903
903
  * fields + max lengths, writes the `applications` row at the pipeline's initial
904
904
  * status, and — when the client supplies a `submissionId` — stamps it as the
905
905
  * transaction's mutationId (`join:${submissionId}`) so a double-tap can never
906
- * create two applications (the second returns `duplicate: true`). Idempotency is
907
- * package-enforced. `now`/`newId` are injected (deterministic in tests).
906
+ * create two applications. The application id is derived from that same stable
907
+ * input, so the duplicate response returns the first request's canonical id
908
+ * rather than a fresh, nonexistent one. Idempotency is package-enforced.
909
+ * `now`/`newId` are injected (deterministic in tests).
908
910
  */
909
911
  declare function submitApplication(db: ChapterDb, chapter: Chapter, fields: Record<string, unknown>, opts: {
910
912
  submissionId?: string;
package/dist/index.d.ts CHANGED
@@ -903,8 +903,10 @@ type SubmitResult = {
903
903
  * fields + max lengths, writes the `applications` row at the pipeline's initial
904
904
  * status, and — when the client supplies a `submissionId` — stamps it as the
905
905
  * transaction's mutationId (`join:${submissionId}`) so a double-tap can never
906
- * create two applications (the second returns `duplicate: true`). Idempotency is
907
- * package-enforced. `now`/`newId` are injected (deterministic in tests).
906
+ * create two applications. The application id is derived from that same stable
907
+ * input, so the duplicate response returns the first request's canonical id
908
+ * rather than a fresh, nonexistent one. Idempotency is package-enforced.
909
+ * `now`/`newId` are injected (deterministic in tests).
908
910
  */
909
911
  declare function submitApplication(db: ChapterDb, chapter: Chapter, fields: Record<string, unknown>, opts: {
910
912
  submissionId?: string;
package/dist/index.js CHANGED
@@ -382,6 +382,17 @@ function canApprove(status, p) {
382
382
  return p.approvableFrom.includes(status);
383
383
  }
384
384
 
385
+ // src/application-id.ts
386
+ var APPLICATION_ID_DOMAIN = "odla-ai/chapter/application/v1:";
387
+ async function applicationIdForSubmission(submissionId) {
388
+ const input = new TextEncoder().encode(`${APPLICATION_ID_DOMAIN}${submissionId}`);
389
+ const bytes = new Uint8Array(await crypto.subtle.digest("SHA-256", input)).slice(0, 16);
390
+ bytes[6] = bytes[6] & 15 | 128;
391
+ bytes[8] = bytes[8] & 63 | 128;
392
+ const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
393
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
394
+ }
395
+
385
396
  // src/member.ts
386
397
  var DEFAULT_REQUIRED = ["firstName", "lastName", "email", "referral", "whoYouAre", "message"];
387
398
  var DEFAULT_OPTIONAL = ["referralName", "linkedin", "phone", "state"];
@@ -454,7 +465,7 @@ async function submitApplication(db, chapter, fields, opts) {
454
465
  if (app.requireDisclaimerAck && !acked) {
455
466
  return { ok: false, error: "disclaimerAck is required" };
456
467
  }
457
- const id2 = opts.newId();
468
+ const id2 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
458
469
  const row = { id: id2, status: chapter.pipeline.initial, createdAt: opts.now };
459
470
  for (const f of [...app.required, ...app.optional]) {
460
471
  if (typeof fields[f] === "string") row[f] = fields[f].trim();