@aglyn/aglyn 1.0.0-beta.198 → 1.0.0-beta.200

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 (113) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/activity-search.d.ts +39 -0
  3. package/src/lib/app-utils/activity-search.js +85 -0
  4. package/src/lib/app-utils/activity-search.js.map +1 -0
  5. package/src/lib/app-utils/admin-audit-index.d.ts +104 -0
  6. package/src/lib/app-utils/admin-audit-index.js +177 -0
  7. package/src/lib/app-utils/admin-audit-index.js.map +1 -0
  8. package/src/lib/app-utils/artifact-list-keys.d.ts +51 -0
  9. package/src/lib/app-utils/artifact-list-keys.js +144 -0
  10. package/src/lib/app-utils/artifact-list-keys.js.map +1 -0
  11. package/src/lib/app-utils/content-query-fields.d.ts +69 -0
  12. package/src/lib/app-utils/content-query-fields.js +79 -0
  13. package/src/lib/app-utils/content-query-fields.js.map +1 -0
  14. package/src/lib/app-utils/crm.d.ts +151 -1
  15. package/src/lib/app-utils/crm.js +388 -1
  16. package/src/lib/app-utils/crm.js.map +1 -1
  17. package/src/lib/app-utils/dataset-models.d.ts +75 -24
  18. package/src/lib/app-utils/dataset-models.js +107 -40
  19. package/src/lib/app-utils/dataset-models.js.map +1 -1
  20. package/src/lib/app-utils/docs-index.generated.js +40 -40
  21. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  22. package/src/lib/app-utils/email-search.d.ts +36 -0
  23. package/src/lib/app-utils/email-search.js +53 -0
  24. package/src/lib/app-utils/email-search.js.map +1 -0
  25. package/src/lib/app-utils/forms.d.ts +83 -3
  26. package/src/lib/app-utils/forms.js +67 -0
  27. package/src/lib/app-utils/forms.js.map +1 -1
  28. package/src/lib/app-utils/marketplace-listing-visibility.d.ts +7 -6
  29. package/src/lib/app-utils/marketplace-listing-visibility.js +12 -10
  30. package/src/lib/app-utils/marketplace-listing-visibility.js.map +1 -1
  31. package/src/lib/app-utils/media-embedded-fields.d.ts +471 -0
  32. package/src/lib/app-utils/media-embedded-fields.js +619 -0
  33. package/src/lib/app-utils/media-embedded-fields.js.map +1 -0
  34. package/src/lib/app-utils/media-embedded-metadata/exif.d.ts +160 -0
  35. package/src/lib/app-utils/media-embedded-metadata/exif.js +1267 -0
  36. package/src/lib/app-utils/media-embedded-metadata/exif.js.map +1 -0
  37. package/src/lib/app-utils/media-embedded-metadata/gif.d.ts +44 -0
  38. package/src/lib/app-utils/media-embedded-metadata/gif.js +134 -0
  39. package/src/lib/app-utils/media-embedded-metadata/gif.js.map +1 -0
  40. package/src/lib/app-utils/media-embedded-metadata/heif.d.ts +23 -0
  41. package/src/lib/app-utils/media-embedded-metadata/heif.js +354 -0
  42. package/src/lib/app-utils/media-embedded-metadata/heif.js.map +1 -0
  43. package/src/lib/app-utils/media-embedded-metadata/icc.d.ts +22 -0
  44. package/src/lib/app-utils/media-embedded-metadata/icc.js +85 -0
  45. package/src/lib/app-utils/media-embedded-metadata/icc.js.map +1 -0
  46. package/src/lib/app-utils/media-embedded-metadata/image-blocks.d.ts +145 -0
  47. package/src/lib/app-utils/media-embedded-metadata/image-blocks.js +200 -0
  48. package/src/lib/app-utils/media-embedded-metadata/image-blocks.js.map +1 -0
  49. package/src/lib/app-utils/media-embedded-metadata/index.d.ts +93 -0
  50. package/src/lib/app-utils/media-embedded-metadata/index.js +744 -0
  51. package/src/lib/app-utils/media-embedded-metadata/index.js.map +1 -0
  52. package/src/lib/app-utils/media-embedded-metadata/iptc.d.ts +59 -0
  53. package/src/lib/app-utils/media-embedded-metadata/iptc.js +529 -0
  54. package/src/lib/app-utils/media-embedded-metadata/iptc.js.map +1 -0
  55. package/src/lib/app-utils/media-embedded-metadata/jpeg.d.ts +43 -0
  56. package/src/lib/app-utils/media-embedded-metadata/jpeg.js +456 -0
  57. package/src/lib/app-utils/media-embedded-metadata/jpeg.js.map +1 -0
  58. package/src/lib/app-utils/media-embedded-metadata/mp4.d.ts +37 -0
  59. package/src/lib/app-utils/media-embedded-metadata/mp4.js +621 -0
  60. package/src/lib/app-utils/media-embedded-metadata/mp4.js.map +1 -0
  61. package/src/lib/app-utils/media-embedded-metadata/ooxml.d.ts +131 -0
  62. package/src/lib/app-utils/media-embedded-metadata/ooxml.js +1058 -0
  63. package/src/lib/app-utils/media-embedded-metadata/ooxml.js.map +1 -0
  64. package/src/lib/app-utils/media-embedded-metadata/pdf-objects.d.ts +223 -0
  65. package/src/lib/app-utils/media-embedded-metadata/pdf-objects.js +936 -0
  66. package/src/lib/app-utils/media-embedded-metadata/pdf-objects.js.map +1 -0
  67. package/src/lib/app-utils/media-embedded-metadata/pdf.d.ts +82 -0
  68. package/src/lib/app-utils/media-embedded-metadata/pdf.js +1167 -0
  69. package/src/lib/app-utils/media-embedded-metadata/pdf.js.map +1 -0
  70. package/src/lib/app-utils/media-embedded-metadata/png.d.ts +41 -0
  71. package/src/lib/app-utils/media-embedded-metadata/png.js +512 -0
  72. package/src/lib/app-utils/media-embedded-metadata/png.js.map +1 -0
  73. package/src/lib/app-utils/media-embedded-metadata/svg.d.ts +30 -0
  74. package/src/lib/app-utils/media-embedded-metadata/svg.js +154 -0
  75. package/src/lib/app-utils/media-embedded-metadata/svg.js.map +1 -0
  76. package/src/lib/app-utils/media-embedded-metadata/tiff.d.ts +40 -0
  77. package/src/lib/app-utils/media-embedded-metadata/tiff.js +158 -0
  78. package/src/lib/app-utils/media-embedded-metadata/tiff.js.map +1 -0
  79. package/src/lib/app-utils/media-embedded-metadata/types.d.ts +60 -0
  80. package/src/lib/app-utils/media-embedded-metadata/types.js +32 -0
  81. package/src/lib/app-utils/media-embedded-metadata/types.js.map +1 -0
  82. package/src/lib/app-utils/media-embedded-metadata/webm.d.ts +26 -0
  83. package/src/lib/app-utils/media-embedded-metadata/webm.js +417 -0
  84. package/src/lib/app-utils/media-embedded-metadata/webm.js.map +1 -0
  85. package/src/lib/app-utils/media-embedded-metadata/webp.d.ts +37 -0
  86. package/src/lib/app-utils/media-embedded-metadata/webp.js +272 -0
  87. package/src/lib/app-utils/media-embedded-metadata/webp.js.map +1 -0
  88. package/src/lib/app-utils/media-embedded-metadata/xml.d.ts +231 -0
  89. package/src/lib/app-utils/media-embedded-metadata/xml.js +598 -0
  90. package/src/lib/app-utils/media-embedded-metadata/xml.js.map +1 -0
  91. package/src/lib/app-utils/media-embedded-metadata/xmp.d.ts +100 -0
  92. package/src/lib/app-utils/media-embedded-metadata/xmp.js +1475 -0
  93. package/src/lib/app-utils/media-embedded-metadata/xmp.js.map +1 -0
  94. package/src/lib/app-utils/media-embedded-metadata/zip.d.ts +93 -0
  95. package/src/lib/app-utils/media-embedded-metadata/zip.js +531 -0
  96. package/src/lib/app-utils/media-embedded-metadata/zip.js.map +1 -0
  97. package/src/lib/app-utils/media-metadata.d.ts +79 -0
  98. package/src/lib/app-utils/media-metadata.js +74 -0
  99. package/src/lib/app-utils/media-metadata.js.map +1 -1
  100. package/src/lib/app-utils/message-search.d.ts +40 -0
  101. package/src/lib/app-utils/message-search.js +168 -0
  102. package/src/lib/app-utils/message-search.js.map +1 -0
  103. package/src/lib/app-utils/name-search.d.ts +95 -0
  104. package/src/lib/app-utils/name-search.js +108 -0
  105. package/src/lib/app-utils/name-search.js.map +1 -1
  106. package/src/lib/app-utils/server.d.ts +2 -0
  107. package/src/lib/app-utils/server.js +2 -0
  108. package/src/lib/app-utils/server.js.map +1 -1
  109. package/src/lib/foundation/definitions/platform.types.d.ts +11 -0
  110. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  111. package/src/server.d.ts +1 -0
  112. package/src/server.js +7 -0
  113. package/src/server.js.map +1 -1
