@marktiderman/genesis-forms 0.9.0 → 0.9.1

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.
@@ -2095,7 +2095,7 @@ function padded(text) {
2095
2095
  return fromBase64(text + "=".repeat((4 - text.length % 4) % 4));
2096
2096
  }
2097
2097
 
2098
- // src/destinations/runner.ts
2098
+ // ../notifications/src/runner/index.ts
2099
2099
  var DEFAULT_MAX_ATTEMPTS = 8;
2100
2100
  var DEFAULT_MAX_DEFERRALS = 24;
2101
2101
  var DEFAULT_BATCH_SIZE = 25;
@@ -2104,8 +2104,11 @@ function defaultBackoff(attempt, random) {
2104
2104
  const r = typeof random === "function" ? random() : Math.random();
2105
2105
  return Math.round(nominal * (0.8 + 0.4 * Math.min(1, Math.max(0, r))));
2106
2106
  }
2107
- function deliveryOutcome(result, attempt, options = {}) {
2108
- if (result.ok) return result.notSent ? { status: "skipped", reason: result.notSent.slice(0, 500) } : { status: "sent" };
2107
+ function jobOutcome(result, attempt, options = {}) {
2108
+ if (result.ok) {
2109
+ if (result.notSent) return { status: "skipped", reason: result.notSent.slice(0, 500) };
2110
+ return result.alreadyThere ? { status: "sent", alreadyThere: true } : { status: "sent" };
2111
+ }
2109
2112
  const http = result.status !== void 0 ? { httpStatus: result.status } : {};
2110
2113
  if (result.disable) return { status: "failed", error: result.error, ...http, disable: true };
2111
2114
  if (!result.retryable) return { status: "failed", error: result.error, ...http };
@@ -2119,14 +2122,109 @@ function deliveryOutcome(result, attempt, options = {}) {
2119
2122
  const seconds = Math.max(1, Math.ceil((options.backoff ?? defaultBackoff)(attempt)));
2120
2123
  return { status: "retry", error: result.error, retryAt: new Date(now + seconds * 1e3), retryAfterSeconds: seconds, counted: true, ...http };
2121
2124
  }
2122
- function createDeliveryRunner(options) {
2125
+ function failureOf(error) {
2126
+ const e = error;
2127
+ if (e && typeof e === "object" && typeof e.retryable === "boolean" && typeof e.message === "string") {
2128
+ return {
2129
+ ok: false,
2130
+ retryable: e.disable === true ? false : e.retryable,
2131
+ error: e.message.slice(0, 500),
2132
+ ...typeof e.retryAfterSeconds === "number" ? { retryAfterSeconds: e.retryAfterSeconds } : {},
2133
+ ...typeof e.status === "number" ? { status: e.status } : {},
2134
+ ...e.disable === true ? { disable: true } : {}
2135
+ };
2136
+ }
2137
+ const timeout = e && typeof e === "object" && (e.name === "TimeoutError" || e.name === "AbortError");
2138
+ return { ok: false, retryable: true, error: timeout ? "Timed out" : "Unexpected error" };
2139
+ }
2140
+ function createRunner(options) {
2123
2141
  const log = (message) => {
2124
2142
  try {
2125
2143
  options.log?.(message);
2126
2144
  } catch {
2127
2145
  }
2128
2146
  };
2147
+ const label = (job) => options.label?.(job) ?? `Job ${job.id}`;
2148
+ const recordLabel = (job) => options.recordLabel?.(job) ?? `job ${job.id}`;
2129
2149
  const now = () => options.now?.() ?? Date.now();
2150
+ return {
2151
+ async run(runOptions = {}) {
2152
+ const report = { claimed: 0, sent: 0, filtered: 0, skipped: 0, retrying: 0, deferred: 0, failed: 0, stale: 0, unrecorded: 0 };
2153
+ const limit = runOptions.limit ?? options.batchSize ?? DEFAULT_BATCH_SIZE;
2154
+ const claimed = await options.claim({ limit, filter: runOptions.filter });
2155
+ report.claimed = claimed.length;
2156
+ const groups = /* @__PURE__ */ new Map();
2157
+ claimed.forEach((job, i) => {
2158
+ const key = options.group ? options.group(job) : `\0${i}`;
2159
+ const list = groups.get(key) ?? [];
2160
+ list.push(job);
2161
+ groups.set(key, list);
2162
+ });
2163
+ await Promise.all(
2164
+ [...groups.values()].map(async (group) => {
2165
+ let results;
2166
+ try {
2167
+ results = await options.send(group, { signal: runOptions.signal });
2168
+ if (results.length !== group.length) throw new Error("send answered the wrong number of results");
2169
+ } catch (error) {
2170
+ const failure = failureOf(error);
2171
+ results = group.map(() => failure);
2172
+ }
2173
+ const at = now();
2174
+ await Promise.all(
2175
+ group.map(async (job, i) => {
2176
+ const result = results[i];
2177
+ const outcome = result === "filtered" ? { status: "filtered" } : jobOutcome(result, job.attempt, {
2178
+ now: at,
2179
+ maxAttempts: options.maxAttempts,
2180
+ backoff: options.backoff,
2181
+ deferrals: job.deferrals,
2182
+ maxDeferrals: options.maxDeferrals
2183
+ });
2184
+ if (outcome.status === "failed" || outcome.status === "retry") {
2185
+ log(`${label(job)} ${outcome.status === "failed" ? "failed" : "will be retried"}: ${outcome.error}`);
2186
+ } else if (outcome.status === "skipped") {
2187
+ log(`${label(job)} skipped: ${outcome.reason}`);
2188
+ }
2189
+ let recorded;
2190
+ try {
2191
+ recorded = await options.record(job, outcome);
2192
+ } catch {
2193
+ report.unrecorded++;
2194
+ log(`Recording ${recordLabel(job)} failed`);
2195
+ return;
2196
+ }
2197
+ if (recorded === false) report.stale++;
2198
+ else if (outcome.status === "sent") report.sent++;
2199
+ else if (outcome.status === "filtered") report.filtered++;
2200
+ else if (outcome.status === "skipped") report.skipped++;
2201
+ else if (outcome.status === "failed") report.failed++;
2202
+ else if (outcome.counted) report.retrying++;
2203
+ else report.deferred++;
2204
+ })
2205
+ );
2206
+ })
2207
+ );
2208
+ return report;
2209
+ }
2210
+ };
2211
+ }
2212
+
2213
+ // src/destinations/runner.ts
2214
+ var DEFAULT_MAX_ATTEMPTS2 = DEFAULT_MAX_ATTEMPTS;
2215
+ var DEFAULT_MAX_DEFERRALS2 = DEFAULT_MAX_DEFERRALS;
2216
+ var DEFAULT_BATCH_SIZE2 = DEFAULT_BATCH_SIZE;
2217
+ var defaultBackoff2 = defaultBackoff;
2218
+ function deliveryOutcome(result, attempt, options = {}) {
2219
+ return jobOutcome(result.ok ? { ok: true, notSent: result.notSent } : result, attempt, options);
2220
+ }
2221
+ function createDeliveryRunner(options) {
2222
+ const log = (message) => {
2223
+ try {
2224
+ options.log?.(message);
2225
+ } catch {
2226
+ }
2227
+ };
2130
2228
  async function send(group, signal) {
2131
2229
  const first = group[0];
2132
2230
  const destination = options.destinations[first.kind];
@@ -2187,78 +2285,34 @@ function createDeliveryRunner(options) {
2187
2285
  let next = 0;
2188
2286
  return results.map((r) => r ?? sent[next++]);
2189
2287
  }
2190
- return {
2191
- async run(runOptions = {}) {
2192
- const report = {
2193
- claimed: 0,
2194
- sent: 0,
2195
- filtered: 0,
2196
- skipped: 0,
2197
- retrying: 0,
2198
- deferred: 0,
2199
- failed: 0,
2200
- stale: 0,
2201
- unrecorded: 0
2202
- };
2203
- const limit = runOptions.limit ?? options.batchSize ?? DEFAULT_BATCH_SIZE;
2204
- const claimed = await options.claim({ limit, filter: runOptions.filter });
2205
- report.claimed = claimed.length;
2206
- const groups = /* @__PURE__ */ new Map();
2207
- for (const delivery of claimed) {
2208
- const list = groups.get(delivery.destinationId) ?? [];
2209
- list.push(delivery);
2210
- groups.set(delivery.destinationId, list);
2211
- }
2212
- await Promise.all(
2213
- [...groups.values()].map(async (group) => {
2214
- const results = await send(group, runOptions.signal);
2215
- const at = now();
2216
- await Promise.all(
2217
- group.map(async (delivery, i) => {
2218
- const result = results[i];
2219
- const outcome = result === "filtered" ? { status: "filtered" } : deliveryOutcome(result, delivery.attempt, {
2220
- now: at,
2221
- maxAttempts: options.maxAttempts,
2222
- backoff: options.backoff,
2223
- deferrals: delivery.deferrals,
2224
- maxDeferrals: options.maxDeferrals
2225
- });
2226
- if (outcome.status === "sent" && result !== "filtered" && result.ok && result.skipped?.includes(delivery.submission.id)) {
2227
- outcome.alreadyThere = true;
2228
- }
2229
- if (outcome.status === "failed" || outcome.status === "retry") {
2230
- log(`Delivery ${delivery.id} (${delivery.kind}) ${outcome.status === "failed" ? "failed" : "will be retried"}: ${outcome.error}`);
2231
- } else if (outcome.status === "skipped") {
2232
- log(`Delivery ${delivery.id} (${delivery.kind}) skipped: ${outcome.reason}`);
2233
- }
2234
- let recorded;
2235
- try {
2236
- recorded = await options.record(delivery, outcome);
2237
- } catch {
2238
- report.unrecorded++;
2239
- log(`Recording delivery ${delivery.id} failed`);
2240
- return;
2241
- }
2242
- if (recorded === false) report.stale++;
2243
- else if (outcome.status === "sent") report.sent++;
2244
- else if (outcome.status === "filtered") report.filtered++;
2245
- else if (outcome.status === "skipped") report.skipped++;
2246
- else if (outcome.status === "failed") report.failed++;
2247
- else if (outcome.counted) report.retrying++;
2248
- else report.deferred++;
2249
- })
2250
- );
2251
- })
2252
- );
2253
- return report;
2254
- }
2255
- };
2288
+ return createRunner({
2289
+ claim: options.claim,
2290
+ // One group per destination: a sheet gets one append for all its rows.
2291
+ group: (delivery) => delivery.destinationId,
2292
+ async send(group, { signal }) {
2293
+ const results = await send(group, signal);
2294
+ return results.map((result, i) => {
2295
+ if (result === "filtered") return result;
2296
+ if (!result.ok) return result;
2297
+ return { ok: true, notSent: result.notSent, alreadyThere: result.skipped?.includes(group[i].submission.id) && !result.notSent };
2298
+ });
2299
+ },
2300
+ record: options.record,
2301
+ now: options.now,
2302
+ log: options.log,
2303
+ label: (delivery) => `Delivery ${delivery.id} (${delivery.kind})`,
2304
+ recordLabel: (delivery) => `delivery ${delivery.id}`,
2305
+ batchSize: options.batchSize,
2306
+ maxAttempts: options.maxAttempts,
2307
+ maxDeferrals: options.maxDeferrals,
2308
+ backoff: options.backoff
2309
+ });
2256
2310
  }
2257
2311
 
2258
2312
  exports.DEDUPE_WINDOW = DEDUPE_WINDOW;
2259
- exports.DEFAULT_BATCH_SIZE = DEFAULT_BATCH_SIZE;
2260
- exports.DEFAULT_MAX_ATTEMPTS = DEFAULT_MAX_ATTEMPTS;
2261
- exports.DEFAULT_MAX_DEFERRALS = DEFAULT_MAX_DEFERRALS;
2313
+ exports.DEFAULT_BATCH_SIZE = DEFAULT_BATCH_SIZE2;
2314
+ exports.DEFAULT_MAX_ATTEMPTS = DEFAULT_MAX_ATTEMPTS2;
2315
+ exports.DEFAULT_MAX_DEFERRALS = DEFAULT_MAX_DEFERRALS2;
2262
2316
  exports.DEFAULT_SHEET_NAME = DEFAULT_SHEET_NAME;
2263
2317
  exports.DRIVE_FILE_SCOPE = DRIVE_FILE_SCOPE;
2264
2318
  exports.DeliveryError = DeliveryError;
@@ -2284,7 +2338,7 @@ exports.clearGoogleTokenCache = clearGoogleTokenCache;
2284
2338
  exports.columnLetter = columnLetter;
2285
2339
  exports.conditionOf = conditionOf;
2286
2340
  exports.createDeliveryRunner = createDeliveryRunner;
2287
- exports.defaultBackoff = defaultBackoff;
2341
+ exports.defaultBackoff = defaultBackoff2;
2288
2342
  exports.deliveryFailure = deliveryFailure;
2289
2343
  exports.deliveryOutcome = deliveryOutcome;
2290
2344
  exports.deriveSecretKey = deriveSecretKey;