@oddington/sdk 0.1.31 → 0.1.33

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 CHANGED
@@ -452,6 +452,38 @@ is read independently with fresh authority; there is no cross-name revision snap
452
452
  Names cannot contain separators or use the reserved `original` basename with
453
453
  or without an extension. Enrichments share their parent File's access and retention.
454
454
 
455
+ Compose retained Files and ordinary Data changes with the pure `source` helpers:
456
+
457
+ ```ts
458
+ import { data, files, source } from '@oddington/sdk';
459
+
460
+ const file = await files.create({ file: statementCsv, name: 'October statement.csv',
461
+ path: 'Statements', storage: 'remote' });
462
+ const statement = source.file({ $ref: '_sources/statement_october', file,
463
+ locations: [source.location.os(file)] });
464
+ const changes = data.repository('general').changes();
465
+ changes.create({ ...statement, added: '2026-10-20' });
466
+ changes.create({ $ref: 'transactions/one',
467
+ sources: [source.csv.row(statement.$ref, parsedTransaction.recordPosition)] });
468
+ await changes.publish();
469
+ ```
470
+
471
+ The caller's CSV parser supplies `recordPosition` from the retained statement:
472
+ positions count CSV records from 1, including the header when present. Expanded
473
+ Source occurrences expose `sourceRecord.$fragment` (for example `row=41`),
474
+ while their `$ref` remains the base Record ref. Exact Source reads and unexpanded
475
+ links retain their ordinary Data shapes.
476
+
477
+ `source.location.gmail({ connection, message, attachment, url? })` and
478
+ `source.location.dropbox({ connection, file, revision?, path?, url? })` build flat
479
+ Locations from successful provider responses. Use
480
+ `source.locations.merge(existingLocations, incomingLocations)` in an ordinary
481
+ guarded Data update. Known objects match by OS File ID, Gmail connection/message/
482
+ attachment, or Dropbox connection/File ID; newer Locations replace old path,
483
+ revision and URL facts. Distinct objects and custom provider values remain.
484
+ Helpers need no initialization and perform no I/O, choose no ref, and add no
485
+ changes. File retention and Data validation/publication remain explicit.
486
+
455
487
  Read a named repository through the authenticated host context:
456
488
 