@@ -0,0 +1,144 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { displayNameSearchFields } from "./name-search.js";
18
+ /*
19
+ * THE KEYS A SITE ARTIFACT'S LISTS QUERY BY (AGL-3321).
20
+ *
21
+ * The screens, layouts, reusable components and templates of a site are
22
+ * listed, searched and filtered by Firestore queries, so every key a list
23
+ * asks about has to be WRITTEN — a query cannot find a document by a value it
24
+ * derives, and cannot find one that lacks the field at all:
25
+ *
26
+ * nameLower / nameTokens / nameReversed
27
+ * the name keys (`displayNameSearchFields`), on all four;
28
+ * kind on a component, `site` or `email` — a component stored
29
+ * before AGL-3287 carries none and is read as `site`, so the
30
+ * "Used in: Page" filter needs the word stored;
31
+ * on a template, `page`, `component` or `layout` — a template
32
+ * stored with none is read as a page template;
33
+ * source.type on a template, its provenance — one stored with no
34
+ * `source`, or with the legacy `workspace`, was saved here,
35
+ * which is `authored`;
36
+ * libraryRow on a template, whether it is its own row of the templates
37
+ * library: a multi-page STARTER is one row (AGL-696), led by
38
+ * its first live page, so the other pages carry `false` — and
39
+ * so does a deleted template (`artifactDeleteListKeys`), which
40
+ * is how the library's query leaves tombstones out;
41
+ *
42
+ * A DELETED screen gets none of them. The email templates list orders by
43
+ * `nameLower`, and an email template's delete clears its name keys so the
44
+ * tombstone leaves that order (a query cannot ask for `deletedAt` to be
45
+ * absent) — so no create, restore or backfill may stamp them back.
46
+ *
47
+ * Every CREATE stamps them (`artifactCreateListKeys`) and every rename
48
+ * restamps the name keys (`artifactRenameListKeys`).
49
+ * `tools/scripts/backfill-artifacts-list-keys.mjs` stamps the documents
50
+ * written before them; a script cannot import this library, so it reads the
51
+ * documents the way this module does, and both sides answer
52
+ * `tools/scripts/lib/artifact-list-keys.fixtures.json` — this module's spec
53
+ * and the backfill's `--self-test`.
54
+ */ /** The site artifact collections whose lists query these keys. */ export const LISTED_ARTIFACT_COLLECTIONS = [
55
+ 'screens',
56
+ 'layouts',
57
+ 'components',
58
+ 'templates'
59
+ ];
60
+ /** Whether a host subcollection is one whose lists query these keys. */ export const isListedArtifactCollection = (collection)=>LISTED_ARTIFACT_COLLECTIONS.includes(collection);
61
+ /** The kinds a template's `kind` holds; absent reads as `page`. */ const TEMPLATE_KINDS = [
62
+ 'page',
63
+ 'component',
64
+ 'layout'
65
+ ];
66
+ /**
67
+ * A provenance word no writer stores any more: a detached marketplace copy
68
+ * was stamped `workspace` before it was stamped `authored` (AGL-3321).
69
+ */ const LEGACY_AUTHORED_SOURCE = 'workspace';
70
+ const record = (value)=>value && typeof value === 'object' ? value : {};
71
+ /**
72
+ * The name a list finds an artifact by.
73
+ *
74
+ * Its `displayName` — except a page of a multi-page STARTER, which the
75
+ * templates library lists as ONE row under the starter's name
76
+ * (`source.starterName`), so every page of it carries that name's keys and a
77
+ * search for the starter finds the row whichever page leads it.
78
+ */ export function artifactSearchName(collection, doc) {
79
+ if (collection === 'templates') {
80
+ const starterName = record(doc['source'])['starterName'];
81
+ if (typeof starterName === 'string' && starterName.trim()) return starterName;
82
+ }
83
+ return doc['displayName'];
84
+ }
85
+ /**
86
+ * The list keys a CREATE of `doc` into `collection` stamps: the name keys,
87
+ * and the stored default of a kind a reader otherwise infers. Nothing for a
88
+ * collection no artifact list queries.
89
+ *
90
+ * Spread AFTER the document's own fields: the keys are derived from them.
91
+ */ export function artifactCreateListKeys(collection, doc) {
92
+ if (!isListedArtifactCollection(collection)) return {};
93
+ if (collection === 'screens' && doc['deletedAt'] != null) return {};
94
+ const keys = _extends({}, displayNameSearchFields(artifactSearchName(collection, doc)));
95
+ if (collection === 'components' && doc['kind'] !== 'email') keys['kind'] = 'site';
96
+ if (collection === 'templates') {
97
+ if (!TEMPLATE_KINDS.includes(String(doc['kind']))) keys['kind'] = 'page';
98
+ // Provenance is server-managed (AGL-666), and a writer that states it is
99
+ // never overridden here. None at all, or the legacy word a detached copy
100
+ // carried, was saved here, which is `authored`.
101
+ const sourceType = record(doc['source'])['type'];
102
+ if (typeof sourceType !== 'string' || sourceType === LEGACY_AUTHORED_SOURCE) {
103
+ keys['source'] = _extends({}, record(doc['source']), {
104
+ type: 'authored'
105
+ });
106
+ }
107
+ keys['libraryRow'] = doc['deletedAt'] == null && leadsItsStarter(doc);
108
+ }
109
+ return keys;
110
+ }
111
+ /**
112
+ * Whether a template leads its starter's library row when the starter is
113
+ * written whole: every template that is not a starter page does, and of a
114
+ * starter's pages the first (`starterOrder` 0). A later delete moves the lead
115
+ * to the next live page — see `artifactDeleteListKeys`.
116
+ */ function leadsItsStarter(doc) {
117
+ var _source_starterOrder;
118
+ const source = record(doc['source']);
119
+ const starterId = source['starterId'];
120
+ if (typeof starterId !== 'string' || !starterId) return true;
121
+ return Number((_source_starterOrder = source['starterOrder']) != null ? _source_starterOrder : 0) === 0;
122
+ }
123
+ /**
124
+ * The list keys a SOFT DELETE writes beside `deletedAt`: a deleted template
125
+ * is no longer a library row. A caller deleting the page that LEADS a
126
+ * starter's row gives the lead to the next live page (`libraryRow: true`),
127
+ * or the starter disappears from the library with pages still in it.
128
+ */ export function artifactDeleteListKeys(collection) {
129
+ return collection === 'templates' ? {
130
+ libraryRow: false
131
+ } : {};
132
+ }
133
+ /**
134
+ * The name keys a RENAME writes beside the new `displayName`. `doc` is the
135
+ * document as read, for the one case whose search name is not its display
136
+ * name (a starter page — see `artifactSearchName`).
137
+ */ export function artifactRenameListKeys(collection, displayName, doc = {}) {
138
+ if (!isListedArtifactCollection(collection)) return {};
139
+ return displayNameSearchFields(artifactSearchName(collection, _extends({}, doc, {
140
+ displayName
141
+ })));
142
+ }
143
+
144
+ //# sourceMappingURL=artifact-list-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/artifact-list-keys.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 { displayNameSearchFields } from './name-search'\n\n/*\n * THE KEYS A SITE ARTIFACT'S LISTS QUERY BY (AGL-3321).\n *\n * The screens, layouts, reusable components and templates of a site are\n * listed, searched and filtered by Firestore queries, so every key a list\n * asks about has to be WRITTEN — a query cannot find a document by a value it\n * derives, and cannot find one that lacks the field at all:\n *\n * nameLower / nameTokens / nameReversed\n * the name keys (`displayNameSearchFields`), on all four;\n * kind on a component, `site` or `email` — a component stored\n * before AGL-3287 carries none and is read as `site`, so the\n * \"Used in: Page\" filter needs the word stored;\n * on a template, `page`, `component` or `layout` — a template\n * stored with none is read as a page template;\n * source.type on a template, its provenance — one stored with no\n * `source`, or with the legacy `workspace`, was saved here,\n * which is `authored`;\n * libraryRow on a template, whether it is its own row of the templates\n * library: a multi-page STARTER is one row (AGL-696), led by\n * its first live page, so the other pages carry `false` — and\n * so does a deleted template (`artifactDeleteListKeys`), which\n * is how the library's query leaves tombstones out;\n *\n * A DELETED screen gets none of them. The email templates list orders by\n * `nameLower`, and an email template's delete clears its name keys so the\n * tombstone leaves that order (a query cannot ask for `deletedAt` to be\n * absent) — so no create, restore or backfill may stamp them back.\n *\n * Every CREATE stamps them (`artifactCreateListKeys`) and every rename\n * restamps the name keys (`artifactRenameListKeys`).\n * `tools/scripts/backfill-artifacts-list-keys.mjs` stamps the documents\n * written before them; a script cannot import this library, so it reads the\n * documents the way this module does, and both sides answer\n * `tools/scripts/lib/artifact-list-keys.fixtures.json` — this module's spec\n * and the backfill's `--self-test`.\n */\n\n/** The site artifact collections whose lists query these keys. */\nexport const LISTED_ARTIFACT_COLLECTIONS = [\n 'screens',\n 'layouts',\n 'components',\n 'templates',\n] as const\n\nexport type ListedArtifactCollection =\n (typeof LISTED_ARTIFACT_COLLECTIONS)[number]\n\n/** Whether a host subcollection is one whose lists query these keys. */\nexport const isListedArtifactCollection = (\n collection: string,\n): collection is ListedArtifactCollection =>\n (LISTED_ARTIFACT_COLLECTIONS as readonly string[]).includes(collection)\n\n/** The kinds a template's `kind` holds; absent reads as `page`. */\nconst TEMPLATE_KINDS = ['page', 'component', 'layout']\n\n/**\n * A provenance word no writer stores any more: a detached marketplace copy\n * was stamped `workspace` before it was stamped `authored` (AGL-3321).\n */\nconst LEGACY_AUTHORED_SOURCE = 'workspace'\n\nconst record = (value: unknown): Record<string, unknown> =>\n value && typeof value === 'object' ? (value as Record<string, unknown>) : {}\n\n/**\n * The name a list finds an artifact by.\n *\n * Its `displayName` — except a page of a multi-page STARTER, which the\n * templates library lists as ONE row under the starter's name\n * (`source.starterName`), so every page of it carries that name's keys and a\n * search for the starter finds the row whichever page leads it.\n */\nexport function artifactSearchName(\n collection: string,\n doc: Record<string, unknown>,\n): unknown {\n if (collection === 'templates') {\n const starterName = record(doc['source'])['starterName']\n if (typeof starterName === 'string' && starterName.trim()) return starterName\n }\n return doc['displayName']\n}\n\n/**\n * The list keys a CREATE of `doc` into `collection` stamps: the name keys,\n * and the stored default of a kind a reader otherwise infers. Nothing for a\n * collection no artifact list queries.\n *\n * Spread AFTER the document's own fields: the keys are derived from them.\n */\nexport function artifactCreateListKeys(\n collection: string,\n doc: Record<string, unknown>,\n): Record<string, unknown> {\n if (!isListedArtifactCollection(collection)) return {}\n if (collection === 'screens' && doc['deletedAt'] != null) return {}\n const keys: Record<string, unknown> = {\n ...displayNameSearchFields(artifactSearchName(collection, doc)),\n }\n if (collection === 'components' && doc['kind'] !== 'email') keys['kind'] = 'site'\n if (collection === 'templates') {\n if (!TEMPLATE_KINDS.includes(String(doc['kind']))) keys['kind'] = 'page'\n // Provenance is server-managed (AGL-666), and a writer that states it is\n // never overridden here. None at all, or the legacy word a detached copy\n // carried, was saved here, which is `authored`.\n const sourceType = record(doc['source'])['type']\n if (typeof sourceType !== 'string' || sourceType === LEGACY_AUTHORED_SOURCE) {\n keys['source'] = { ...record(doc['source']), type: 'authored' }\n }\n keys['libraryRow'] = doc['deletedAt'] == null && leadsItsStarter(doc)\n }\n return keys\n}\n\n/**\n * Whether a template leads its starter's library row when the starter is\n * written whole: every template that is not a starter page does, and of a\n * starter's pages the first (`starterOrder` 0). A later delete moves the lead\n * to the next live page — see `artifactDeleteListKeys`.\n */\nfunction leadsItsStarter(doc: Record<string, unknown>): boolean {\n const source = record(doc['source'])\n const starterId = source['starterId']\n if (typeof starterId !== 'string' || !starterId) return true\n return Number(source['starterOrder'] ?? 0) === 0\n}\n\n/**\n * The list keys a SOFT DELETE writes beside `deletedAt`: a deleted template\n * is no longer a library row. A caller deleting the page that LEADS a\n * starter's row gives the lead to the next live page (`libraryRow: true`),\n * or the starter disappears from the library with pages still in it.\n */\nexport function artifactDeleteListKeys(\n collection: string,\n): Record<string, unknown> {\n return collection === 'templates' ? { libraryRow: false } : {}\n}\n\n/**\n * The name keys a RENAME writes beside the new `displayName`. `doc` is the\n * document as read, for the one case whose search name is not its display\n * name (a starter page — see `artifactSearchName`).\n */\nexport function artifactRenameListKeys(\n collection: string,\n displayName: unknown,\n doc: Record<string, unknown> = {},\n): Record<string, unknown> {\n if (!isListedArtifactCollection(collection)) return {}\n return displayNameSearchFields(\n artifactSearchName(collection, { ...doc, displayName }),\n )\n}\n"],"names":["displayNameSearchFields","LISTED_ARTIFACT_COLLECTIONS","isListedArtifactCollection","collection","includes","TEMPLATE_KINDS","LEGACY_AUTHORED_SOURCE","record","value","artifactSearchName","doc","starterName","trim","artifactCreateListKeys","keys","String","sourceType","type","leadsItsStarter","source","starterId","Number","artifactDeleteListKeys","libraryRow","artifactRenameListKeys","displayName"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,uBAAuB,QAAQ,mBAAe;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCC,GAED,gEAAgE,GAChE,OAAO,MAAMC,8BAA8B;IACzC;IACA;IACA;IACA;CACD,CAAS;AAKV,sEAAsE,GACtE,OAAO,MAAMC,6BAA6B,CACxCC,aAEA,AAACF,4BAAkDG,QAAQ,CAACD,YAAW;AAEzE,iEAAiE,GACjE,MAAME,iBAAiB;IAAC;IAAQ;IAAa;CAAS;AAEtD;;;CAGC,GACD,MAAMC,yBAAyB;AAE/B,MAAMC,SAAS,CAACC,QACdA,SAAS,OAAOA,UAAU,WAAYA,QAAoC,CAAC;AAE7E;;;;;;;CAOC,GACD,OAAO,SAASC,mBACdN,UAAkB,EAClBO,GAA4B;IAE5B,IAAIP,eAAe,aAAa;QAC9B,MAAMQ,cAAcJ,OAAOG,GAAG,CAAC,SAAS,CAAC,CAAC,cAAc;QACxD,IAAI,OAAOC,gBAAgB,YAAYA,YAAYC,IAAI,IAAI,OAAOD;IACpE;IACA,OAAOD,GAAG,CAAC,cAAc;AAC3B;AAEA;;;;;;CAMC,GACD,OAAO,SAASG,uBACdV,UAAkB,EAClBO,GAA4B;IAE5B,IAAI,CAACR,2BAA2BC,aAAa,OAAO,CAAC;IACrD,IAAIA,eAAe,aAAaO,GAAG,CAAC,YAAY,IAAI,MAAM,OAAO,CAAC;IAClE,MAAMI,OAAgC,aACjCd,wBAAwBS,mBAAmBN,YAAYO;IAE5D,IAAIP,eAAe,gBAAgBO,GAAG,CAAC,OAAO,KAAK,SAASI,IAAI,CAAC,OAAO,GAAG;IAC3E,IAAIX,eAAe,aAAa;QAC9B,IAAI,CAACE,eAAeD,QAAQ,CAACW,OAAOL,GAAG,CAAC,OAAO,IAAII,IAAI,CAAC,OAAO,GAAG;QAClE,yEAAyE;QACzE,yEAAyE;QACzE,gDAAgD;QAChD,MAAME,aAAaT,OAAOG,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO;QAChD,IAAI,OAAOM,eAAe,YAAYA,eAAeV,wBAAwB;YAC3EQ,IAAI,CAAC,SAAS,GAAG,aAAKP,OAAOG,GAAG,CAAC,SAAS;gBAAGO,MAAM;;QACrD;QACAH,IAAI,CAAC,aAAa,GAAGJ,GAAG,CAAC,YAAY,IAAI,QAAQQ,gBAAgBR;IACnE;IACA,OAAOI;AACT;AAEA;;;;;CAKC,GACD,SAASI,gBAAgBR,GAA4B;QAIrCS;IAHd,MAAMA,SAASZ,OAAOG,GAAG,CAAC,SAAS;IACnC,MAAMU,YAAYD,MAAM,CAAC,YAAY;IACrC,IAAI,OAAOC,cAAc,YAAY,CAACA,WAAW,OAAO;IACxD,OAAOC,QAAOF,uBAAAA,MAAM,CAAC,eAAe,YAAtBA,uBAA0B,OAAO;AACjD;AAEA;;;;;CAKC,GACD,OAAO,SAASG,uBACdnB,UAAkB;IAElB,OAAOA,eAAe,cAAc;QAAEoB,YAAY;IAAM,IAAI,CAAC;AAC/D;AAEA;;;;CAIC,GACD,OAAO,SAASC,uBACdrB,UAAkB,EAClBsB,WAAoB,EACpBf,MAA+B,CAAC,CAAC;IAEjC,IAAI,CAACR,2BAA2BC,aAAa,OAAO,CAAC;IACrD,OAAOH,wBACLS,mBAAmBN,YAAY,aAAKO;QAAKe;;AAE7C"}
@@ -0,0 +1,69 @@
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
+ * The field the console's entries table searches (AGL-3321): every word
19
+ * prefix of the entry's title, as `nameSearchTokens` writes them, which the
20
+ * table's quick search and its Title "contains" filter ask with
21
+ * `array-contains` on the Firestore query.
22
+ *
23
+ * DERIVED, and never carried in a bundle. Every writer of `title` stamps it
24
+ * through {@link entryTitleSearchFields}: the entry editor's save, the
25
+ * resources route's create, the bundle import, and the seed and changelog
26
+ * scripts. `tools/scripts/backfill-entries-title-tokens.mjs` stamps the
27
+ * entries written before it. An entry without it still lists; it just cannot
28
+ * be found by a word of its title.
29
+ */
30
+ export declare const ENTRY_TITLE_TOKENS_FIELD = "titleTokens";
31
+ /**
32
+ * The search half of a write that sets an entry's `title`. An entry with no
33
+ * title gets an empty array, so the field is present on every entry and the
34
+ * backfill has nothing left to find.
35
+ */
36
+ export declare function entryTitleSearchFields(title: unknown): {
37
+ [ENTRY_TITLE_TOKENS_FIELD]: string[];
38
+ };
39
+ /**
40
+ * Where an author's schema type is stored as the WORD the console's Authors
41
+ * table filters by (AGL-3321).
42
+ *
43
+ * `type` holds `HostEntityType` in whichever spelling wrote it: the number
44
+ * from the authors tab, a string `"1"`/`"2"` from an older bundle, nothing at
45
+ * all on a record `contentAuthorSchemaType` reads as `Organization`. A query
46
+ * matches one stored value, so an equality on `type` would miss every record
47
+ * spelled another way. This field is that one value.
48
+ */
49
+ export declare const AUTHOR_SCHEMA_TYPE_FIELD = "schemaType";
50
+ /**
51
+ * The fields the Authors table's query reads (AGL-3321), derived from what the
52
+ * record already says: the name's lower-cased key and word-prefix tokens,
53
+ * normalized exactly as `nameSearchKey` / `nameSearchTokens` normalize a typed
54
+ * value, and the schema type as the row reads it.
55
+ *
56
+ * Spread at EVERY write of an author's `name` or `type` — the resources
57
+ * route's create, the authors tab's edit, the bundle import — and stamped on
58
+ * older records by `tools/scripts/backfill-authors-search-fields.mjs`. A
59
+ * record without them still reaches the picker, which reads every author; it
60
+ * is only the table's filters and search that cannot see it.
61
+ */
62
+ export declare function contentAuthorQueryFields(author: {
63
+ name?: unknown;
64
+ type?: unknown;
65
+ }): {
66
+ nameLower: string;
67
+ nameTokens: string[];
68
+ [AUTHOR_SCHEMA_TYPE_FIELD]: 'Person' | 'Organization';
69
+ };
@@ -0,0 +1,79 @@
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
+ */ import { contentAuthorSchemaType } from "./content-authors.js";
17
+ import { nameSearchKey, nameSearchTokens } from "./name-search.js";
18
+ /*
19
+ * The fields the console's content tables QUERY, stamped by the writes that
20
+ * set an entry's title or an author's name (AGL-3321).
21
+ *
22
+ * ⛔ Kept apart from `collection-entries` and `content-authors`. Those two are
23
+ * read by the compose pipeline, so whatever they import ships in the site
24
+ * runtime a published page loads (`check:tenant-wire-weight`). These helpers
25
+ * are only ever called by the console and its routes, and `name-search` has
26
+ * no business on a customer's page.
27
+ */ /**
28
+ * The field the console's entries table searches (AGL-3321): every word
29
+ * prefix of the entry's title, as `nameSearchTokens` writes them, which the
30
+ * table's quick search and its Title "contains" filter ask with
31
+ * `array-contains` on the Firestore query.
32
+ *
33
+ * DERIVED, and never carried in a bundle. Every writer of `title` stamps it
34
+ * through {@link entryTitleSearchFields}: the entry editor's save, the
35
+ * resources route's create, the bundle import, and the seed and changelog
36
+ * scripts. `tools/scripts/backfill-entries-title-tokens.mjs` stamps the
37
+ * entries written before it. An entry without it still lists; it just cannot
38
+ * be found by a word of its title.
39
+ */ export const ENTRY_TITLE_TOKENS_FIELD = 'titleTokens';
40
+ /**
41
+ * The search half of a write that sets an entry's `title`. An entry with no
42
+ * title gets an empty array, so the field is present on every entry and the
43
+ * backfill has nothing left to find.
44
+ */ export function entryTitleSearchFields(title) {
45
+ return {
46
+ [ENTRY_TITLE_TOKENS_FIELD]: nameSearchTokens(typeof title === 'string' ? title : '')
47
+ };
48
+ }
49
+ /**
50
+ * Where an author's schema type is stored as the WORD the console's Authors
51
+ * table filters by (AGL-3321).
52
+ *
53
+ * `type` holds `HostEntityType` in whichever spelling wrote it: the number
54
+ * from the authors tab, a string `"1"`/`"2"` from an older bundle, nothing at
55
+ * all on a record `contentAuthorSchemaType` reads as `Organization`. A query
56
+ * matches one stored value, so an equality on `type` would miss every record
57
+ * spelled another way. This field is that one value.
58
+ */ export const AUTHOR_SCHEMA_TYPE_FIELD = 'schemaType';
59
+ /**
60
+ * The fields the Authors table's query reads (AGL-3321), derived from what the
61
+ * record already says: the name's lower-cased key and word-prefix tokens,
62
+ * normalized exactly as `nameSearchKey` / `nameSearchTokens` normalize a typed
63
+ * value, and the schema type as the row reads it.
64
+ *
65
+ * Spread at EVERY write of an author's `name` or `type` — the resources
66
+ * route's create, the authors tab's edit, the bundle import — and stamped on
67
+ * older records by `tools/scripts/backfill-authors-search-fields.mjs`. A
68
+ * record without them still reaches the picker, which reads every author; it
69
+ * is only the table's filters and search that cannot see it.
70
+ */ export function contentAuthorQueryFields(author) {
71
+ const name = typeof author.name === 'string' ? author.name : '';
72
+ return {
73
+ nameLower: nameSearchKey(name),
74
+ nameTokens: nameSearchTokens(name),
75
+ [AUTHOR_SCHEMA_TYPE_FIELD]: contentAuthorSchemaType(author.type)
76
+ };
77
+ }
78
+
79
+ //# sourceMappingURL=content-query-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/content-query-fields.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 { contentAuthorSchemaType } from './content-authors'\nimport type { HostEntityType } from '../foundation/definitions/platform.types'\nimport { nameSearchKey, nameSearchTokens } from './name-search'\n\n/*\n * The fields the console's content tables QUERY, stamped by the writes that\n * set an entry's title or an author's name (AGL-3321).\n *\n * ⛔ Kept apart from `collection-entries` and `content-authors`. Those two are\n * read by the compose pipeline, so whatever they import ships in the site\n * runtime a published page loads (`check:tenant-wire-weight`). These helpers\n * are only ever called by the console and its routes, and `name-search` has\n * no business on a customer's page.\n */\n\n/**\n * The field the console's entries table searches (AGL-3321): every word\n * prefix of the entry's title, as `nameSearchTokens` writes them, which the\n * table's quick search and its Title \"contains\" filter ask with\n * `array-contains` on the Firestore query.\n *\n * DERIVED, and never carried in a bundle. Every writer of `title` stamps it\n * through {@link entryTitleSearchFields}: the entry editor's save, the\n * resources route's create, the bundle import, and the seed and changelog\n * scripts. `tools/scripts/backfill-entries-title-tokens.mjs` stamps the\n * entries written before it. An entry without it still lists; it just cannot\n * be found by a word of its title.\n */\nexport const ENTRY_TITLE_TOKENS_FIELD = 'titleTokens'\n\n/**\n * The search half of a write that sets an entry's `title`. An entry with no\n * title gets an empty array, so the field is present on every entry and the\n * backfill has nothing left to find.\n */\nexport function entryTitleSearchFields(title: unknown): {\n [ENTRY_TITLE_TOKENS_FIELD]: string[]\n} {\n return {\n [ENTRY_TITLE_TOKENS_FIELD]: nameSearchTokens(\n typeof title === 'string' ? title : '',\n ),\n }\n}\n\n/**\n * Where an author's schema type is stored as the WORD the console's Authors\n * table filters by (AGL-3321).\n *\n * `type` holds `HostEntityType` in whichever spelling wrote it: the number\n * from the authors tab, a string `\"1\"`/`\"2\"` from an older bundle, nothing at\n * all on a record `contentAuthorSchemaType` reads as `Organization`. A query\n * matches one stored value, so an equality on `type` would miss every record\n * spelled another way. This field is that one value.\n */\nexport const AUTHOR_SCHEMA_TYPE_FIELD = 'schemaType'\n\n/**\n * The fields the Authors table's query reads (AGL-3321), derived from what the\n * record already says: the name's lower-cased key and word-prefix tokens,\n * normalized exactly as `nameSearchKey` / `nameSearchTokens` normalize a typed\n * value, and the schema type as the row reads it.\n *\n * Spread at EVERY write of an author's `name` or `type` — the resources\n * route's create, the authors tab's edit, the bundle import — and stamped on\n * older records by `tools/scripts/backfill-authors-search-fields.mjs`. A\n * record without them still reaches the picker, which reads every author; it\n * is only the table's filters and search that cannot see it.\n */\nexport function contentAuthorQueryFields(author: {\n name?: unknown\n type?: unknown\n}): {\n nameLower: string\n nameTokens: string[]\n [AUTHOR_SCHEMA_TYPE_FIELD]: 'Person' | 'Organization'\n} {\n const name = typeof author.name === 'string' ? author.name : ''\n return {\n nameLower: nameSearchKey(name),\n nameTokens: nameSearchTokens(name),\n [AUTHOR_SCHEMA_TYPE_FIELD]: contentAuthorSchemaType(\n author.type as HostEntityType | string | number | undefined | null,\n ),\n }\n}\n"],"names":["contentAuthorSchemaType","nameSearchKey","nameSearchTokens","ENTRY_TITLE_TOKENS_FIELD","entryTitleSearchFields","title","AUTHOR_SCHEMA_TYPE_FIELD","contentAuthorQueryFields","author","name","nameLower","nameTokens","type"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,uBAAuB,QAAQ,uBAAmB;AAE3D,SAASC,aAAa,EAAEC,gBAAgB,QAAQ,mBAAe;AAE/D;;;;;;;;;CASC,GAED;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC,2BAA2B,cAAa;AAErD;;;;CAIC,GACD,OAAO,SAASC,uBAAuBC,KAAc;IAGnD,OAAO;QACL,CAACF,yBAAyB,EAAED,iBAC1B,OAAOG,UAAU,WAAWA,QAAQ;IAExC;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,MAAMC,2BAA2B,aAAY;AAEpD;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,yBAAyBC,MAGxC;IAKC,MAAMC,OAAO,OAAOD,OAAOC,IAAI,KAAK,WAAWD,OAAOC,IAAI,GAAG;IAC7D,OAAO;QACLC,WAAWT,cAAcQ;QACzBE,YAAYT,iBAAiBO;QAC7B,CAACH,yBAAyB,EAAEN,wBAC1BQ,OAAOI,IAAI;IAEf;AACF"}
@@ -47,7 +47,7 @@ import { type AglynPostalAddress, type OrgCrmAssignmentRule } from '../foundatio
47
47
  import { type ConsentGroup } from './consent-groups';
