@aglyn/plugins-data 1.0.0-beta.220 → 1.0.0-beta.222

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.
Files changed (135) hide show
  1. package/README.md +9 -6
  2. package/package.json +10 -10
  3. package/src/index.d.ts +1 -1
  4. package/src/index.js +1 -1
  5. package/src/index.js.map +1 -1
  6. package/src/lib/artifact/dataset-schema-artifact.server.d.ts +57 -0
  7. package/src/lib/artifact/dataset-schema-artifact.server.js +308 -0
  8. package/src/lib/artifact/dataset-schema-artifact.server.js.map +1 -0
  9. package/src/lib/artifact/dataset-schema.d.ts +136 -0
  10. package/src/lib/artifact/dataset-schema.js +232 -0
  11. package/src/lib/artifact/dataset-schema.js.map +1 -0
  12. package/src/lib/components/dataset-record-dialog.component.d.ts +1 -1
  13. package/src/lib/components/dataset-record-dialog.component.js +1 -1
  14. package/src/lib/components/dataset-record-dialog.component.js.map +1 -1
  15. package/src/lib/components/dataset-record-filter.d.ts +1 -1
  16. package/src/lib/components/dataset-record-filter.js +2 -2
  17. package/src/lib/components/dataset-record-filter.js.map +1 -1
  18. package/src/lib/components/dataset-schema-dialog.component.d.ts +1 -1
  19. package/src/lib/components/dataset-schema-dialog.component.js +55 -23
  20. package/src/lib/components/dataset-schema-dialog.component.js.map +1 -1
  21. package/src/lib/components/host-datasets-card.component.js +91 -403
  22. package/src/lib/components/host-datasets-card.component.js.map +1 -1
  23. package/src/lib/constants/bundle-common.d.ts +12 -0
  24. package/src/lib/constants/bundle-common.js +13 -0
  25. package/src/lib/constants/bundle-common.js.map +1 -1
  26. package/src/lib/declarations.console-server.d.ts +16 -1
  27. package/src/lib/declarations.console-server.js +34 -2
  28. package/src/lib/declarations.console-server.js.map +1 -1
  29. package/src/lib/declarations.server.d.ts +13 -0
  30. package/src/lib/declarations.server.js +48 -2
  31. package/src/lib/declarations.server.js.map +1 -1
  32. package/src/lib/form-target/dataset-form-record-target.server.js +5 -3
  33. package/src/lib/form-target/dataset-form-record-target.server.js.map +1 -1
  34. package/src/lib/model/dataset-models.d.ts +392 -0
  35. package/src/lib/model/dataset-models.js +806 -0
  36. package/src/lib/model/dataset-models.js.map +1 -0
  37. package/src/lib/model/dataset-record-view.d.ts +1 -1
  38. package/src/lib/model/dataset-record-view.js +1 -2
  39. package/src/lib/model/dataset-record-view.js.map +1 -1
  40. package/src/lib/model/dataset-record.js +2 -2
  41. package/src/lib/model/dataset-record.js.map +1 -1
  42. package/src/lib/model/datasets.d.ts +162 -0
  43. package/src/lib/model/datasets.js +227 -0
  44. package/src/lib/model/datasets.js.map +1 -0
  45. package/src/lib/plugin.d.ts +4 -0
  46. package/src/lib/plugin.js +40 -0
  47. package/src/lib/plugin.js.map +1 -1
  48. package/src/lib/record-pages/record-page-live-paths.server.d.ts +55 -0
  49. package/src/lib/record-pages/record-page-live-paths.server.js +83 -0
  50. package/src/lib/record-pages/record-page-live-paths.server.js.map +1 -0
  51. package/src/lib/record-pages/record-page-read.server.d.ts +70 -0
  52. package/src/lib/record-pages/record-page-read.server.js +175 -0
  53. package/src/lib/record-pages/record-page-read.server.js.map +1 -0
  54. package/src/lib/record-pages/record-page-resolver.server.d.ts +37 -0
  55. package/src/lib/record-pages/record-page-resolver.server.js +131 -0
  56. package/src/lib/record-pages/record-page-resolver.server.js.map +1 -0
  57. package/src/lib/record-pages/record-page-sitemap.server.d.ts +32 -0
  58. package/src/lib/record-pages/record-page-sitemap.server.js +102 -0
  59. package/src/lib/record-pages/record-page-sitemap.server.js.map +1 -0
  60. package/src/lib/record-pages/record-page-source.d.ts +42 -0
  61. package/src/lib/record-pages/record-page-source.js +176 -0
  62. package/src/lib/record-pages/record-page-source.js.map +1 -0
  63. package/src/lib/record-pages/record-pages-route.d.ts +42 -0
  64. package/src/lib/record-pages/record-pages-route.js +348 -0
  65. package/src/lib/record-pages/record-pages-route.js.map +1 -0
  66. package/src/lib/record-pages/record-pages.d.ts +218 -0
  67. package/src/lib/record-pages/record-pages.js +351 -0
  68. package/src/lib/record-pages/record-pages.js.map +1 -0
  69. package/src/lib/record-pages/record-template-row-chip.component.d.ts +28 -0
  70. package/src/lib/record-pages/record-template-row-chip.component.js +87 -0
  71. package/src/lib/record-pages/record-template-row-chip.component.js.map +1 -0
  72. package/src/lib/record-pages/record-template-section.component.d.ts +30 -0
  73. package/src/lib/record-pages/record-template-section.component.js +491 -0
  74. package/src/lib/record-pages/record-template-section.component.js.map +1 -0
  75. package/src/lib/repeat/dataset-repeat-rows.d.ts +2 -1
  76. package/src/lib/repeat/dataset-repeat-rows.js +30 -12
  77. package/src/lib/repeat/dataset-repeat-rows.js.map +1 -1
  78. package/src/lib/repeat/dataset-repeat-rows.server.d.ts +1 -1
  79. package/src/lib/repeat/dataset-repeat-rows.server.js +23 -12
  80. package/src/lib/repeat/dataset-repeat-rows.server.js.map +1 -1
  81. package/src/lib/repeat/repeat-record-pages.d.ts +48 -0
  82. package/src/lib/repeat/repeat-record-pages.js +64 -0
  83. package/src/lib/repeat/repeat-record-pages.js.map +1 -0
  84. package/src/lib/server/announce-dataset-records.d.ts +1 -1
  85. package/src/lib/server/announce-dataset-records.js +1 -1
  86. package/src/lib/server/announce-dataset-records.js.map +1 -1
  87. package/src/lib/server/api-v1/datasets.js +5 -3
  88. package/src/lib/server/api-v1/datasets.js.map +1 -1
  89. package/src/lib/server/dataset-figures.js +2 -2
  90. package/src/lib/server/dataset-figures.js.map +1 -1
  91. package/src/lib/server/dataset-live-pages.d.ts +147 -0
  92. package/src/lib/server/dataset-live-pages.js +436 -0
  93. package/src/lib/server/dataset-live-pages.js.map +1 -0
  94. package/src/lib/server/dataset-steps.server.d.ts +45 -0
  95. package/src/lib/server/dataset-steps.server.js +268 -0
  96. package/src/lib/server/dataset-steps.server.js.map +1 -0
  97. package/src/lib/server/datasets-route.d.ts +2 -0
  98. package/src/lib/server/datasets-route.js +102 -6
  99. package/src/lib/server/datasets-route.js.map +1 -1
  100. package/src/lib/server/resolve-dataset.d.ts +40 -0
  101. package/src/lib/server/resolve-dataset.js +64 -0
  102. package/src/lib/server/resolve-dataset.js.map +1 -0
  103. package/src/lib/server.d.ts +3 -2
  104. package/src/lib/server.js +8 -9
  105. package/src/lib/server.js.map +1 -1
  106. package/src/lib/site-bundle/datasets-package.d.ts +42 -0
  107. package/src/lib/site-bundle/datasets-package.js +95 -0
  108. package/src/lib/site-bundle/datasets-package.js.map +1 -0
  109. package/src/lib/site-bundle/datasets-site-bundle.server.js +2 -2
  110. package/src/lib/site-bundle/datasets-site-bundle.server.js.map +1 -1
  111. package/src/lib/{model/index.js → tenant-api.d.ts} +12 -7
  112. package/src/lib/tenant-api.js +30 -0
  113. package/src/lib/tenant-api.js.map +1 -0
  114. package/src/lib/transfer/dataset-transfer-key.d.ts +30 -0
  115. package/src/lib/transfer/dataset-transfer-key.js +31 -0
  116. package/src/lib/transfer/dataset-transfer-key.js.map +1 -0
  117. package/src/lib/transfer/dataset-transfer-model.d.ts +146 -0
  118. package/src/lib/transfer/dataset-transfer-model.js +536 -0
  119. package/src/lib/transfer/dataset-transfer-model.js.map +1 -0
  120. package/src/lib/{model/index.d.ts → transfer/dataset-transfer-resource.d.ts} +2 -6
  121. package/src/lib/transfer/dataset-transfer-resource.js +41 -0
  122. package/src/lib/transfer/dataset-transfer-resource.js.map +1 -0
  123. package/src/lib/transfer/dataset-transfer.server.d.ts +69 -0
  124. package/src/lib/transfer/dataset-transfer.server.js +930 -0
  125. package/src/lib/transfer/dataset-transfer.server.js.map +1 -0
  126. package/src/lib/model/dataset-csv.d.ts +0 -36
  127. package/src/lib/model/dataset-csv.js +0 -85
  128. package/src/lib/model/dataset-csv.js.map +0 -1
  129. package/src/lib/model/dataset-io.d.ts +0 -44
  130. package/src/lib/model/dataset-io.js +0 -85
  131. package/src/lib/model/dataset-io.js.map +0 -1
  132. package/src/lib/model/index.js.map +0 -1
  133. package/src/lib/server/datasets-export-route.d.ts +0 -57
  134. package/src/lib/server/datasets-export-route.js +0 -229
  135. package/src/lib/server/datasets-export-route.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/data/src/lib/transfer/dataset-transfer.server.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createHash } from 'node:crypto'\nimport {\n TRANSFER_ID_FIELD,\n buildTransferPlan,\n matchLookupKey,\n planTransferUndo,\n rankTransferLookupSuggestions,\n transferLookupKey,\n transferLookupNewName,\n transferResourceInstanceOf,\n type MatchLookupRequest,\n type PlannedTransferRow,\n type TransferLookupSuggestion,\n type TransferPlanLimits,\n type TransferRowResult,\n type TransferUndoEntry,\n type TransferUndoStep,\n} from '@aglyn/aglyn/data-transfer'\nimport type { PicklistValue } from '@aglyn/aglyn/app-utils/picklists'\nimport type {\n TransferApplyResult,\n TransferApplyWriter,\n TransferLookupResult,\n TransferLookupSuggestRequest,\n TransferPicklistList,\n TransferReadOptions,\n TransferReadPage,\n TransferRecordsHooks,\n TransferResourceContext,\n TransferRevertDecisions,\n TransferRevertResult,\n} from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport type { ListFilterClause } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { checkQuota, ensureDeclaredCustomFieldTypes, memberCanSee } from '@aglyn/aglyn/server'\nimport {\n dataStorageRefusal,\n firebaseAdmin,\n isServerReleaseFlagOnForOrg,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin'\nimport { applyListQuery } from '@aglyn/tenant-data-admin/server/list-query'\nimport { TransferEngineError } from '@aglyn/tenant-data-admin/server/transfer-jobs'\nimport { FieldPath, FieldValue, Timestamp, type Firestore } from 'firebase-admin/firestore'\nimport { datasetRecordFilter, planRecordQuery } from '../components/dataset-record-filter'\nimport {\n datasetFilterToken,\n datasetFilterValuePath,\n datasetFilterWords,\n datasetIntegrityFields,\n datasetIntegrityUpdate,\n effectiveDatasetModel,\n type DatasetFieldDefinition,\n type DatasetModel,\n} from '../model/dataset-models'\nimport { datasetDisplayName, describeDatasetRecordErrors } from '../model/datasets'\nimport { fillRecordAddresses } from '../record-pages/record-pages'\nimport { announceDatasetRecords } from '../server/announce-dataset-records'\nimport { datasetTransferResourceKey } from './dataset-transfer-key'\nimport {\n datasetFieldIdOf,\n datasetFieldOfPicklist,\n datasetHoldsKey,\n datasetMatchKeyOffer,\n datasetMatchKeyQuery,\n datasetOptionsPicklist,\n datasetRecordTransferValues,\n datasetStorageValues,\n datasetTransferCatalog,\n datasetTransferFieldId,\n datasetWriteErrors,\n isOptionsField,\n limitDatasetCreates,\n refuseInvalidDatasetRows,\n type DatasetReferenceTarget,\n} from './dataset-transfer-model'\n\n/*==========================================\n * A DATASET'S RECORDS AS A TRANSFER RESOURCE (AGL-3530) — the server half.\n *\n * `data.dataset:<datasetId>` (`plugins.config.json`, `instances`): the job\n * engine (`@aglyn/tenant-data-admin/server/transfer-jobs`) reads the file,\n * matches it and keeps the job; these hooks answer for one dataset — its\n * catalog and match keys, its records page by page and by key, the dry run\n * held to its model, and the writes and their undo.\n *\n * ## The writes are the dataset's own\n *\n * A create goes the way `/api/orgs/datasets` creates (`datasets-route.ts`):\n * the values coerced, page addresses filled in and validated against the\n * model, and the `recordsPerDataset` count read, decided and written in ONE\n * transaction, the data storage band refusing first. An update is the\n * console's update: the record's values with the row's changes over them,\n * written with `mergeFields` so `filterValues` is replaced whole. Every write\n * carries the integrity index (`datasetIntegrityFields` on a create,\n * `datasetIntegrityUpdate` on an update), so `restrict` and `setNull` keep\n * answering by query. The live pages repeating over the dataset are told\n * once per chunk.\n *\n * ## A retried chunk never creates a record twice\n *\n * A created record is named by {@link transferRecordId} — the job and the\n * row — so a chunk the engine retries after a write landed but before the\n * ledger heard of it finds the record already there and writes nothing.\n *\n * ## References are the engine's lookups (AGL-3556)\n *\n * A reference field is a lookup of its target dataset, so the engine resolves\n * it through these same hooks asked as THAT dataset ({@link lookupDatasetRecords},\n * {@link suggestDatasetRecords}). A value the person chose to create arrives\n * as `transferLookupNewValue(name)`; {@link createNamedReferences} creates it\n * in the target through the same create as a row's record — once per name\n * however many rows name it, under an id derived from the job and the name,\n * so a retried chunk finds the record it already created. Undo leaves such a\n * record: other records may point at it by then.\n *\n * ## Undo keeps the references whole\n *\n * Undo deletes a created record the way the Data card deletes one: every\n * dataset that references this one is asked, by query, whether a record\n * still points at it. `restrict` keeps the record (reported back as a\n * conflict); `setNull` strips the reference from the holders first.\n *=========================================*/\n\n/** What the hooks run against; a spec hands in a double. */\nexport interface DatasetTransferDeps {\n firestore: Firestore\n}\n\nconst productionDeps = (): DatasetTransferDeps => ({ firestore: firebaseAdmin.app().firestore() })\n\n/** The page an export reads when the engine names none. */\nconst PAGE_ROWS = 500\n\n/** Firestore's cap on the values of one `in` query. */\nconst IN_MAX = 30\n\n/** The most documents one `getAll` reads. */\nconst GET_ALL_MAX = 300\n\n/** What a merging record write names, so each field is replaced whole. */\nconst RECORD_MERGE_FIELDS = ['values', 'referencedIds', 'filterKeys', 'filterValues', 'updatedAt']\n\nconst chunked = <T>(items: readonly T[], size: number): T[][] => {\n const out: T[][] = []\n for (let at = 0; at < items.length; at += size) out.push(items.slice(at, at + size))\n return out\n}\n\nconst notFound = () => new TransferEngineError('notFound', 404, 'No such dataset.')\n\n/** The nanoid alphabet `createResourceUid` names documents from. */\nconst ID_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'\n\n/**\n * The id a record created by an import's row is given: ten characters of the\n * resource-id alphabet, derived from the job and the row, so a retried chunk\n * names the record it already wrote rather than writing a second one.\n */\nexport function transferRecordId(jobId: string, row: number): string {\n const digest = createHash('sha256').update(`${jobId}:${row}`).digest()\n let id = ''\n for (let at = 0; at < 10; at += 1) id += ID_ALPHABET[(digest[at] as number) % ID_ALPHABET.length]\n return id\n}\n\n/*------------------------------------------\n * The dataset a context names\n *-----------------------------------------*/\n\ninterface LoadedDataset {\n id: string\n label: string\n model: DatasetModel\n visibleTo: string[]\n orgRef: FirebaseFirestore.DocumentReference\n ref: FirebaseFirestore.DocumentReference\n records: FirebaseFirestore.CollectionReference\n}\n\n/**\n * The dataset per context object: the engine hands one context to every\n * hook a step runs, so an export's pages and a plan's lookups read the\n * dataset and the member once.\n */\nconst loaded = new WeakMap<TransferResourceContext, Promise<LoadedDataset>>()\n\nasync function readDataset(deps: DatasetTransferDeps, ctx: TransferResourceContext): Promise<LoadedDataset> {\n const datasetId = transferResourceInstanceOf(ctx)\n if (!datasetId) throw notFound()\n const orgRef = deps.firestore.collection('orgs').doc(ctx.orgId)\n const ref = orgRef.collection('datasets').doc(datasetId)\n const snapshot = await ref.get()\n const data = snapshot.exists ? (snapshot.data() as Record<string, unknown>) : null\n if (!data || data['deletedAt'] != null) throw notFound()\n const visibleTo = Array.isArray(data['visibleTo']) ? (data['visibleTo'] as string[]) : []\n /*\n * The member has to SEE the dataset, as on every other door onto its\n * records (`datasets-route.ts`): the gate asked about the workspace, and a\n * collaborator scoped to one site passes it. The Admin SDK reads past the\n * rules, so this IS the enforcement, answered with the same 404 as a\n * dataset that does not exist. No membership is staff (the gate admitted\n * them) or the sweep resuming a job; the data store's release flag holds\n * for everyone else.\n */\n if (ctx.actorUid) {\n const member = (await resolveOrgMembership(ctx.actorUid, ctx.orgId))?.member\n if (member) {\n if (!memberCanSee(member as never, visibleTo)) throw notFound()\n if (!(await isServerReleaseFlagOnForOrg('release_data_store', ctx.orgId))) {\n throw new TransferEngineError('notFound', 404, 'Datasets are not available on this workspace.')\n }\n }\n }\n return {\n id: datasetId,\n label: datasetDisplayName(data) || 'Fields',\n model: effectiveDatasetModel(data as { model?: DatasetModel; fields?: string[] }),\n visibleTo,\n orgRef,\n ref,\n records: ref.collection('records'),\n }\n}\n\nfunction datasetFor(deps: DatasetTransferDeps, ctx: TransferResourceContext): Promise<LoadedDataset> {\n let pending = loaded.get(ctx)\n if (!pending) {\n pending = readDataset(deps, ctx)\n loaded.set(ctx, pending)\n pending.catch(() => loaded.delete(ctx))\n }\n return pending\n}\n\n/** The context a referenced dataset is asked in: the same job, actor and site, as that dataset. */\nfunction targetContext(ctx: TransferResourceContext, datasetId: string): TransferResourceContext {\n return { ...ctx, resource: datasetTransferResourceKey(datasetId) }\n}\n\n/**\n * Each dataset the model references, as the person may use it: whether they\n * can read it (the same check as opening it), and its model when they can.\n */\nasync function datasetReferenceTargets(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n model: DatasetModel,\n): Promise<Record<string, DatasetReferenceTarget>> {\n const out: Record<string, DatasetReferenceTarget> = {}\n for (const field of Object.values(model.fields ?? {})) {\n const targetId = field?.type === 'reference' ? field.reference?.datasetId : undefined\n if (!targetId || out[targetId]) continue\n try {\n out[targetId] = { readable: true, model: (await datasetFor(deps, targetContext(ctx, targetId))).model }\n } catch (error) {\n if (!(error instanceof TransferEngineError && error.code === 'notFound')) throw error\n out[targetId] = { readable: false }\n }\n }\n return out\n}\n\n/** A record document as transfer values. */\nfunction viewOf(model: DatasetModel, doc: FirebaseFirestore.DocumentSnapshot): Record<string, unknown> {\n const data = (doc.data() ?? {}) as Record<string, unknown>\n return datasetRecordTransferValues(model, {\n id: doc.id,\n values: (data['values'] ?? {}) as Record<string, unknown>,\n createdAt: data['createdAt'],\n updatedAt: data['updatedAt'],\n })\n}\n\nasync function getAll(\n firestore: Firestore,\n refs: readonly FirebaseFirestore.DocumentReference[],\n): Promise<FirebaseFirestore.DocumentSnapshot[]> {\n const out: FirebaseFirestore.DocumentSnapshot[] = []\n for (const slice of chunked(refs, GET_ALL_MAX)) if (slice.length) out.push(...(await firestore.getAll(...slice)))\n return out\n}\n\n/*------------------------------------------\n * Reading: the export's pages and the count\n *-----------------------------------------*/\n\n/** The records table's filter as the Data card sends it: the grid's clauses and the search words. */\nexport interface DatasetTransferFilter {\n clauses: ListFilterClause[]\n search: string[]\n}\n\nfunction readFilter(filter: Readonly<Record<string, unknown>>): DatasetTransferFilter {\n const clauses = Array.isArray(filter['clauses']) ? filter['clauses'] : []\n const search = Array.isArray(filter['search']) ? filter['search'] : []\n const clause = (entry: unknown): entry is ListFilterClause => {\n const one = entry as Record<string, unknown> | null\n return Boolean(one) && typeof one?.['field'] === 'string' && typeof one?.['op'] === 'string' && typeof one?.['value'] === 'string'\n }\n if (!clauses.every(clause) || !search.every((word) => typeof word === 'string')) {\n throw new TransferEngineError('invalid', 400, 'The filter could not be read.')\n }\n return { clauses: clauses as ListFilterClause[], search: search as string[] }\n}\n\n/**\n * Every record, or the ones the records table's filter matches — the same\n * one query the table pages (`planRecordQuery`), ordered by document id.\n * What the table could not put on its query it did not apply either, so\n * neither does the export.\n */\nfunction recordsQuery(dataset: LoadedDataset, options: TransferReadOptions | undefined): FirebaseFirestore.Query {\n if (!options?.filter) return dataset.records.orderBy(FieldPath.documentId())\n const filter = readFilter(options.filter)\n const { plan } = planRecordQuery(dataset.model, datasetRecordFilter(dataset.model), filter.clauses, filter.search)\n return applyListQuery(dataset.records, plan)\n}\n\n/** A collaborator's scope tokens must reach the dataset; records carry no scope of their own. */\nfunction assertScope(dataset: LoadedDataset, options: TransferReadOptions | undefined): void {\n if (options?.scopeTokens && !options.scopeTokens.some((token) => dataset.visibleTo.includes(token))) throw notFound()\n}\n\nfunction pick(view: Readonly<Record<string, unknown>>, fieldIds: readonly string[]): Record<string, unknown> {\n return Object.fromEntries(fieldIds.filter((fieldId) => view[fieldId] !== undefined).map((fieldId) => [fieldId, view[fieldId]]))\n}\n\nexport async function countDatasetRecords(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n options: TransferReadOptions,\n): Promise<number> {\n const dataset = await datasetFor(deps, ctx)\n assertScope(dataset, options)\n if (options.ids) {\n let total = 0\n for (const slice of chunked(options.ids, IN_MAX)) {\n total += Number((await dataset.records.where(FieldPath.documentId(), 'in', slice).count().get()).data().count ?? 0)\n }\n return total\n }\n return Number((await recordsQuery(dataset, options).count().get()).data().count ?? 0)\n}\n\nexport async function readDatasetPage(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n cursor: string | null,\n fieldIds: readonly string[],\n options?: TransferReadOptions,\n): Promise<TransferReadPage> {\n const dataset = await datasetFor(deps, ctx)\n assertScope(dataset, options)\n const size = Math.max(1, Math.min(options?.pageSize ?? PAGE_ROWS, PAGE_ROWS))\n if (options?.ids) {\n // The selection, in the order it was made; the cursor is how far through it.\n const offset = Math.max(0, Number(cursor ?? 0) || 0)\n const slice = options.ids.slice(offset, offset + size)\n const docs = await getAll(deps.firestore, slice.map((id) => dataset.records.doc(id)))\n return {\n rows: docs.filter((doc) => doc.exists).map((doc) => pick(viewOf(dataset.model, doc), fieldIds)),\n next: offset + size < options.ids.length ? String(offset + size) : null,\n }\n }\n let query = recordsQuery(dataset, options)\n if (cursor) query = query.startAfter(cursor)\n const snapshot = await query.limit(size).get()\n const docs = snapshot.docs\n return {\n rows: docs.map((doc) => pick(viewOf(dataset.model, doc), fieldIds)),\n next: docs.length === size ? (docs[docs.length - 1] as FirebaseFirestore.QueryDocumentSnapshot).id : null,\n }\n}\n\n/*------------------------------------------\n * Matching\n *-----------------------------------------*/\n\nexport async function lookupDatasetRecords(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n requests: readonly MatchLookupRequest[],\n): Promise<TransferLookupResult> {\n const dataset = await datasetFor(deps, ctx)\n const lookup = new Map<string, string[]>()\n const records = new Map<string, Readonly<Record<string, unknown>>>()\n const file = (fieldId: string, normalized: string, id: string) => {\n const at = matchLookupKey(fieldId, normalized)\n const ids = lookup.get(at) ?? []\n if (!ids.includes(id)) ids.push(id)\n lookup.set(at, ids)\n }\n for (const request of requests) {\n if (!request.values.length) continue\n if (request.fieldId === TRANSFER_ID_FIELD) {\n const docs = await getAll(deps.firestore, request.values.map((id) => dataset.records.doc(id)))\n for (const doc of docs) {\n if (!doc.exists) continue\n records.set(doc.id, viewOf(dataset.model, doc))\n file(request.fieldId, doc.id, doc.id)\n }\n continue\n }\n // Asked of `filterValues`, the index the records table's own filters\n // read; a value clipped there is held to the whole value below.\n const asked = new Map<string | number, string[]>()\n for (const normalized of request.values) {\n const query = datasetMatchKeyQuery(dataset.model, request, normalized)\n if (!query) continue\n asked.set(query.value, [...(asked.get(query.value) ?? []), normalized])\n }\n const path = datasetFilterValuePath(datasetFieldIdOf(request.fieldId) ?? '')\n if (!path) continue\n for (const slice of chunked([...asked.keys()], IN_MAX)) {\n const snapshot = await dataset.records.where(path, 'in', slice).get()\n for (const doc of snapshot.docs) {\n const view = viewOf(dataset.model, doc)\n let held = false\n for (const normalized of slice.flatMap((value) => asked.get(value) ?? [])) {\n if (!datasetHoldsKey(view, request, normalized)) continue\n file(request.fieldId, normalized, doc.id)\n held = true\n }\n if (held) records.set(doc.id, view)\n }\n }\n }\n return { lookup, records }\n}\n\n/** The most distinct word queries one suggestion request runs. */\nconst SUGGEST_QUERIES_MAX = 200\n\n/** The records one word query reads. */\nconst SUGGEST_READ = 20\n\n/** The words of a value its candidates are asked by. */\nconst SUGGEST_WORDS = 2\n\n/** The leading characters a word is also asked by, so a later misspelling still finds it. */\nconst SUGGEST_STEM = 3\n\n/**\n * Records named LIKE each value a reference could not find, for the person\n * to pick from: each plain text `by` field asked for records with a word that\n * starts with one of the value's first words, or with its first few letters\n * (the `filterKeys` prefix tokens the records table's `contains` reads), then\n * ranked by how close the whole name is. Values past the query budget get none.\n */\nexport async function suggestDatasetRecords(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n request: TransferLookupSuggestRequest,\n): Promise<Record<string, TransferLookupSuggestion[]>> {\n const dataset = await datasetFor(deps, ctx)\n const fields = request.by\n .map((transferId) => datasetFieldIdOf(transferId))\n .filter((fieldId): fieldId is string => {\n const field = fieldId ? dataset.model.fields?.[fieldId] : undefined\n return field?.type === 'text' && !isOptionsField(field)\n })\n const asked = new Map<string, Promise<Array<{ recordId: string; label: string }>>>()\n const ask = (fieldId: string, token: string) => {\n const at = `${fieldId}\\u0000${token}`\n let pending = asked.get(at)\n if (!pending) {\n pending = dataset.records\n .where('filterKeys', 'array-contains', token)\n .limit(SUGGEST_READ)\n .get()\n .then((snapshot) =>\n snapshot.docs.map((doc) => ({\n recordId: doc.id,\n label: String(((doc.get('values') ?? {}) as Record<string, unknown>)[fieldId] ?? '').trim(),\n })),\n )\n asked.set(at, pending)\n }\n return pending\n }\n const answer: Record<string, TransferLookupSuggestion[]> = {}\n for (const value of request.values) {\n const candidates: TransferLookupSuggestion[] = []\n for (const fieldId of fields) {\n for (const word of datasetFilterWords(value).slice(0, SUGGEST_WORDS)) {\n const stem = Array.from(word).slice(0, SUGGEST_STEM).join('')\n for (const asWord of new Set([word, stem])) {\n const token = datasetFilterToken(dataset.model, { field: fieldId, op: 'contains', value: asWord })\n if (!token || (asked.size >= SUGGEST_QUERIES_MAX && !asked.has(`${fieldId}\\u0000${token}`))) continue\n candidates.push(...(await ask(fieldId, token)).filter((candidate) => candidate.label))\n }\n }\n }\n answer[value] = rankTransferLookupSuggestions(value, candidates, { limit: 5 })\n }\n return answer\n}\n\n/*------------------------------------------\n * Options fields as picklists\n *-----------------------------------------*/\n\nexport async function datasetPicklists(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n picklistIds: readonly string[],\n): Promise<Record<string, TransferPicklistList>> {\n const { model } = await datasetFor(deps, ctx)\n const out: Record<string, TransferPicklistList> = {}\n for (const picklistId of picklistIds) {\n const field = model.fields?.[datasetFieldOfPicklist(picklistId) ?? '']\n if (field) out[picklistId] = datasetOptionsPicklist(field)\n }\n return out\n}\n\n/**\n * The values the person chose to add to an options field, added to the\n * field's options in the dataset's model — the schema the Schema dialog\n * edits — before the import's first write. A value already there is left.\n */\nexport async function addDatasetOptions(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n picklistId: string,\n values: readonly PicklistValue[],\n): Promise<void> {\n const dataset = await datasetFor(deps, ctx)\n const fieldId = datasetFieldOfPicklist(picklistId)\n if (!fieldId) return\n await deps.firestore.runTransaction(async (tx) => {\n const snapshot = await tx.get(dataset.ref)\n const model = effectiveDatasetModel((snapshot.data() ?? {}) as { model?: DatasetModel; fields?: string[] })\n const field = model.fields?.[fieldId]\n if (!field) return\n const options = [...(field.validation?.options ?? [])]\n const added = values.map((value) => value.label).filter((label) => label && !options.includes(label))\n if (!added.length) return\n tx.update(dataset.ref, {\n model: {\n ...model,\n fields: { ...model.fields, [fieldId]: { ...field, validation: { ...field.validation, options: [...options, ...added] } } },\n },\n updatedAt: Timestamp.now(),\n })\n })\n // The writes that follow validate against the options just added.\n loaded.delete(ctx)\n}\n\n/*------------------------------------------\n * The dry run\n *-----------------------------------------*/\n\n/**\n * The records this import may create, from the plan's `recordsPerDataset`\n * against the dataset's real size — and none while the data storage band is\n * full — so the dry run fails what Apply would refuse.\n */\nasync function createLimits(dataset: LoadedDataset): Promise<Pick<TransferPlanLimits, 'maxCreates'>> {\n const org = (await dataset.orgRef.get()).data() as never\n if (await dataStorageRefusal(org, dataset.orgRef)) return { maxCreates: 0 }\n const live = Number((await dataset.records.count().get()).data().count ?? 0)\n const { remaining } = checkQuota(org, 'recordsPerDataset', live)\n return Number.isFinite(remaining) ? { maxCreates: remaining } : {}\n}\n\n/*------------------------------------------\n * Apply\n *-----------------------------------------*/\n\ninterface Landed {\n result: TransferRowResult\n undo?: TransferUndoEntry\n}\n\nconst failed = (row: PlannedTransferRow, reason: string, message?: string): Landed => ({\n result: {\n row: row.index,\n outcome: 'failed',\n ...(row.recordId ? { recordId: row.recordId } : {}),\n reason,\n ...(message ? { message } : {}),\n },\n})\n\nfunction only(view: Readonly<Record<string, unknown>>, fieldIds: readonly string[]): Record<string, unknown> {\n return Object.fromEntries(fieldIds.map((fieldId) => [fieldId, view[fieldId] ?? null]))\n}\n\n/** Updates: each record's values with the row's changes over them, in one batch. */\nasync function applyUpdates(\n deps: DatasetTransferDeps,\n dataset: LoadedDataset,\n rows: readonly PlannedTransferRow[],\n): Promise<Landed[]> {\n if (!rows.length) return []\n const { model, records } = dataset\n const docs = await getAll(deps.firestore, rows.map((row) => records.doc(row.recordId as string)))\n const batch = deps.firestore.batch()\n const landed: Landed[] = []\n let writes = 0\n rows.forEach((row, at) => {\n const doc = docs[at] as FirebaseFirestore.DocumentSnapshot\n if (!doc.exists) return landed.push(failed(row, 'matchedRecordMissing'))\n const stored = { ...((doc.get('values') ?? {}) as Record<string, unknown>) }\n const before = viewOf(model, doc)\n const { values: changes, cleared } = datasetStorageValues(\n model,\n Object.fromEntries(row.diff.map((change) => [change.fieldId, change.after])),\n )\n const merged: Record<string, unknown> = { ...stored, ...changes }\n for (const fieldId of cleared) delete merged[fieldId]\n const coerced = fillRecordAddresses(model, merged)\n const errors = datasetWriteErrors(model, coerced, new Set([...Object.keys(changes), ...cleared]))\n if (Object.keys(errors).length) return landed.push(failed(row, 'refusedValue', describeDatasetRecordErrors(errors)))\n batch.set(\n doc.ref,\n {\n values: coerced,\n // The merging form: an update that clears the last reference has to\n // REMOVE the index rather than omit it.\n ...datasetIntegrityUpdate(model, coerced, FieldValue.delete()),\n updatedAt: Timestamp.now(),\n },\n // Each named field replaced whole: `merge: true` would fold the new\n // `filterValues` into the stored map and keep a cleared value.\n { mergeFields: RECORD_MERGE_FIELDS },\n )\n writes += 1\n const touched = row.diff.map((change) => change.fieldId)\n const after = datasetRecordTransferValues(model, { values: coerced })\n landed.push({\n result: { row: row.index, outcome: 'updated', recordId: doc.id },\n undo: {\n row: row.index,\n recordId: doc.id,\n action: 'updated',\n previous: only(before, touched),\n written: only(after, touched),\n modes: Object.fromEntries(row.diff.map((change) => [change.fieldId, change.mode])),\n },\n })\n return undefined\n })\n if (writes) await batch.commit()\n return landed\n}\n\n/** What became of one new record: written, already there (an earlier attempt's), or past the plan. */\ntype NewRecordOutcome = 'created' | 'existed' | 'refused'\n\n/** Why a dataset's new records were not written at all. */\ninterface NewRecordsRefused {\n message: string\n}\n\n/**\n * New records written as `/api/orgs/datasets` creates them: the data storage\n * band refusing first, then counted, decided and written in ONE transaction\n * against `recordsPerDataset`, each with the integrity index of its values.\n * A record already there is left as it is. `values` are validated already.\n */\nasync function writeNewRecords(\n deps: DatasetTransferDeps,\n dataset: LoadedDataset,\n entries: ReadonlyArray<{ id: string; values: Record<string, unknown> }>,\n): Promise<{ outcomes: NewRecordOutcome[]; limitMessage: string } | NewRecordsRefused> {\n const { model, records } = dataset\n const org = (await dataset.orgRef.get()).data() as never\n // Bytes, not rows (AGL-2163), before the write as on every other create.\n const storage = await dataStorageRefusal(org, dataset.orgRef)\n if (storage) return { message: `Dataset storage is full (${storage.includedMb} MB on this plan) — upgrade in Billing` }\n const outcomes = await deps.firestore.runTransaction(async (tx) => {\n const refs = entries.map((entry) => records.doc(entry.id))\n const existing = await tx.getAll(...refs)\n const live = Number((await tx.get(records.count())).data().count ?? 0)\n let room = checkQuota(org, 'recordsPerDataset', live).remaining\n let order = live\n return entries.map((entry, at): NewRecordOutcome => {\n if ((existing[at] as FirebaseFirestore.DocumentSnapshot).exists) return 'existed'\n if (room <= 0) return 'refused'\n room -= 1\n tx.create(refs[at] as FirebaseFirestore.DocumentReference, {\n values: entry.values,\n // The integrity index the delete check queries, on the same write as\n // the values it describes.\n ...datasetIntegrityFields(model, entry.values),\n order: order++,\n createdAt: Timestamp.now(),\n updatedAt: Timestamp.now(),\n })\n return 'created'\n })\n })\n const limit = checkQuota(org, 'recordsPerDataset', 0).limit\n return { outcomes, limitMessage: `Record limit reached (${limit}) — upgrade in Billing` }\n}\n\n/**\n * Creates, through {@link writeNewRecords}: validated first, so a row the\n * model refuses fails naming why. A row the plan's room has run out for\n * fails as past the plan; a record this job already created (a retried\n * chunk) is reported, not written again.\n */\nasync function applyCreates(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n dataset: LoadedDataset,\n rows: readonly PlannedTransferRow[],\n): Promise<Landed[]> {\n if (!rows.length) return []\n const { model } = dataset\n const landed: Landed[] = []\n const prepared: Array<{ row: PlannedTransferRow; id: string; values: Record<string, unknown> }> = []\n for (const row of rows) {\n const { values } = datasetStorageValues(\n model,\n Object.fromEntries(row.diff.map((change) => [change.fieldId, change.after])),\n )\n const coerced = fillRecordAddresses(model, values)\n const errors = datasetWriteErrors(model, coerced)\n if (Object.keys(errors).length) landed.push(failed(row, 'refusedValue', describeDatasetRecordErrors(errors)))\n else prepared.push({ row, id: transferRecordId(ctx.jobId ?? ctx.resource, row.index), values: coerced })\n }\n if (!prepared.length) return landed\n const written = await writeNewRecords(deps, dataset, prepared)\n if ('message' in written) return [...landed, ...prepared.map((entry) => failed(entry.row, 'planLimit', written.message))]\n prepared.forEach((entry, at) => {\n if (written.outcomes[at] === 'refused') {\n landed.push(failed(entry.row, 'planLimit', written.limitMessage))\n return\n }\n const values = datasetRecordTransferValues(model, { values: entry.values })\n delete values[TRANSFER_ID_FIELD]\n landed.push({\n result: { row: entry.row.index, outcome: 'created', recordId: entry.id },\n undo: { row: entry.row.index, recordId: entry.id, action: 'created', written: values },\n })\n })\n return landed\n}\n\n/*------------------------------------------\n * References the person chose to create\n *-----------------------------------------*/\n\n/**\n * The id a record created for a reference's name is given: ten characters\n * of the resource-id alphabet, from the job, the target, the field it is\n * named in and the name as the engine keys it — so every row naming it, in\n * any chunk, and a retried chunk all land on the one record.\n */\nexport function transferReferenceRecordId(jobId: string, datasetId: string, displayFieldId: string, name: string): string {\n return transferRecordId(`${jobId}:${datasetId}:${displayFieldId}:${transferLookupKey(name)}`, 0)\n}\n\n/** The names a reference value asks to create, by the engine's marker. */\nfunction namesToCreate(value: unknown): string[] {\n const names: string[] = []\n for (const item of Array.isArray(value) ? value : [value]) {\n const name = transferLookupNewName(item)\n if (name) names.push(name)\n }\n return names\n}\n\n/** A reference field with a target and the field it names records by. */\ninterface NamedReference {\n transferId: string\n targetId: string\n displayFieldId: string\n}\n\nfunction namedReferences(model: DatasetModel): NamedReference[] {\n const out: NamedReference[] = []\n for (const fieldId of model.order ?? []) {\n const field = model.fields?.[fieldId] as DatasetFieldDefinition | undefined\n const targetId = field?.type === 'reference' ? field.reference?.datasetId : undefined\n const displayFieldId = field?.reference?.displayFieldId\n if (targetId && displayFieldId) out.push({ transferId: datasetTransferFieldId(fieldId), targetId, displayFieldId })\n }\n return out\n}\n\n/**\n * The records the person chose to create from a reference's names, created\n * in the referenced dataset before the rows that name them are written: each\n * name once, holding the name in the field the reference displays, held to\n * the target's model and its plan, through {@link writeNewRecords}. The rows\n * come back naming the records' ids; a row naming one that could not be\n * created fails, saying why.\n */\nasync function createNamedReferences(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n dataset: LoadedDataset,\n rows: readonly PlannedTransferRow[],\n): Promise<{ rows: PlannedTransferRow[]; failed: Landed[] }> {\n const references = namedReferences(dataset.model)\n // Target and display field → name key → the name as the file spelled it.\n const wanted = new Map<string, { reference: NamedReference; names: Map<string, string> }>()\n for (const row of rows) {\n for (const change of row.diff) {\n const reference = references.find((one) => one.transferId === change.fieldId)\n const names = reference ? namesToCreate(change.after) : []\n if (!reference || !names.length) continue\n const at = `${reference.targetId}\\u0000${reference.displayFieldId}`\n const group = wanted.get(at) ?? { reference, names: new Map<string, string>() }\n for (const name of names) if (!group.names.has(transferLookupKey(name))) group.names.set(transferLookupKey(name), name)\n wanted.set(at, group)\n }\n }\n if (!wanted.size) return { rows: [...rows], failed: [] }\n\n // Name key → the record's id, or why it could not be created; per target and display field.\n const outcome = new Map<string, Map<string, { id: string } | { message: string }>>()\n const jobId = ctx.jobId ?? ctx.resource\n for (const [at, { reference, names }] of wanted) {\n const decided = new Map<string, { id: string } | { message: string }>()\n outcome.set(at, decided)\n const target = await datasetFor(deps, targetContext(ctx, reference.targetId))\n await ensureDeclaredCustomFieldTypes(target.model)\n const entries: Array<{ key: string; id: string; values: Record<string, unknown> }> = []\n for (const [key, name] of names) {\n const values = fillRecordAddresses(target.model, datasetStorageValues(target.model, { [datasetTransferFieldId(reference.displayFieldId)]: name }).values)\n const errors = datasetWriteErrors(target.model, values)\n if (Object.keys(errors).length) {\n decided.set(key, { message: `“${name}” could not be created in ${target.label}: ${describeDatasetRecordErrors(errors)}` })\n } else {\n entries.push({ key, id: transferReferenceRecordId(jobId, target.id, reference.displayFieldId, name), values })\n }\n }\n if (!entries.length) continue\n const written = await writeNewRecords(deps, target, entries)\n entries.forEach((entry, index) => {\n const name = names.get(entry.key) as string\n if ('message' in written) decided.set(entry.key, { message: `“${name}” could not be created in ${target.label}: ${written.message}` })\n else if (written.outcomes[index] === 'refused') {\n decided.set(entry.key, { message: `“${name}” could not be created in ${target.label}: ${written.limitMessage}` })\n } else decided.set(entry.key, { id: entry.id })\n })\n if (!('message' in written) && written.outcomes.includes('created')) {\n await announceDatasetRecords({ firestore: deps.firestore, orgId: ctx.orgId, datasetId: target.id })\n }\n }\n\n const out: PlannedTransferRow[] = []\n const refused: Landed[] = []\n for (const row of rows) {\n const problems: string[] = []\n const diff = row.diff.map((change) => {\n const reference = references.find((one) => one.transferId === change.fieldId)\n if (!reference || !namesToCreate(change.after).length) return change\n const decided = outcome.get(`${reference.targetId}\\u0000${reference.displayFieldId}`)\n const idOf = (item: unknown): unknown => {\n const name = transferLookupNewName(item)\n if (!name) return item\n const one = decided?.get(transferLookupKey(name))\n if (one && 'id' in one) return one.id\n problems.push(one && 'message' in one ? one.message : `“${name}” could not be created.`)\n return item\n }\n return { ...change, after: Array.isArray(change.after) ? [...new Set(change.after.map(idOf))] : idOf(change.after) }\n })\n if (problems.length) refused.push(failed(row, 'refusedValue', [...new Set(problems)].join(' ')))\n else out.push({ ...row, diff })\n }\n return { rows: out, failed: refused }\n}\n\nexport async function applyDatasetChunk(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n chunk: { rows: PlannedTransferRow[] },\n writer: TransferApplyWriter,\n): Promise<TransferApplyResult> {\n const dataset = await datasetFor(deps, ctx)\n // A plugin's field validator runs only once its plugin registered it.\n await ensureDeclaredCustomFieldTypes(dataset.model)\n const results: TransferRowResult[] = []\n const undo: TransferUndoEntry[] = []\n const pending: PlannedTransferRow[] = []\n for (const row of chunk.rows) {\n const done = await writer.alreadyApplied(row.index)\n if (done) results.push(done)\n else pending.push(row)\n }\n // The records the rows' references name for creation, before the rows.\n const named = await createNamedReferences(deps, ctx, dataset, pending)\n const landed = [\n ...named.failed,\n ...(await applyUpdates(deps, dataset, named.rows.filter((row) => row.verdict === 'update' && row.recordId))),\n ...(await applyCreates(deps, ctx, dataset, named.rows.filter((row) => row.verdict === 'create'))),\n ]\n for (const entry of landed) {\n await writer.markApplied(entry.result, entry.undo)\n results.push(entry.result)\n if (entry.undo) undo.push(entry.undo)\n }\n // The pages repeating over the dataset, told once for the chunk (AGL-3113).\n if (undo.length) {\n await announceDatasetRecords({ firestore: deps.firestore, orgId: ctx.orgId, datasetId: dataset.id })\n }\n return { results, undo }\n}\n\n/*------------------------------------------\n * Undo\n *-----------------------------------------*/\n\n/**\n * Deletes a record the import created, keeping every reference to it whole\n * (see the block header). `false` when a `restrict` reference holds it.\n */\nasync function deleteWithIntegrity(\n deps: DatasetTransferDeps,\n dataset: LoadedDataset,\n recordId: string,\n): Promise<boolean> {\n const others = await dataset.orgRef.collection('datasets').get()\n const strips: Array<() => void> = []\n const batch = deps.firestore.batch()\n for (const other of others.docs) {\n const model = effectiveDatasetModel(other.data() as { model?: DatasetModel; fields?: string[] })\n const referencing = Object.keys(model.fields ?? {}).filter(\n (fieldId) => model.fields[fieldId]?.type === 'reference' && model.fields[fieldId]?.reference?.datasetId === dataset.id,\n )\n if (!referencing.length) continue\n const holders = (\n await other.ref.collection('records').where('referencedIds', 'array-contains', recordId).get()\n ).docs.filter((doc) =>\n referencing.some((fieldId) => {\n const stored = ((doc.get('values') ?? {}) as Record<string, unknown>)[fieldId]\n return Array.isArray(stored) ? stored.includes(recordId) : stored === recordId\n }),\n )\n if (!holders.length) continue\n if (referencing.some((fieldId) => model.fields[fieldId]?.reference?.onDelete === 'restrict')) return false\n for (const holder of holders) {\n strips.push(() => {\n const values = { ...((holder.get('values') ?? {}) as Record<string, unknown>) }\n for (const fieldId of referencing) {\n const stored = values[fieldId]\n if (Array.isArray(stored)) values[fieldId] = stored.filter((id) => id !== recordId)\n else if (stored === recordId) delete values[fieldId]\n }\n batch.update(holder.ref, {\n values,\n ...datasetIntegrityUpdate(model, values, FieldValue.delete()),\n updatedAt: Timestamp.now(),\n })\n })\n }\n }\n for (const strip of strips) strip()\n batch.delete(dataset.records.doc(recordId))\n await batch.commit()\n return true\n}\n\nexport async function revertDatasetChunk(\n deps: DatasetTransferDeps,\n ctx: TransferResourceContext,\n snapshot: { entries: TransferUndoEntry[] },\n decisions: TransferRevertDecisions = {},\n): Promise<TransferRevertResult> {\n const dataset = await datasetFor(deps, ctx)\n const { model, records } = dataset\n const done: TransferUndoStep[] = []\n const conflicts: TransferUndoStep[] = []\n if (!snapshot.entries.length) return { done, conflicts }\n const docs = await getAll(deps.firestore, snapshot.entries.map((entry) => records.doc(entry.recordId)))\n const restores = deps.firestore.batch()\n let restoring = 0\n for (const [at, entry] of snapshot.entries.entries()) {\n const doc = docs[at] as FirebaseFirestore.DocumentSnapshot\n const step = planTransferUndo(entry, doc.exists ? viewOf(model, doc) : null)\n const revert = step.action !== 'conflict' || decisions[entry.recordId] === 'revert'\n if (!revert) {\n conflicts.push(step)\n continue\n }\n if (step.action === 'nothing') {\n done.push(step)\n continue\n }\n if (step.action === 'delete' || (step.action === 'conflict' && entry.action === 'created')) {\n if (await deleteWithIntegrity(deps, dataset, entry.recordId)) done.push({ action: 'delete', recordId: entry.recordId })\n else conflicts.push(step)\n continue\n }\n // Restore (or a conflict the person chose to revert): the fields the\n // import wrote, back to what they held before it.\n const stored = { ...((doc.get('values') ?? {}) as Record<string, unknown>) }\n const { values, cleared } = datasetStorageValues(model, step.values)\n const merged: Record<string, unknown> = { ...stored, ...values }\n for (const fieldId of cleared) delete merged[fieldId]\n restores.set(\n doc.ref,\n {\n values: merged,\n ...datasetIntegrityUpdate(model, merged, FieldValue.delete()),\n updatedAt: Timestamp.now(),\n },\n { mergeFields: RECORD_MERGE_FIELDS },\n )\n restoring += 1\n done.push({ action: 'restore', recordId: entry.recordId, values: step.values })\n }\n if (restoring) await restores.commit()\n if (done.some((step) => step.action !== 'nothing')) {\n await announceDatasetRecords({ firestore: deps.firestore, orgId: ctx.orgId, datasetId: dataset.id })\n }\n return { done, conflicts }\n}\n\n/*------------------------------------------\n * The hooks\n *-----------------------------------------*/\n\n/** The records hooks for `data.dataset`, over `deps` (the Admin SDK's Firestore by default). */\nexport function datasetTransferHooks(deps: () => DatasetTransferDeps = productionDeps): TransferRecordsHooks {\n return {\n fields: async (ctx) => {\n const dataset = await datasetFor(deps(), ctx)\n return datasetTransferCatalog(dataset.model, dataset.label, await datasetReferenceTargets(deps(), ctx, dataset.model))\n },\n matchKeys: async (ctx) => datasetMatchKeyOffer((await datasetFor(deps(), ctx)).model),\n count: (ctx, options) => countDatasetRecords(deps(), ctx, options),\n readPage: (ctx, cursor, fieldIds, options) => readDatasetPage(deps(), ctx, cursor, fieldIds, options),\n lookup: (ctx, requests) => lookupDatasetRecords(deps(), ctx, requests),\n suggest: (ctx, request) => suggestDatasetRecords(deps(), ctx, request),\n picklists: (ctx, picklistIds) => datasetPicklists(deps(), ctx, picklistIds),\n addPicklistValues: (ctx, picklistId, values) => addDatasetOptions(deps(), ctx, picklistId, values),\n plan: async (ctx, input) => {\n const dataset = await datasetFor(deps(), ctx)\n await ensureDeclaredCustomFieldTypes(dataset.model)\n const plan = refuseInvalidDatasetRows(dataset.model, buildTransferPlan(input), input.existing)\n return limitDatasetCreates(plan, (await createLimits(dataset)).maxCreates)\n },\n apply: (ctx, chunk, writer) => applyDatasetChunk(deps(), ctx, chunk, writer),\n revert: (ctx, snapshot, decisions) => revertDatasetChunk(deps(), ctx, snapshot, decisions),\n }\n}\n"],"names":["createHash","TRANSFER_ID_FIELD","buildTransferPlan","matchLookupKey","planTransferUndo","rankTransferLookupSuggestions","transferLookupKey","transferLookupNewName","transferResourceInstanceOf","checkQuota","ensureDeclaredCustomFieldTypes","memberCanSee","dataStorageRefusal","firebaseAdmin","isServerReleaseFlagOnForOrg","resolveOrgMembership","applyListQuery","TransferEngineError","FieldPath","FieldValue","Timestamp","datasetRecordFilter","planRecordQuery","datasetFilterToken","datasetFilterValuePath","datasetFilterWords","datasetIntegrityFields","datasetIntegrityUpdate","effectiveDatasetModel","datasetDisplayName","describeDatasetRecordErrors","fillRecordAddresses","announceDatasetRecords","datasetTransferResourceKey","datasetFieldIdOf","datasetFieldOfPicklist","datasetHoldsKey","datasetMatchKeyOffer","datasetMatchKeyQuery","datasetOptionsPicklist","datasetRecordTransferValues","datasetStorageValues","datasetTransferCatalog","datasetTransferFieldId","datasetWriteErrors","isOptionsField","limitDatasetCreates","refuseInvalidDatasetRows","productionDeps","firestore","app","PAGE_ROWS","IN_MAX","GET_ALL_MAX","RECORD_MERGE_FIELDS","chunked","items","size","out","at","length","push","slice","notFound","ID_ALPHABET","transferRecordId","jobId","row","digest","update","id","loaded","WeakMap","readDataset","deps","ctx","datasetId","orgRef","collection","doc","orgId","ref","snapshot","get","data","exists","visibleTo","Array","isArray","actorUid","member","label","model","records","datasetFor","pending","set","catch","delete","targetContext","resource","datasetReferenceTargets","field","Object","values","fields","targetId","type","reference","undefined","readable","error","code","viewOf","createdAt","updatedAt","getAll","refs","readFilter","filter","clauses","search","clause","entry","one","Boolean","every","word","recordsQuery","dataset","options","orderBy","documentId","plan","assertScope","scopeTokens","some","token","includes","pick","view","fieldIds","fromEntries","fieldId","map","countDatasetRecords","ids","total","Number","where","count","readDatasetPage","cursor","Math","max","min","pageSize","offset","docs","rows","next","String","query","startAfter","limit","lookupDatasetRecords","requests","lookup","Map","file","normalized","request","asked","value","path","keys","held","flatMap","SUGGEST_QUERIES_MAX","SUGGEST_READ","SUGGEST_WORDS","SUGGEST_STEM","suggestDatasetRecords","by","transferId","ask","then","recordId","trim","answer","candidates","stem","from","join","asWord","Set","op","has","candidate","datasetPicklists","picklistIds","picklistId","addDatasetOptions","runTransaction","tx","validation","added","now","createLimits","org","maxCreates","live","remaining","isFinite","failed","reason","message","result","index","outcome","only","applyUpdates","batch","landed","writes","forEach","stored","before","changes","cleared","diff","change","after","merged","coerced","errors","mergeFields","touched","undo","action","previous","written","modes","mode","commit","writeNewRecords","entries","storage","includedMb","outcomes","existing","room","order","create","limitMessage","applyCreates","prepared","transferReferenceRecordId","displayFieldId","name","namesToCreate","names","item","namedReferences","createNamedReferences","references","wanted","find","group","decided","target","key","refused","problems","idOf","applyDatasetChunk","chunk","writer","results","done","alreadyApplied","named","verdict","markApplied","deleteWithIntegrity","others","strips","other","referencing","holders","onDelete","holder","strip","revertDatasetChunk","decisions","conflicts","restores","restoring","step","revert","datasetTransferHooks","matchKeys","readPage","suggest","picklists","addPicklistValues","input","apply"],"mappings":"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"}
@@ -1,36 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- import type { DatasetFieldDefinition, DatasetModel } from '@aglyn/aglyn/app-utils/dataset-models';
18
- /** Storage value → portable string (CSV cell / JSON value). */
19
- export declare function serializeDatasetValue(field: DatasetFieldDefinition, value: unknown): string;
20
- /**
21
- * The header line — field ids, in model order (AGL-2335).
22
- *
23
- * Split out of {@link datasetRecordsToCsv} so a server export can emit the
24
- * file a page at a time. The whole point of that export is that it is not
25
- * bounded by what one process can hold in memory, and a helper that only
26
- * takes a complete `rows` array cannot serve it. Everything below shares
27
- * one escaper: there are already five divergent hand-rolled CSV escapers in
28
- * this repo and this is not becoming the sixth.
29
- */
30
- export declare function datasetCsvHeader(model: DatasetModel): string;
31
- /** One record's values (storage form) → one CSV line, no terminator. */
32
- export declare function datasetCsvRow(model: DatasetModel, row: Record<string, unknown>): string;
33
- /** One record's values → the portable JSON object the JSON export emits. */
34
- export declare function datasetRecordToJson(model: DatasetModel, row: Record<string, unknown>): Record<string, string>;
35
- /** Rows (storage-form value maps) → CSV with a fieldId header row. */
36
- export declare function datasetRecordsToCsv(model: DatasetModel, rows: Array<Record<string, unknown>>): string;
@@ -1,85 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */ /**
17
- * Dataset CSV/JSON serialization (AGL-182). The predictable inverse of
18
- * `coerceDocumentValues`: ISO dates, `lat, lon` coordinates, comma-joined
19
- * lists, JSON maps, reference ids — so an export re-imports losslessly
20
- * through the same coercion.
21
- *
22
- * This plugin's own: the export route that streams a dataset a page at a
23
- * time and the hub's client-side download both write through it. The
24
- * escaper, the parser and the row-count check are the platform's
25
- * (`app-utils/csv`), because the email, commerce and CRM imports read files
26
- * too and may not reach this plugin.
27
- */ import { escapeCsvCell as csvEscape } from "@aglyn/aglyn/app-utils/csv";
28
- /** Storage value → portable string (CSV cell / JSON value). */ export function serializeDatasetValue(field, value) {
29
- if (value == null) return '';
30
- switch(field.type){
31
- case 'timestamp':
32
- return typeof value === 'number' && Number.isFinite(value) ? new Date(value).toISOString() : String(value);
33
- case 'coordinates':
34
- {
35
- const coordinates = value;
36
- return coordinates && typeof coordinates.latitude === 'number' && typeof coordinates.longitude === 'number' ? `${coordinates.latitude}, ${coordinates.longitude}` : String(value);
37
- }
38
- case 'sorted':
39
- case 'reference':
40
- return Array.isArray(value) ? value.join(', ') : String(value);
41
- case 'map':
42
- try {
43
- return JSON.stringify(value);
44
- } catch (unused) {
45
- return String(value);
46
- }
47
- default:
48
- return String(value);
49
- }
50
- }
51
- /**
52
- * The header line — field ids, in model order (AGL-2335).
53
- *
54
- * Split out of {@link datasetRecordsToCsv} so a server export can emit the
55
- * file a page at a time. The whole point of that export is that it is not
56
- * bounded by what one process can hold in memory, and a helper that only
57
- * takes a complete `rows` array cannot serve it. Everything below shares
58
- * one escaper: there are already five divergent hand-rolled CSV escapers in
59
- * this repo and this is not becoming the sixth.
60
- */ export function datasetCsvHeader(model) {
61
- return model.order.map(csvEscape).join(',');
62
- }
63
- /** One record's values (storage form) → one CSV line, no terminator. */ export function datasetCsvRow(model, row) {
64
- return model.order.map((fieldId)=>{
65
- const field = model.fields[fieldId];
66
- return csvEscape(field ? serializeDatasetValue(field, row[fieldId]) : '');
67
- }).join(',');
68
- }
69
- /** One record's values → the portable JSON object the JSON export emits. */ export function datasetRecordToJson(model, row) {
70
- return Object.fromEntries(model.order.map((fieldId)=>{
71
- const field = model.fields[fieldId];
72
- return [
73
- fieldId,
74
- field ? serializeDatasetValue(field, row[fieldId]) : ''
75
- ];
76
- }));
77
- }
78
- /** Rows (storage-form value maps) → CSV with a fieldId header row. */ export function datasetRecordsToCsv(model, rows) {
79
- return [
80
- datasetCsvHeader(model),
81
- ...rows.map((row)=>datasetCsvRow(model, row))
82
- ].join('\n');
83
- }
84
-
85
- //# sourceMappingURL=dataset-csv.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/data/src/lib/model/dataset-csv.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Dataset CSV/JSON serialization (AGL-182). The predictable inverse of\n * `coerceDocumentValues`: ISO dates, `lat, lon` coordinates, comma-joined\n * lists, JSON maps, reference ids — so an export re-imports losslessly\n * through the same coercion.\n *\n * This plugin's own: the export route that streams a dataset a page at a\n * time and the hub's client-side download both write through it. The\n * escaper, the parser and the row-count check are the platform's\n * (`app-utils/csv`), because the email, commerce and CRM imports read files\n * too and may not reach this plugin.\n */\n\nimport { escapeCsvCell as csvEscape } from '@aglyn/aglyn/app-utils/csv'\nimport type {\n DatasetFieldDefinition,\n DatasetModel,\n} from '@aglyn/aglyn/app-utils/dataset-models'\n\n\n/** Storage value → portable string (CSV cell / JSON value). */\nexport function serializeDatasetValue(\n field: DatasetFieldDefinition,\n value: unknown,\n): string {\n if (value == null) return ''\n switch (field.type) {\n case 'timestamp':\n return typeof value === 'number' && Number.isFinite(value)\n ? new Date(value).toISOString()\n : String(value)\n case 'coordinates': {\n const coordinates = value as { latitude?: number; longitude?: number }\n return coordinates &&\n typeof coordinates.latitude === 'number' &&\n typeof coordinates.longitude === 'number'\n ? `${coordinates.latitude}, ${coordinates.longitude}`\n : String(value)\n }\n case 'sorted':\n case 'reference':\n return Array.isArray(value) ? value.join(', ') : String(value)\n case 'map':\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n default:\n return String(value)\n }\n}\n\n/**\n * The header line — field ids, in model order (AGL-2335).\n *\n * Split out of {@link datasetRecordsToCsv} so a server export can emit the\n * file a page at a time. The whole point of that export is that it is not\n * bounded by what one process can hold in memory, and a helper that only\n * takes a complete `rows` array cannot serve it. Everything below shares\n * one escaper: there are already five divergent hand-rolled CSV escapers in\n * this repo and this is not becoming the sixth.\n */\nexport function datasetCsvHeader(model: DatasetModel): string {\n return model.order.map(csvEscape).join(',')\n}\n\n/** One record's values (storage form) → one CSV line, no terminator. */\nexport function datasetCsvRow(\n model: DatasetModel,\n row: Record<string, unknown>,\n): string {\n return model.order\n .map((fieldId) => {\n const field = model.fields[fieldId]\n return csvEscape(field ? serializeDatasetValue(field, row[fieldId]) : '')\n })\n .join(',')\n}\n\n/** One record's values → the portable JSON object the JSON export emits. */\nexport function datasetRecordToJson(\n model: DatasetModel,\n row: Record<string, unknown>,\n): Record<string, string> {\n return Object.fromEntries(\n model.order.map((fieldId) => {\n const field = model.fields[fieldId]\n return [fieldId, field ? serializeDatasetValue(field, row[fieldId]) : '']\n }),\n )\n}\n\n/** Rows (storage-form value maps) → CSV with a fieldId header row. */\nexport function datasetRecordsToCsv(\n model: DatasetModel,\n rows: Array<Record<string, unknown>>,\n): string {\n return [\n datasetCsvHeader(model),\n ...rows.map((row) => datasetCsvRow(model, row)),\n ].join('\\n')\n}\n"],"names":["escapeCsvCell","csvEscape","serializeDatasetValue","field","value","type","Number","isFinite","Date","toISOString","String","coordinates","latitude","longitude","Array","isArray","join","JSON","stringify","datasetCsvHeader","model","order","map","datasetCsvRow","row","fieldId","fields","datasetRecordToJson","Object","fromEntries","datasetRecordsToCsv","rows"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;CAWC,GAED,SAASA,iBAAiBC,SAAS,QAAQ,6BAA4B;AAOvE,6DAA6D,GAC7D,OAAO,SAASC,sBACdC,KAA6B,EAC7BC,KAAc;IAEd,IAAIA,SAAS,MAAM,OAAO;IAC1B,OAAQD,MAAME,IAAI;QAChB,KAAK;YACH,OAAO,OAAOD,UAAU,YAAYE,OAAOC,QAAQ,CAACH,SAChD,IAAII,KAAKJ,OAAOK,WAAW,KAC3BC,OAAON;QACb,KAAK;YAAe;gBAClB,MAAMO,cAAcP;gBACpB,OAAOO,eACL,OAAOA,YAAYC,QAAQ,KAAK,YAChC,OAAOD,YAAYE,SAAS,KAAK,WAC/B,GAAGF,YAAYC,QAAQ,CAAC,EAAE,EAAED,YAAYE,SAAS,EAAE,GACnDH,OAAON;YACb;QACA,KAAK;QACL,KAAK;YACH,OAAOU,MAAMC,OAAO,CAACX,SAASA,MAAMY,IAAI,CAAC,QAAQN,OAAON;QAC1D,KAAK;YACH,IAAI;gBACF,OAAOa,KAAKC,SAAS,CAACd;YACxB,EAAE,eAAM;gBACN,OAAOM,OAAON;YAChB;QACF;YACE,OAAOM,OAAON;IAClB;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASe,iBAAiBC,KAAmB;IAClD,OAAOA,MAAMC,KAAK,CAACC,GAAG,CAACrB,WAAWe,IAAI,CAAC;AACzC;AAEA,sEAAsE,GACtE,OAAO,SAASO,cACdH,KAAmB,EACnBI,GAA4B;IAE5B,OAAOJ,MAAMC,KAAK,CACfC,GAAG,CAAC,CAACG;QACJ,MAAMtB,QAAQiB,MAAMM,MAAM,CAACD,QAAQ;QACnC,OAAOxB,UAAUE,QAAQD,sBAAsBC,OAAOqB,GAAG,CAACC,QAAQ,IAAI;IACxE,GACCT,IAAI,CAAC;AACV;AAEA,0EAA0E,GAC1E,OAAO,SAASW,oBACdP,KAAmB,EACnBI,GAA4B;IAE5B,OAAOI,OAAOC,WAAW,CACvBT,MAAMC,KAAK,CAACC,GAAG,CAAC,CAACG;QACf,MAAMtB,QAAQiB,MAAMM,MAAM,CAACD,QAAQ;QACnC,OAAO;YAACA;YAAStB,QAAQD,sBAAsBC,OAAOqB,GAAG,CAACC,QAAQ,IAAI;SAAG;IAC3E;AAEJ;AAEA,oEAAoE,GACpE,OAAO,SAASK,oBACdV,KAAmB,EACnBW,IAAoC;IAEpC,OAAO;QACLZ,iBAAiBC;WACdW,KAAKT,GAAG,CAAC,CAACE,MAAQD,cAAcH,OAAOI;KAC3C,CAACR,IAAI,CAAC;AACT"}
@@ -1,44 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- /**
18
- * Dataset CSV/JSON round-tripping (AGL-182) — the IMPORT half.
19
- *
20
- * The serialization half is `dataset-csv.ts` beside this, and the CSV parser
21
- * and the row-count check are the platform's `app-utils/csv` (AGL-2335), so
22
- * the email, commerce and CRM imports read files with the same parser; all of
23
- * them are re-exported here, so `@aglyn/plugins-data/model` remains the one
24
- * import site for everything dataset-io. What stays here is the part that is
25
- * about DATASETS — mapping a file's columns onto a `DatasetModel`.
26
- */
27
- import type { DatasetModel } from '@aglyn/aglyn';
28
- export { countCsvDataRows, exportShortfall, parseCsv, } from '@aglyn/aglyn/app-utils/csv';
29
- export { datasetCsvHeader, datasetCsvRow, datasetRecordToJson, datasetRecordsToCsv, serializeDatasetValue, } from './dataset-csv';
30
- /**
31
- * Parses pasted/uploaded import text — a JSON array of objects or CSV
32
- * with a header row — into raw string maps keyed by the source column
33
- * names. Returns null when nothing parseable is found.
34
- */
35
- export declare function parseImportRows(text: string): Array<Record<string, string>> | null;
36
- /**
37
- * Maps raw import columns to model fieldIds: exact fieldId match first,
38
- * then case-insensitive display-name match. Unmatched columns are
39
- * reported, never silently dropped.
40
- */
41
- export declare function mapImportColumns(model: DatasetModel, columns: string[]): {
42
- mapping: Record<string, string>;
43
- unmatched: string[];
44
- };
@@ -1,85 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */ /**
17
- * Dataset CSV/JSON round-tripping (AGL-182) — the IMPORT half.
18
- *
19
- * The serialization half is `dataset-csv.ts` beside this, and the CSV parser
20
- * and the row-count check are the platform's `app-utils/csv` (AGL-2335), so
21
- * the email, commerce and CRM imports read files with the same parser; all of
22
- * them are re-exported here, so `@aglyn/plugins-data/model` remains the one
23
- * import site for everything dataset-io. What stays here is the part that is
24
- * about DATASETS — mapping a file's columns onto a `DatasetModel`.
25
- */ import { parseCsv } from "@aglyn/aglyn/app-utils/csv";
26
- export { countCsvDataRows, exportShortfall, parseCsv } from "@aglyn/aglyn/app-utils/csv";
27
- export { datasetCsvHeader, datasetCsvRow, datasetRecordToJson, datasetRecordsToCsv, serializeDatasetValue } from "./dataset-csv.js";
28
- /**
29
- * Parses pasted/uploaded import text — a JSON array of objects or CSV
30
- * with a header row — into raw string maps keyed by the source column
31
- * names. Returns null when nothing parseable is found.
32
- */ export function parseImportRows(text) {
33
- const trimmed = String(text != null ? text : '').trim();
34
- if (!trimmed) return null;
35
- if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
36
- try {
37
- const parsed = JSON.parse(trimmed);
38
- const list = Array.isArray(parsed) ? parsed : [
39
- parsed
40
- ];
41
- return list.filter((entry)=>entry && typeof entry === 'object').map((entry)=>Object.fromEntries(Object.entries(entry).map(([key, value])=>[
42
- key,
43
- typeof value === 'string' ? value : JSON.stringify(value)
44
- ])));
45
- } catch (unused) {
46
- return null;
47
- }
48
- }
49
- const rows = parseCsv(trimmed);
50
- if (rows.length < 2) return null;
51
- const [header, ...body] = rows;
52
- return body.map((cells)=>Object.fromEntries(header.map((column, index)=>{
53
- var _cells_index;
54
- return [
55
- column.trim(),
56
- (_cells_index = cells[index]) != null ? _cells_index : ''
57
- ];
58
- })));
59
- }
60
- /**
61
- * Maps raw import columns to model fieldIds: exact fieldId match first,
62
- * then case-insensitive display-name match. Unmatched columns are
63
- * reported, never silently dropped.
64
- */ export function mapImportColumns(model, columns) {
65
- const byName = new Map();
66
- for (const fieldId of model.order){
67
- var _model_fields_fieldId;
68
- byName.set(fieldId.toLowerCase(), fieldId);
69
- const display = (_model_fields_fieldId = model.fields[fieldId]) == null ? void 0 : _model_fields_fieldId.name;
70
- if (display) byName.set(display.trim().toLowerCase(), fieldId);
71
- }
72
- const mapping = {};
73
- const unmatched = [];
74
- for (const column of columns){
75
- const fieldId = byName.get(column.trim().toLowerCase());
76
- if (fieldId) mapping[column] = fieldId;
77
- else unmatched.push(column);
78
- }
79
- return {
80
- mapping,
81
- unmatched
82
- };
83
- }
84
-
85
- //# sourceMappingURL=dataset-io.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/data/src/lib/model/dataset-io.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Dataset CSV/JSON round-tripping (AGL-182) — the IMPORT half.\n *\n * The serialization half is `dataset-csv.ts` beside this, and the CSV parser\n * and the row-count check are the platform's `app-utils/csv` (AGL-2335), so\n * the email, commerce and CRM imports read files with the same parser; all of\n * them are re-exported here, so `@aglyn/plugins-data/model` remains the one\n * import site for everything dataset-io. What stays here is the part that is\n * about DATASETS — mapping a file's columns onto a `DatasetModel`.\n */\n\nimport type { DatasetModel } from '@aglyn/aglyn'\nimport { parseCsv } from '@aglyn/aglyn/app-utils/csv'\nexport {\n countCsvDataRows,\n exportShortfall,\n parseCsv,\n} from '@aglyn/aglyn/app-utils/csv'\nexport {\n datasetCsvHeader,\n datasetCsvRow,\n datasetRecordToJson,\n datasetRecordsToCsv,\n serializeDatasetValue,\n} from './dataset-csv'\n\n/**\n * Parses pasted/uploaded import text — a JSON array of objects or CSV\n * with a header row — into raw string maps keyed by the source column\n * names. Returns null when nothing parseable is found.\n */\nexport function parseImportRows(\n text: string,\n): Array<Record<string, string>> | null {\n const trimmed = String(text ?? '').trim()\n if (!trimmed) return null\n if (trimmed.startsWith('[') || trimmed.startsWith('{')) {\n try {\n const parsed = JSON.parse(trimmed)\n const list = Array.isArray(parsed) ? parsed : [parsed]\n return list\n .filter((entry) => entry && typeof entry === 'object')\n .map((entry) =>\n Object.fromEntries(\n Object.entries(entry as Record<string, unknown>).map(\n ([key, value]) => [\n key,\n typeof value === 'string' ? value : JSON.stringify(value),\n ],\n ),\n ),\n )\n } catch {\n return null\n }\n }\n const rows = parseCsv(trimmed)\n if (rows.length < 2) return null\n const [header, ...body] = rows\n return body.map((cells) =>\n Object.fromEntries(\n header.map((column, index) => [column.trim(), cells[index] ?? '']),\n ),\n )\n}\n\n/**\n * Maps raw import columns to model fieldIds: exact fieldId match first,\n * then case-insensitive display-name match. Unmatched columns are\n * reported, never silently dropped.\n */\nexport function mapImportColumns(\n model: DatasetModel,\n columns: string[],\n): { mapping: Record<string, string>; unmatched: string[] } {\n const byName = new Map<string, string>()\n for (const fieldId of model.order) {\n byName.set(fieldId.toLowerCase(), fieldId)\n const display = model.fields[fieldId]?.name\n if (display) byName.set(display.trim().toLowerCase(), fieldId)\n }\n const mapping: Record<string, string> = {}\n const unmatched: string[] = []\n for (const column of columns) {\n const fieldId = byName.get(column.trim().toLowerCase())\n if (fieldId) mapping[column] = fieldId\n else unmatched.push(column)\n }\n return { mapping, unmatched }\n}\n"],"names":["parseCsv","countCsvDataRows","exportShortfall","datasetCsvHeader","datasetCsvRow","datasetRecordToJson","datasetRecordsToCsv","serializeDatasetValue","parseImportRows","text","trimmed","String","trim","startsWith","parsed","JSON","parse","list","Array","isArray","filter","entry","map","Object","fromEntries","entries","key","value","stringify","rows","length","header","body","cells","column","index","mapImportColumns","model","columns","byName","Map","fieldId","order","set","toLowerCase","display","fields","name","mapping","unmatched","get","push"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;CASC,GAGD,SAASA,QAAQ,QAAQ,6BAA4B;AACrD,SACEC,gBAAgB,EAChBC,eAAe,EACfF,QAAQ,QACH,6BAA4B;AACnC,SACEG,gBAAgB,EAChBC,aAAa,EACbC,mBAAmB,EACnBC,mBAAmB,EACnBC,qBAAqB,QAChB,mBAAe;AAEtB;;;;CAIC,GACD,OAAO,SAASC,gBACdC,IAAY;IAEZ,MAAMC,UAAUC,OAAOF,eAAAA,OAAQ,IAAIG,IAAI;IACvC,IAAI,CAACF,SAAS,OAAO;IACrB,IAAIA,QAAQG,UAAU,CAAC,QAAQH,QAAQG,UAAU,CAAC,MAAM;QACtD,IAAI;YACF,MAAMC,SAASC,KAAKC,KAAK,CAACN;YAC1B,MAAMO,OAAOC,MAAMC,OAAO,CAACL,UAAUA,SAAS;gBAACA;aAAO;YACtD,OAAOG,KACJG,MAAM,CAAC,CAACC,QAAUA,SAAS,OAAOA,UAAU,UAC5CC,GAAG,CAAC,CAACD,QACJE,OAAOC,WAAW,CAChBD,OAAOE,OAAO,CAACJ,OAAkCC,GAAG,CAClD,CAAC,CAACI,KAAKC,MAAM,GAAK;wBAChBD;wBACA,OAAOC,UAAU,WAAWA,QAAQZ,KAAKa,SAAS,CAACD;qBACpD;QAIX,EAAE,eAAM;YACN,OAAO;QACT;IACF;IACA,MAAME,OAAO7B,SAASU;IACtB,IAAImB,KAAKC,MAAM,GAAG,GAAG,OAAO;IAC5B,MAAM,CAACC,QAAQ,GAAGC,KAAK,GAAGH;IAC1B,OAAOG,KAAKV,GAAG,CAAC,CAACW,QACfV,OAAOC,WAAW,CAChBO,OAAOT,GAAG,CAAC,CAACY,QAAQC;gBAA0BF;mBAAhB;gBAACC,OAAOtB,IAAI;iBAAIqB,eAAAA,KAAK,CAACE,MAAM,YAAZF,eAAgB;aAAG;;AAGvE;AAEA;;;;CAIC,GACD,OAAO,SAASG,iBACdC,KAAmB,EACnBC,OAAiB;IAEjB,MAAMC,SAAS,IAAIC;IACnB,KAAK,MAAMC,WAAWJ,MAAMK,KAAK,CAAE;YAEjBL;QADhBE,OAAOI,GAAG,CAACF,QAAQG,WAAW,IAAIH;QAClC,MAAMI,WAAUR,wBAAAA,MAAMS,MAAM,CAACL,QAAQ,qBAArBJ,sBAAuBU,IAAI;QAC3C,IAAIF,SAASN,OAAOI,GAAG,CAACE,QAAQjC,IAAI,GAAGgC,WAAW,IAAIH;IACxD;IACA,MAAMO,UAAkC,CAAC;IACzC,MAAMC,YAAsB,EAAE;IAC9B,KAAK,MAAMf,UAAUI,QAAS;QAC5B,MAAMG,UAAUF,OAAOW,GAAG,CAAChB,OAAOtB,IAAI,GAAGgC,WAAW;QACpD,IAAIH,SAASO,OAAO,CAACd,OAAO,GAAGO;aAC1BQ,UAAUE,IAAI,CAACjB;IACtB;IACA,OAAO;QAAEc;QAASC;IAAU;AAC9B"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/data/src/lib/model/index.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Dataset io model (AGL-413): CSV serialize/parse for dataset records relocated from core app-utils.\n * Context-free — importable by client components, /server handlers, and\n * other plugins/apps via `@aglyn/plugins-data/model`.\n */\nexport * from './dataset-io'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;CAIC,GACD,cAAc,kBAAc"}
@@ -1,57 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- import { type PluginWebApiHandler } from '@aglyn/aglyn/server';
18
- /** Header naming the row count the server undertook to send. */
19
- export declare const EXPORT_ROWS_HEADER = "X-Aglyn-Export-Rows";
20
- /**
21
- * Complete dataset export, streamed (AGL-2335).
22
- *
23
- * `run-an-agency-workspace.md` sells this as half of a **full handover** —
24
- * the artifact a departing client is told is theirs. What it actually
25
- * produced was `records`, the card's live listener window: `limit(500)`
26
- * **with no `orderBy`**. Firestore answers an unordered limit in document-id
27
- * order over auto-ids, so a 2,000-record dataset did not export "the first
28
- * 500", it exported *500 unpredictable rows*, and re-exporting could produce
29
- * a different set. `sortDatasetRecords` then sorted that sample, which made
30
- * the result look ordered and be arbitrary. The button said `CSV`.
31
- *
32
- * Three things this route does that the client could not:
33
- *
34
- * 1. **Every row.** Cursor-paged over the whole collection, streamed out as
35
- * it goes, so completeness does not cost a browser hang or a giant buffer
36
- * — the reason the obvious client-side fix was unsafe on a tier where
37
- * `recordsPerDataset` is `UNLIMITED`.
38
- * 2. **A deterministic total order.** `orderBy(FieldPath.documentId())`.
39
- * Deliberately NOT `orderBy('order')`, the field the table sorts by:
40
- * Firestore silently DROPS documents missing an ordered field, and legacy
41
- * records have no `order`, so ordering by it would reintroduce exactly the
42
- * silent row loss this issue is about — with an index requirement on top.
43
- * Document id is present on every document, is a total order, and is the
44
- * order the old window happened to arrive in. Same order, now complete.
45
- * 3. **A checkable promise.** `X-Aglyn-Export-Rows` carries a `count()`
46
- * aggregate taken before the first page, so the client can prove the file
47
- * it received is whole. A stream that dies halfway yields a perfectly
48
- * well-formed shorter file; nothing about the bytes says they are short.
49
- *
50
- * **No entitlement gate, deliberately.** The client button never had one and
51
- * this is the customer's own data — the same reasoning `/api/orgs/export-data`
52
- * records for the workspace export. Adding a plan gate here would take a
53
- * capability away from people who have it today, on the export path of all
54
- * places. The `release_data_store` flag gate is kept, because that is
55
- * whether the feature exists at all rather than whether it is paid for.
56
- */
57
- export declare const datasetsExportHandler: PluginWebApiHandler;