457
489
  ```ts
package/dist/browser.js CHANGED
@@ -140,8 +140,8 @@ function serializable(value, ancestors = /* @__PURE__ */ new Set()) {
140
140
  try {
141
141
  if (Array.isArray(value))
142
142
  return array(value, "JSON array"), value.map((entry) => serializable(entry, ancestors));
143
- let source = object(value, "JSON input");
144
- return keys(source, Object.keys(source), "JSON input"), Object.fromEntries(Object.entries(source).map(([key, entry]) => [key, serializable(entry, ancestors)]));
143
+ let source2 = object(value, "JSON input");
144
+ return keys(source2, Object.keys(source2), "JSON input"), Object.fromEntries(Object.entries(source2).map(([key, entry]) => [key, serializable(entry, ancestors)]));
145
145
  } finally {
146
146
  ancestors.delete(value);
147
147
  }
@@ -235,9 +235,9 @@ function selection(value) {
235
235
  return keys(input, ["source"], "selection"), validateResponseDefinition({ type: "selection", source: input.source });
236
236
  }
237
237
  function validateAuthoredSource(value) {
238
- let source = object(value, "response definition");
239
- keys(source, ["type", "options", "suggestions", "search"], "response definition"), source.type !== "authored" && fail("Expected authored options");
240
- let options = array(source.options, "options").map((entry) => {
238
+ let source2 = object(value, "response definition");
239
+ keys(source2, ["type", "options", "suggestions", "search"], "response definition"), source2.type !== "authored" && fail("Expected authored options");
240
+ let options = array(source2.options, "options").map((entry) => {
241
241
  let option = object(entry, "choice");
242
242
  keys(option, ["value", "label", "description", "note"], "choice");
243
243
  let note;
@@ -252,14 +252,14 @@ function validateAuthoredSource(value) {
252
252
  ...note ? { note } : {}
253
253
  };
254
254
  });
255
- if (options.length || fail("Choices require at least one option"), new Set(options.map((option) => option.value)).size !== options.length && fail("Duplicate choice value"), Object.hasOwn(source, "search") || Object.hasOwn(source, "suggestions")) {
256
- let labels = object(source.search, "choice search");
255
+ if (options.length || fail("Choices require at least one option"), new Set(options.map((option) => option.value)).size !== options.length && fail("Duplicate choice value"), Object.hasOwn(source2, "search") || Object.hasOwn(source2, "suggestions")) {
256
+ let labels = object(source2.search, "choice search");
257
257
  keys(labels, ["triggerLabel", "placeholder", "emptyLabel"], "choice search");
258
258
  let search = {
259
259
  triggerLabel: text(labels.triggerLabel, "search trigger label"),
260
260
  placeholder: text(labels.placeholder, "search placeholder"),
261
261
  emptyLabel: text(labels.emptyLabel, "search empty label")
262
- }, suggestions = array(source.suggestions, "suggestions").map((value2) => id(value2, "suggestion value"));
262
+ }, suggestions = array(source2.suggestions, "suggestions").map((value2) => id(value2, "suggestion value"));
263
263
  return (suggestions.length > 8 || new Set(suggestions).size !== suggestions.length || suggestions.some((value2) => !options.some((option) => option.value === value2))) && fail("Search permits at most eight unique published suggestions"), { type: "authored", options, suggestions, search };
264
264
  }
265
265
  return options.length > 9 && fail("Choices require one to nine options"), { type: "authored", options };
@@ -271,22 +271,22 @@ function validateRecordReference(value) {
271
271
  return (!/^[a-z0-9][a-z0-9._/-]{0,127}$/.test(repository) || /\.\.|\/\//.test(repository) || ref.length > 512 || !/^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)+$/.test(ref)) && fail("Invalid Record identity"), { repository, ref };
272
272
  }
273
273
  function validateRecordSource(value) {
274
- let source = object(value, "Record selection");
275
- keys(source, ["type", "collection", "refPrefix", "where", "display", "suggestions"], "Record selection"), source.type !== "records" && fail("Expected Record options");
276
- let collection = object(source.collection, "collection");
274
+ let source2 = object(value, "Record selection");
275
+ keys(source2, ["type", "collection", "refPrefix", "where", "display", "suggestions"], "Record selection"), source2.type !== "records" && fail("Expected Record options");
276
+ let collection = object(source2.collection, "collection");
277
277
  keys(collection, ["repository", "name"], "collection");
278
278
  let repository = validateRecordReference({ repository: collection.repository, ref: "records/example" }).repository, name2 = id(collection.name, "collection name");
279
279
  /^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/.test(name2) || fail("Invalid collection name");
280
- let refPrefix = source.refPrefix === void 0 ? void 0 : id(source.refPrefix, "Record reference prefix");
280
+ let refPrefix = source2.refPrefix === void 0 ? void 0 : id(source2.refPrefix, "Record reference prefix");
281
281
  refPrefix !== void 0 && (!refPrefix.endsWith("/") || refPrefix.length > 512 || !/^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)*\/$/.test(refPrefix)) && fail("Invalid Record reference prefix");
282
- let where = object(source.where ?? {}, "Record filter"), fieldName = (name3) => /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name3) && !["constructor", "prototype"].includes(name3);
282
+ let where = object(source2.where ?? {}, "Record filter"), fieldName = (name3) => /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name3) && !["constructor", "prototype"].includes(name3);
283
283
  for (let [name3, value2] of Object.entries(where))
284
284
  (!fieldName(name3) || !(value2 === null || ["string", "number", "boolean"].includes(typeof value2)) || typeof value2 == "number" && !Number.isFinite(value2)) && fail("Record filters require scalar equality");
285
- let display = object(source.display, "Record display");
285
+ let display = object(source2.display, "Record display");
286
286
  keys(display, ["labelFields"], "Record display");
287
287
  let labelFields = distinctIds(display.labelFields, "label fields");
288
288
  (labelFields.length > 2 || labelFields.some((name3) => !fieldName(name3))) && fail("Select one or two scalar label fields");
289
- let suggestions = array(source.suggestions ?? [], "suggestions").map((value2) => {
289
+ let suggestions = array(source2.suggestions ?? [], "suggestions").map((value2) => {
290
290
  let entry = object(value2, "suggestion");
291
291
  keys(entry, ["record", "reason"], "suggestion");
292
292
  let record4 = validateRecordReference(entry.record);
@@ -304,61 +304,61 @@ function validateRecordSource(value) {
304
304
  function validateResponseDefinition(value) {
305
305
  let definition = object(value, "response definition");
306
306
  keys(definition, ["type", "source"], "response definition"), definition.type !== "selection" && fail("Expected a selection definition");
307
- let source = object(definition.source, "option source");
308
- switch (source.type) {
307
+ let source2 = object(definition.source, "option source");
308
+ switch (source2.type) {
309
309
  case "authored":
310
- return { type: "selection", source: validateAuthoredSource(source) };
310
+ return { type: "selection", source: validateAuthoredSource(source2) };
311
311
  case "records":
312
- return { type: "selection", source: validateRecordSource(source) };
312
+ return { type: "selection", source: validateRecordSource(source2) };
313
313
  default:
314
314
  return fail("Unsupported option source");
315
315
  }
316
316
  }
317
317
  function handlerReference(value) {
318
- let source = object(value, "handler reference");
319
- return keys(source, ["id", "input"], "handler reference"), { id: id(source.id, "handler ID"), input: serializable(source.input) };
318
+ let source2 = object(value, "handler reference");
319
+ return keys(source2, ["id", "input"], "handler reference"), { id: id(source2.id, "handler ID"), input: serializable(source2.input) };
320
320
  }
321
321
  function defineHandler(value) {
322
- let source = object(value, "handler");
323
- keys(source, ["id", "results", "run"], "handler");
322
+ let source2 = object(value, "handler");
323
+ keys(source2, ["id", "results", "run"], "handler");
324
324
  let stable = (value2) => typeof value2 == "string" && /^[a-zA-Z0-9][a-zA-Z0-9_./-]{0,127}$/.test(value2);
325
- stable(source.id) || fail("Handler ID must be stable");
326
- let results = source.results === void 0 ? void 0 : distinctIds(source.results, "handler results", !1);
327
- return results && (results.length > 64 || results.some((result2) => !stable(result2))) && fail("Handler results must be stable IDs"), typeof source.run != "function" && fail("Handler requires a run function"), Object.freeze({ id: value.id, ...results === void 0 ? {} : { results: Object.freeze(results.sort()) }, run: value.run });
325
+ stable(source2.id) || fail("Handler ID must be stable");
326
+ let results = source2.results === void 0 ? void 0 : distinctIds(source2.results, "handler results", !1);
327
+ return results && (results.length > 64 || results.some((result2) => !stable(result2))) && fail("Handler results must be stable IDs"), typeof source2.run != "function" && fail("Handler requires a run function"), Object.freeze({ id: value.id, ...results === void 0 ? {} : { results: Object.freeze(results.sort()) }, run: value.run });
328
328
  }
329
329
  function respond(handler, input) {
330
330
  return defineHandler(handler), handlerReference({ id: handler.id, input });
331
331
  }
332
332
  function question(value) {
333
- let source = object(value, "question");
334
- keys(source, ["id", "to", "allowedResponders", "prompt", "instructions", "response", "handler"], "question");
335
- let to = personReference(source.to, "question assignee"), allowedResponders = source.allowedResponders === void 0 ? [to] : distinctPeople(source.allowedResponders, "allowed responders");
333
+ let source2 = object(value, "question");
334
+ keys(source2, ["id", "to", "allowedResponders", "prompt", "instructions", "response", "handler"], "question");
335
+ let to = personReference(source2.to, "question assignee"), allowedResponders = source2.allowedResponders === void 0 ? [to] : distinctPeople(source2.allowedResponders, "allowed responders");
336
336
  return allowedResponders.includes(to) || fail("Allowed responders must include the assignee"), {
337
- id: id(source.id, "question ID"),
337
+ id: id(source2.id, "question ID"),
338
338
  to,
339
339
  allowedResponders,
340
- prompt: text(source.prompt, "question prompt"),
341
- ...source.instructions === void 0 ? {} : { instructions: text(source.instructions, "instructions") },
342
- response: validateResponseDefinition(source.response),
343
- ...Object.hasOwn(source, "handler") ? { handler: handlerReference(source.handler) } : {}
340
+ prompt: text(source2.prompt, "question prompt"),
341
+ ...source2.instructions === void 0 ? {} : { instructions: text(source2.instructions, "instructions") },
342
+ response: validateResponseDefinition(source2.response),
343
+ ...Object.hasOwn(source2, "handler") ? { handler: handlerReference(source2.handler) } : {}
344
344
  };
345
345
  }
346
- function presentation(source) {
347
- let views = source.views === void 0 ? [] : array(source.views, "views").map((entry) => {
346
+ function presentation(source2) {
347
+ let views = source2.views === void 0 ? [] : array(source2.views, "views").map((entry) => {
348
348
  let view = object(entry, "view");
349
349
  return keys(view, ["id", "view", "input"], "view"), { id: id(view.id, "view ID"), view: id(view.view, "view name"), input: serializable(view.input) };
350
350
  });
351
351
  new Set(views.map((view) => view.id)).size !== views.length && fail("Duplicate view ID");
352
- let body = source.body === void 0 ? [] : array(source.body, "body").map(validateBlock);
352
+ let body = source2.body === void 0 ? [] : array(source2.body, "body").map(validateBlock);
353
353
  for (let block2 of body) {
354
354
  let targets = block2.kind === "record" ? [block2.view] : block2.kind === "records" ? [block2.view, ...block2.records.map((record4) => record4.view)] : [];
355
355
  for (let target of targets) target !== void 0 && !views.some((view) => view.id === target) && fail(`Unknown Block View target: ${target}`);
356
356
  }
357
- return { title: text(source.title, "title"), description: text(source.description, "description"), views, body };
357
+ return { title: text(source2.title, "title"), description: text(source2.description, "description"), views, body };
358
358
  }
359
359
  function validatePublication(value) {
360
- let source = object(value, "publication");
361
- source.kind !== "ask" && source.kind !== "digest" && fail("Publication requires an explicit kind"), keys(source, [
360
+ let source2 = object(value, "publication");
361
+ source2.kind !== "ask" && source2.kind !== "digest" && fail("Publication requires an explicit kind"), keys(source2, [
362
362
  "kind",
363
363
  "title",
364
364
  "description",
@@ -366,25 +366,25 @@ function validatePublication(value) {
366
366
  "views",
367
367
  "body",
368
368
  "notifyImmediately",
369
- ...source.kind === "ask" ? ["questions", "tags", "recordRefs"] : []
370
- ], "publication"), source.notifyImmediately !== void 0 && typeof source.notifyImmediately != "boolean" && fail("notifyImmediately must be a boolean");
371
- let itemPresentation = { ...presentation(source), ...source.notifyImmediately === void 0 ? {} : { notifyImmediately: source.notifyImmediately } }, linkedAsks = (itemPresentation.body ?? []).flatMap((block2) => block2.kind === "asks" ? block2.asks : []);
372
- if (new Set(linkedAsks).size !== linkedAsks.length && fail("Ask belongs to more than one problem"), source.kind === "digest") return {
369
+ ...source2.kind === "ask" ? ["questions", "tags", "recordRefs"] : []
370
+ ], "publication"), source2.notifyImmediately !== void 0 && typeof source2.notifyImmediately != "boolean" && fail("notifyImmediately must be a boolean");
371
+ let itemPresentation = { ...presentation(source2), ...source2.notifyImmediately === void 0 ? {} : { notifyImmediately: source2.notifyImmediately } }, linkedAsks = (itemPresentation.body ?? []).flatMap((block2) => block2.kind === "asks" ? block2.asks : []);
372
+ if (new Set(linkedAsks).size !== linkedAsks.length && fail("Ask belongs to more than one problem"), source2.kind === "digest") return {
373
373
  kind: "digest",
374
374
  ...itemPresentation,
375
- recipients: distinctPeople(source.recipients, "digest recipients")
375
+ recipients: distinctPeople(source2.recipients, "digest recipients")
376
376
  };
377
377
  itemPresentation.body?.some((block2) => block2.kind === "asks") && fail("Asks Blocks belong to Digests");
378
- let questions = array(source.questions === void 0 ? [] : source.questions, "questions").map((value2) => question(value2));
378
+ let questions = array(source2.questions === void 0 ? [] : source2.questions, "questions").map((value2) => question(value2));
379
379
  new Set(questions.map((entry) => entry.id)).size !== questions.length && fail("Duplicate question ID");
380
- let extra = source.recipients === void 0 ? [] : distinctPeople(source.recipients, "recipients", !1);
380
+ let extra = source2.recipients === void 0 ? [] : distinctPeople(source2.recipients, "recipients", !1);
381
381
  !questions.length && !extra.length && fail("Ask requires at least one recipient");
382
- let tags = array(source.tags === void 0 ? [] : source.tags, "Ask tags").map((value2) => {
382
+ let tags = array(source2.tags === void 0 ? [] : source2.tags, "Ask tags").map((value2) => {
383
383
  let tag = text(value2, "Ask tag");
384
384
  return new TextEncoder().encode(tag).byteLength > 64 && fail("Ask tag exceeds 64 UTF-8 bytes"), tag;
385
385
  });
386
386
  (tags.length > 32 || new Set(tags).size !== tags.length) && fail("Ask tags exceed bounds or contain duplicates");
387
- let recordRefs = array(source.recordRefs === void 0 ? [] : source.recordRefs, "Ask Record references").map((value2) => {
387
+ let recordRefs = array(source2.recordRefs === void 0 ? [] : source2.recordRefs, "Ask Record references").map((value2) => {
388
388
  let row = object(value2, "Ask Record reference");
389
389
  keys(row, ["repository", "id"], "Ask Record reference");
390
390
  let { repository, ref } = validateRecordReference({ repository: row.repository, ref: row.id });
@@ -400,25 +400,25 @@ function validatePublication(value) {
400
400
  };
401
401
  }
402
402
  function continueAsk(value) {
403
- let source = object(value, "continueAsk");
404
- return keys(source, [], "continueAsk"), { type: "continue" };
403
+ let source2 = object(value, "continueAsk");
404
+ return keys(source2, [], "continueAsk"), { type: "continue" };
405
405
  }
406
406
  function resolution(value) {
407
- let source = object(value, "resolution");
408
- return keys(source, ["outcome", "evidence"], "resolution"), { outcome: text(source.outcome, "outcome"), ...source.evidence === void 0 ? {} : {
409
- evidence: array(source.evidence, "evidence").map((entry) => serializable(entry))
407
+ let source2 = object(value, "resolution");
408
+ return keys(source2, ["outcome", "evidence"], "resolution"), { outcome: text(source2.outcome, "outcome"), ...source2.evidence === void 0 ? {} : {
409
+ evidence: array(source2.evidence, "evidence").map((entry) => serializable(entry))
410
410
  } };
411
411
  }
412
412
  function resolveAsk(value) {
413
413
  return { type: "resolve", ...resolution(value) };
414
414
  }
415
415
  function validateResolution(value) {
416
- let source = object(value, "published resolution");
417
- return keys(source, ["expectedRevision", "outcome", "evidence"], "published resolution"), (!Number.isSafeInteger(source.expectedRevision) || source.expectedRevision < 1) && fail("Expected revision must be a positive integer"), {
418
- expectedRevision: source.expectedRevision,
416
+ let source2 = object(value, "published resolution");
417
+ return keys(source2, ["expectedRevision", "outcome", "evidence"], "published resolution"), (!Number.isSafeInteger(source2.expectedRevision) || source2.expectedRevision < 1) && fail("Expected revision must be a positive integer"), {
418
+ expectedRevision: source2.expectedRevision,
419
419
  ...resolution({
420
- outcome: source.outcome,
421
- ...source.evidence === void 0 ? {} : { evidence: source.evidence }
420
+ outcome: source2.outcome,
421
+ ...source2.evidence === void 0 ? {} : { evidence: source2.evidence }
422
422
  })
423
423
  };
424
424
  }
@@ -429,20 +429,20 @@ function publicationRequestKey(value) {
429
429
  function createInboxPublisher(transport) {
430
430
  return {
431
431
  async publishAsk(value, options) {
432
- let source = object(value, "ask");
433
- keys(source, ["title", "description", "recipients", "views", "body", "questions", "notifyImmediately", "tags", "recordRefs"], "ask");
432
+ let source2 = object(value, "ask");
433
+ keys(source2, ["title", "description", "recipients", "views", "body", "questions", "notifyImmediately", "tags", "recordRefs"], "ask");
434
434
  let result2 = await transport.publish({
435
435
  requestKey: publicationRequestKey(options),
436
- item: validatePublication({ ...source, kind: "ask" })
436
+ item: validatePublication({ ...source2, kind: "ask" })
437
437
  });
438
438
  return id(result2.id, "published ask ID"), (!("revision" in result2) || !Number.isSafeInteger(result2.revision) || result2.revision !== 1) && fail("Initial ask publication must return revision 1"), { id: result2.id, revision: result2.revision };
439
439
  },
440
440
  async publishDigest(value, options) {
441
- let source = object(value, "digest");
442
- keys(source, ["title", "description", "recipients", "views", "body", "notifyImmediately"], "digest");
441
+ let source2 = object(value, "digest");
442
+ keys(source2, ["title", "description", "recipients", "views", "body", "notifyImmediately"], "digest");
443
443
  let result2 = await transport.publish({
444
444
  requestKey: publicationRequestKey(options),
445
- item: validatePublication({ ...source, kind: "digest" })
445
+ item: validatePublication({ ...source2, kind: "digest" })
446
446
  });
447
447
  return { id: id(result2.id, "published digest ID") };
448
448
  },
@@ -1406,11 +1406,11 @@ async function hostedContent(url, init2, parent, upstream = fetch, check = async
1406
1406
  }
1407
1407
  if (!response2.body)
1408
1408
  return detach(), response2;
1409
- let source = response2.body, reader, settled = !1, output, cancel = async (reason) => {
1409
+ let source2 = response2.body, reader, settled = !1, output, cancel = async (reason) => {
1410
1410
  if (!settled) {
1411
1411
  settled = !0, detach(), delivery.signal.removeEventListener("abort", stop), delivery.abort(reason);
1412
1412
  try {
1413
- await (reader ? reader.cancel(reason) : source.cancel(reason));
1413
+ await (reader ? reader.cancel(reason) : source2.cancel(reason));
1414
1414
  } catch (error) {
1415
1415
  if (error !== delivery.signal.reason && !(error instanceof Error && error.name === "AbortError")) throw error;
1416
1416
  } finally {
@@ -1429,7 +1429,7 @@ async function hostedContent(url, init2, parent, upstream = fetch, check = async
1429
1429
  async pull(controller) {
1430
1430
  try {
1431
1431
  if (await check(), settled) return;
1432
- reader ??= source.getReader();
1432
+ reader ??= source2.getReader();
1433
1433
  let { done, value } = await reader.read();
1434
1434
  if (await check(), settled) return;
1435
1435
  done ? (settled = !0, detach(), delivery.signal.removeEventListener("abort", stop), reader.releaseLock(), controller.close()) : controller.enqueue(value);
@@ -2192,6 +2192,68 @@ function validCreatePath(value) {
2192
2192
  return value === value.trim() && value.endsWith(".md") && !value.startsWith("/") && value.split("/").every((segment) => !!segment && ![".", ".."].includes(segment) && /^[a-zA-Z0-9_.-]+$/.test(segment));
2193
2193
  }
2194
2194
 
2195
+ function locationIdentity(location) {
2196
+ let coordinates = location.provider === "os" ? [location.file] : location.provider === "gmail" ? [location.connection, location.message, location.attachment] : location.provider === "dropbox" ? [location.connection, location.file] : void 0;
2197
+ if (coordinates?.every((value) => typeof value == "string" && value.length > 0))
2198
+ return JSON.stringify([location.provider, ...coordinates]);
2199
+ }
2200
+ var source = {
2201
+ file(input) {
2202
+ return {
2203
+ $ref: input.$ref,
2204
+ type: "source",
2205
+ kind: "file",
2206
+ filename: input.file.name,
2207
+ media_type: input.file.mediaType,
2208
+ size_bytes: input.file.size,
2209
+ sha256: input.file.sha256,
2210
+ locations: input.locations.map((location) => ({ ...location }))
2211
+ };
2212
+ },
2213
+ location: {
2214
+ os(file2) {
2215
+ return { provider: "os", file: file2.id };
2216
+ },
2217
+ gmail(input) {
2218
+ return {
2219
+ provider: "gmail",
2220
+ connection: input.connection,
2221
+ message: input.message,
2222
+ attachment: input.attachment,
2223
+ ...input.url === void 0 ? {} : { url: input.url }
2224
+ };
2225
+ },
2226
+ dropbox(input) {
2227
+ return {
2228
+ provider: "dropbox",
2229
+ connection: input.connection,
2230
+ file: input.file,
2231
+ ...input.revision === void 0 ? {} : { revision: input.revision },
2232
+ ...input.path === void 0 ? {} : { path: input.path },
2233
+ ...input.url === void 0 ? {} : { url: input.url }
2234
+ };
2235
+ }
2236
+ },
2237
+ locations: {
2238
+ merge(existing, incoming) {
2239
+ let merged = existing.map((location) => ({ ...location }));
2240
+ for (let location of incoming) {
2241
+ let identity = locationIdentity(location), index = identity === void 0 ? -1 : merged.findIndex((value) => locationIdentity(value) === identity);
2242
+ index < 0 ? merged.push({ ...location }) : merged[index] = { ...location };
2243
+ }
2244
+ return merged;
2245
+ }
2246
+ },
2247
+ csv: {
2248
+ /** One-based CSV record position; a present header is record 1. */
2249
+ row(baseRef, row) {
2250
+ if (!validRecordRef(baseRef)) throw new TypeError("CSV row links require a base Record ref");
2251
+ if (!Number.isSafeInteger(row) || row < 1) throw new RangeError("CSV row must be a positive safe integer");
2252
+ return `${baseRef}#row=${row}`;
2253
+ }
2254
+ }
2255
+ };
2256
+
2195
2257
  var InboxOperationError = class extends Error {
2196
2258
  constructor(status, reason) {
2197
2259
  super(reason === "conflict" ? "This question already has an accepted answer" : reason === "unavailable" ? "Inbox request unavailable. Retry the same action." : reason === "invalid_input" ? "Invalid Inbox request" : "Inbox access unavailable");
@@ -2268,10 +2330,10 @@ function recordReference(value) {
2268
2330
  function responseDefinition(value) {
2269
2331
  let definition = object4(value);
2270
2332
  keys3(definition, ["type", "source"]), assert(definition.type === "selection");
2271
- let source = object4(definition.source);
2272
- if (source.type === "authored") {
2273
- keys3(source, ["type", "options", "suggestions", "search"], ["type", "options"]);
2274
- let options = array2(source.options);
2333
+ let source2 = object4(definition.source);
2334
+ if (source2.type === "authored") {
2335
+ keys3(source2, ["type", "options", "suggestions", "search"], ["type", "options"]);
2336
+ let options = array2(source2.options);
2275
2337
  assert(options.length > 0);
2276
2338
  let values = options.map((value2) => {
2277
2339
  let option = object4(value2);
@@ -2281,24 +2343,24 @@ function responseDefinition(value) {
2281
2343
  }
2282
2344
  return option.value;
2283
2345
  });
2284
- if (assert(new Set(values).size === values.length), "search" in source || "suggestions" in source) {
2285
- let search = object4(source.search);
2346
+ if (assert(new Set(values).size === values.length), "search" in source2 || "suggestions" in source2) {
2347
+ let search = object4(source2.search);
2286
2348
  keys3(search, ["triggerLabel", "placeholder", "emptyLabel"]), assert(text4(search.triggerLabel) && text4(search.placeholder) && text4(search.emptyLabel));
2287
- let suggestions2 = array2(source.suggestions);
2349
+ let suggestions2 = array2(source2.suggestions);
2288
2350
  assert(suggestions2.length <= 8 && suggestions2.every((value2) => text4(value2) && values.includes(value2)) && new Set(suggestions2).size === suggestions2.length);
2289
2351
  } else assert(options.length <= 9);
2290
2352
  return;
2291
2353
  }
2292
- assert(source.type === "records"), keys3(source, ["type", "collection", "refPrefix", "where", "display", "suggestions"], ["type", "collection", "display"]);
2293
- let collection = object4(source.collection);
2294
- keys3(collection, ["repository", "name"]), recordReference({ repository: collection.repository, ref: "records/example" }), assert(text4(collection.name) && /^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/.test(collection.name)), "refPrefix" in source && assert(text4(source.refPrefix) && source.refPrefix.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)*\/$/.test(source.refPrefix));
2295
- let where = object4(source.where ?? {});
2354
+ assert(source2.type === "records"), keys3(source2, ["type", "collection", "refPrefix", "where", "display", "suggestions"], ["type", "collection", "display"]);
2355
+ let collection = object4(source2.collection);
2356
+ keys3(collection, ["repository", "name"]), recordReference({ repository: collection.repository, ref: "records/example" }), assert(text4(collection.name) && /^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/.test(collection.name)), "refPrefix" in source2 && assert(text4(source2.refPrefix) && source2.refPrefix.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)*\/$/.test(source2.refPrefix));
2357
+ let where = object4(source2.where ?? {});
2296
2358
  assert(Object.entries(where).every(([name2, value2]) => /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name2) && !["constructor", "prototype"].includes(name2) && (value2 === null || typeof value2 == "string" || typeof value2 == "boolean" || typeof value2 == "number" && Number.isFinite(value2))));
2297
- let display = object4(source.display);
2359
+ let display = object4(source2.display);
2298
2360
  keys3(display, ["labelFields"]);
2299
2361
  let labels = array2(display.labelFields);
2300
2362
  assert(labels.length > 0 && labels.length <= 2 && new Set(labels).size === labels.length && labels.every((value2) => text4(value2) && /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value2)));
2301
- let suggestions = array2(source.suggestions ?? []), refs = suggestions.map((value2) => {
2363
+ let suggestions = array2(source2.suggestions ?? []), refs = suggestions.map((value2) => {
2302
2364
  let suggestion = object4(value2);
2303
2365
  keys3(suggestion, ["record", "reason"], ["record"]);
2304
2366
  let record4 = recordReference(suggestion.record);
@@ -3017,16 +3079,16 @@ async function send2(operation2, input, admitted) {
3017
3079
  }), unavailable3();
3018
3080
  if (operation2 === "files.read") {
3019
3081
  if (!response2.body) throw unavailable3();
3020
- let source = response2.body, reader, size = 0, cancel = async (reason) => {
3082
+ let source2 = response2.body, reader, size = 0, cancel = async (reason) => {
3021
3083
  try {
3022
- await (reader ? reader.cancel(reason) : source.cancel(reason));
3084
+ await (reader ? reader.cancel(reason) : source2.cancel(reason));
3023
3085
  } finally {
3024
3086
  reader?.releaseLock();
3025
3087
  }
3026
3088
  }, body = new ReadableStream({
3027
3089
  async pull(controller) {
3028
3090
  try {
3029
- check(), reader ??= source.getReader();
3091
+ check(), reader ??= source2.getReader();
3030
3092
  let next = await reader.read();
3031
3093
  if (check(), next.done) {
3032
3094
  reader.releaseLock(), controller.close();
@@ -3126,8 +3188,8 @@ var inbox = {
3126
3188
  };
3127
3189
 
3128
3190
  var sources = /* @__PURE__ */ new WeakMap();
3129
- function registerBrowserAuth(handle2, source) {
3130
- sources.set(handle2, source);
3191
+ function registerBrowserAuth(handle2, source2) {
3192
+ sources.set(handle2, source2);
3131
3193
  }
3132
3194
  function browserAuthSource(handle2) {
3133
3195
  return sources.get(handle2);
@@ -3970,5 +4032,6 @@ export {
3970
4032
  richTextBlock,
3971
4033
  routinePublicationClient2 as routinePublicationClient,
3972
4034
  schema,
3973
- selection
4035
+ selection,
4036
+ source
3974
4037
  };