48
48
  import { type ContactLifecycleStage, type CrmActivityKind, type CrmTaskKind } from './crm-kinds';
49
49
  import { type ContactInteraction, type ContactSegment, type ContactSource } from './contacts';
50
- import type { EmailState } from './email-state';
50
+ import { type EmailState } from './email-state';
51
51
  import { type ScopeToken } from './scope-tokens';
52
52
  export * from './crm-kinds';
53
53
  /**
@@ -1769,3 +1769,153 @@ export declare const CRM_MEDIA_IDS_MAX = 20;
1769
1769
  * that names its current location.
1770
1770
  */
1771
1771
  export declare function normalizeCrmMediaIds(value: unknown): string[];
1772
+ /** The search box's word-prefix tokens, on every CRM record a list searches. */
1773
+ export declare const CRM_SEARCH_TOKENS_FIELD = "searchTokens";
1774
+ /** The same tokens behind each `visibleTo` token — see the block header. */
1775
+ export declare const CRM_SCOPED_SEARCH_TOKENS_FIELD = "scopedSearchTokens";
1776
+ /** What joins a scope token to a search token — the platform's one join (`SCOPED_SEARCH_JOIN`). */
1777
+ export declare const CRM_SCOPED_SEARCH_JOIN = "~";
1778
+ /**
1779
+ * The most search tokens one record keeps. A name, an address, a company
1780
+ * and a title are a few dozen prefixes; the cap is what keeps a record with
1781
+ * twenty long tags from spending hundreds of index entries on them.
1782
+ */
1783
+ export declare const CRM_SEARCH_TOKENS_MAX = 200;
1784
+ /** The collections whose lists query the fields below. */
1785
+ export type CrmListCollection = 'leads' | 'contacts' | 'companies' | 'deals' | 'crmTasks';
1786
+ /**
1787
+ * The word-prefix tokens of several values, merged: every prefix (up to
1788
+ * twelve characters) of every whitespace-separated word, lower-cased —
1789
+ * exactly `nameSearchTokens` per value — plus, for an email address, the
1790
+ * words it is made of, so `acme` finds `dana@acme.com` and `dana` does too.
1791
+ */
1792
+ export declare function crmSearchTokens(values: readonly unknown[]): string[];
1793
+ /**
1794
+ * The two search fields for a record whose search reads `values`: the
1795
+ * tokens, and the same tokens behind each of its scope tokens through the
1796
+ * platform's one scoped writer (`scopedSearchTokens`).
1797
+ */
1798
+ export declare function crmSearchFields(visibleTo: unknown, values: readonly unknown[]): {
1799
+ searchTokens: string[];
1800
+ scopedSearchTokens: string[];
1801
+ };
1802
+ /**
1803
+ * The address verdict as a list asks it (AGL-3245): the state's status, or
1804
+ * `none` when nothing is known — stored rather than left absent, because a
1805
+ * query cannot find a field's absence and "Nothing known" is a filter.
1806
+ */
1807
+ export declare const CRM_EMAIL_STATUS_FIELD = "emailStatus";
1808
+ export declare const CRM_EMAIL_STATUS_NONE = "none";
1809
+ export declare function crmEmailStatusKey(record: Record<string, unknown> | null | undefined): string;
1810
+ /** A lead's lead source as the list compares it: the picklist's key, `null` for none. */
1811
+ export declare const CRM_LEAD_SOURCE_KEY_FIELD = "leadSourceKey";
1812
+ export declare function crmLeadSourceKey(value: unknown): string | null;
1813
+ /** What a lead's search box reads (AGL-3246). */
1814
+ export declare const CRM_LEAD_SEARCH_SOURCES: readonly ["name", "email", "company", "jobTitle", "tags"];
1815
+ /**
1816
+ * A lead's campaigns behind each of its `visibleTo` tokens
1817
+ * (`host:abc~{campaignId}`), for the Leads list's Campaign filter under a
1818
+ * site: there the scope clause is the query's one array clause, and a
1819
+ * campaign asked of these keys answers both at once — the way the search
1820
+ * folds into the scope through `scopedSearchTokens`.
1821
+ */
1822
+ export declare const CRM_LEAD_SCOPED_CAMPAIGNS_FIELD = "scopedCampaignIds";
1823
+ /**
1824
+ * Every field the Leads list queries by, from the lead as stored (or as it
1825
+ * will be once written): the search tokens, its campaigns behind its scope,
1826
+ * the status — `new` for a lead nobody has touched, written so `status in
1827
+ * [new, working]` finds it — the lead source key and the address verdict.
1828
+ */
1829
+ export declare function crmLeadListFields(record: object): {
1830
+ searchTokens: string[];
1831
+ scopedSearchTokens: string[];
1832
+ scopedCampaignIds: string[];
1833
+ status: CrmLeadStatus;
1834
+ leadSourceKey: string | null;
1835
+ emailStatus: string;
1836
+ };
1837
+ /** What a company's search box reads: its name and its domain. */
1838
+ export declare const CRM_COMPANY_SEARCH_SOURCES: readonly ["name", "domain"];
1839
+ export declare function crmCompanyListFields(record: object): {
1840
+ searchTokens: string[];
1841
+ scopedSearchTokens: string[];
1842
+ };
1843
+ /**
1844
+ * What a deal's search box reads: its title (AGL-3315) — as word prefixes,
1845
+ * and as `titleLower`, the whole title's key, which a reader whose access
1846
+ * is some sites searches by its start.
1847
+ */
1848
+ export declare function crmDealListFields(record: object): {
1849
+ searchTokens: string[];
1850
+ scopedSearchTokens: string[];
1851
+ titleLower: string;
1852
+ };
1853
+ /** What a task's search box reads: its title. */
1854
+ export declare function crmTaskListFields(record: object): {
1855
+ searchTokens: string[];
1856
+ scopedSearchTokens: string[];
1857
+ };
1858
+ export declare const CRM_CONTACT_FACET_KEYS_FIELD = "facetKeys";
1859
+ /** The group a key names at the organization level: any holder. */
1860
+ export declare const CRM_FACET_KEY_ANY_GROUP = "*";
1861
+ /** The most facet keys one contact keeps. */
1862
+ export declare const CRM_FACET_KEYS_MAX = 600;
1863
+ /** The facet fields a contact list filters by, as their keys name them. */
1864
+ export type CrmFacetKeyField = 'owner' | 'stage' | 'source' | 'company' | 'tag' | 'orders' | 'ltv' | `custom.${string}`;
1865
+ /** A facet value as its key spells it, or `null` for a value a key cannot hold. */
1866
+ export declare function crmFacetKeyValue(value: unknown): string | null;
1867
+ /** One facet key: `{group}:{field}` for presence, `{group}:{field}={value}` for a value. */
1868
+ export declare function crmFacetKey(group: string, field: CrmFacetKeyField, value?: string): string;
1869
+ /** Every facet key a contact carries — see the block above. */
1870
+ export declare function crmContactFacetKeys(record: object): string[];
1871
+ /**
1872
+ * What a contact's search box reads: the shared identity — the canonical
1873
+ * name and every address — and the two profile values a person is looked
1874
+ * up by, through their top-level search echoes (`HostContact.phone`,
1875
+ * `HostContact.companyName`): the company's words, and the phone's digits.
1876
+ */
1877
+ export declare const CRM_CONTACT_SEARCH_SOURCES: readonly ["name", "email", "alternateEmails", "companyName"];
1878
+ /**
1879
+ * A phone number as the words a search box finds it by: its digits whole,
1880
+ * without a leading country code of one to three digits, and its last seven
1881
+ * and last four — so `5551234`, `555123` and `4567` all find +1 555 123 4567.
1882
+ * Typed as digits: a search reads one word, and `(555)` is not one of these.
1883
+ */
1884
+ export declare function crmPhoneSearchWords(phone: unknown): string[];
1885
+ export declare function crmContactListFields(record: object): {
1886
+ searchTokens: string[];
1887
+ scopedSearchTokens: string[];
1888
+ facetKeys: string[];
1889
+ emailStatus: string;
1890
+ };
1891
+ /**
1892
+ * The stored fields each collection's list fields are computed from. A
1893
+ * write that sets one of them must restamp the record — or spread
1894
+ * {@link crmListFields} over the document it wrote.
1895
+ */
1896
+ export declare const CRM_LIST_FIELD_INPUTS: Readonly<Record<CrmListCollection, readonly string[]>>;
1897
+ /** Every list field of one record, from the record as stored. */
1898
+ export declare function crmListFields(collection: CrmListCollection, record: object): Record<string, unknown>;
1899
+ /**
1900
+ * The list fields a stored record carries WRONGLY — the patch that brings
1901
+ * it level, `{}` when it already is. What a restamp and a backfill write.
1902
+ */
1903
+ export declare function crmListFieldsPatch(collection: CrmListCollection, record: object): Record<string, unknown>;
1904
+ /**
1905
+ * The field each collection's list ORDERS or asks `null` of, which a record
1906
+ * must therefore carry even when it has no value: a contact, a company and a
1907
+ * deal carry `nextTaskAtMs` ("No next activity" asks `== null`), and a task
1908
+ * carries `dueAtMs` (every task view orders by it, and Firestore leaves a
1909
+ * document without the ordered field out of the answer). An absent field is
1910
+ * neither `null` nor anything else a query can ask for.
1911
+ */
1912
+ export declare const CRM_LIST_NULLABLE_FIELD: Readonly<Record<CrmListCollection, string | null>>;
1913
+ /**
1914
+ * The list fields a NEW record is written with: {@link crmListFields}, and
1915
+ * the collection's {@link CRM_LIST_NULLABLE_FIELD} as `null` when the record
1916
+ * does not set it — nothing is scheduled against a record that did not
1917
+ * exist, and a task created undated is still one its views must reach.
1918
+ */
1919
+ export declare function crmNewRecordListFields(collection: CrmListCollection, record: object): Record<string, unknown>;
1920
+ /** Whether a patch touches a field the collection's list fields are computed from. */
1921
+ export declare function crmListFieldsTouched(collection: CrmListCollection, patch: Record<string, unknown>): boolean;