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

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 (56) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/artifact-list-keys.js +10 -0
  3. package/src/lib/app-utils/artifact-list-keys.js.map +1 -1
  4. package/src/lib/app-utils/crm.d.ts +19 -0
  5. package/src/lib/app-utils/crm.js +27 -1
  6. package/src/lib/app-utils/crm.js.map +1 -1
  7. package/src/lib/app-utils/docs-help.generated.d.ts +4 -4
  8. package/src/lib/app-utils/docs-help.generated.js +10 -0
  9. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  10. package/src/lib/app-utils/docs-index.generated.js +78 -64
  11. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  12. package/src/lib/app-utils/email-state.d.ts +7 -3
  13. package/src/lib/app-utils/email-state.js +22 -6
  14. package/src/lib/app-utils/email-state.js.map +1 -1
  15. package/src/lib/app-utils/forms.d.ts +101 -9
  16. package/src/lib/app-utils/forms.js +137 -7
  17. package/src/lib/app-utils/forms.js.map +1 -1
  18. package/src/lib/app-utils/health-report.d.ts +3 -3
  19. package/src/lib/app-utils/health-report.js +9 -9
  20. package/src/lib/app-utils/health-report.js.map +1 -1
  21. package/src/lib/app-utils/host-naming.d.ts +12 -7
  22. package/src/lib/app-utils/host-naming.js +18 -3
  23. package/src/lib/app-utils/host-naming.js.map +1 -1
  24. package/src/lib/app-utils/lockdown.d.ts +19 -0
  25. package/src/lib/app-utils/lockdown.js +21 -0
  26. package/src/lib/app-utils/lockdown.js.map +1 -1
  27. package/src/lib/app-utils/media-embedded-fields.d.ts +40 -0
  28. package/src/lib/app-utils/media-embedded-fields.js +77 -0
  29. package/src/lib/app-utils/media-embedded-fields.js.map +1 -1
  30. package/src/lib/app-utils/media-metadata.d.ts +17 -1
  31. package/src/lib/app-utils/media-metadata.js +22 -2
  32. package/src/lib/app-utils/media-metadata.js.map +1 -1
  33. package/src/lib/app-utils/payment-risk.d.ts +75 -0
  34. package/src/lib/app-utils/payment-risk.js +139 -0
  35. package/src/lib/app-utils/payment-risk.js.map +1 -0
  36. package/src/lib/app-utils/plugin-api-request-host.d.ts +29 -0
  37. package/src/lib/app-utils/plugin-api-request-host.js +90 -0
  38. package/src/lib/app-utils/plugin-api-request-host.js.map +1 -0
  39. package/src/lib/app-utils/plugin-bundle-checks.d.ts +3 -2
  40. package/src/lib/app-utils/plugin-bundle-checks.js +76 -5
  41. package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -1
  42. package/src/lib/app-utils/webhook-delivery.js +4 -1
  43. package/src/lib/app-utils/webhook-delivery.js.map +1 -1
  44. package/src/lib/plugin-manager/billing-webhook-hooks.d.ts +9 -0
  45. package/src/lib/plugin-manager/billing-webhook-hooks.js +5 -1
  46. package/src/lib/plugin-manager/billing-webhook-hooks.js.map +1 -1
  47. package/src/lib/plugin-manager/plugin-activity-actions.d.ts +9 -1
  48. package/src/lib/plugin-manager/plugin-activity-actions.js +11 -1
  49. package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
  50. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +8 -0
  51. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
  52. package/src/lib/plugin-manager/plugin-events.d.ts +22 -0
  53. package/src/lib/plugin-manager/plugin-events.js.map +1 -1
  54. package/src/lib/plugin-manager/plugin-jobs.d.ts +8 -4
  55. package/src/lib/plugin-manager/plugin-jobs.js +8 -4
  56. package/src/lib/plugin-manager/plugin-jobs.js.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/aglyn",
3
- "version": "1.0.0-beta.200",
3
+ "version": "1.0.0-beta.201",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -33,16 +33,16 @@
33
33
  "./package.json": "./package.json"
34
34
  },
35
35
  "dependencies": {
36
- "@aglyn/shared-data-enums": "1.0.0-beta.200",
37
- "@aglyn/shared-data-mdi": "1.0.0-beta.200",
38
- "@aglyn/shared-data-types": "1.0.0-beta.200",
39
- "@aglyn/shared-util-email": "1.0.0-beta.200",
40
- "@aglyn/shared-util-first-touch": "1.0.0-beta.200",
41
- "@aglyn/shared-util-http": "1.0.0-beta.200",
42
- "@aglyn/shared-util-logger": "1.0.0-beta.200",
43
- "@aglyn/shared-util-timestamp": "1.0.0-beta.200",
44
- "@aglyn/shared-util-tools": "1.0.0-beta.200",
45
- "@aglyn/shared-util-vendor": "1.0.0-beta.200",
36
+ "@aglyn/shared-data-enums": "1.0.0-beta.201",
37
+ "@aglyn/shared-data-mdi": "1.0.0-beta.201",
38
+ "@aglyn/shared-data-types": "1.0.0-beta.201",
39
+ "@aglyn/shared-util-email": "1.0.0-beta.201",
40
+ "@aglyn/shared-util-first-touch": "1.0.0-beta.201",
41
+ "@aglyn/shared-util-http": "1.0.0-beta.201",
42
+ "@aglyn/shared-util-logger": "1.0.0-beta.201",
43
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.201",
44
+ "@aglyn/shared-util-tools": "1.0.0-beta.201",
45
+ "@aglyn/shared-util-vendor": "1.0.0-beta.201",
46
46
  "@data-driven-forms/react-form-renderer": "^4.2.0",
47
47
  "@msgpack/msgpack": "^3.1.3",
48
48
  "@types/unist": "^3.0.3",
@@ -39,6 +39,13 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
39
39
  * so does a deleted template (`artifactDeleteListKeys`), which
40
40
  * is how the library's query leaves tombstones out;
41
41
  *
42
+ * deletedAt on a screen, `null` — the flag a campaign's screens list
43
+ * asks for (`deletedAt == null`) to leave tombstones out. A
44
+ * query cannot ask for a field to be absent, so a live
45
+ * screen must STORE the null; a delete overwrites it with
46
+ * the time (and the security rules let an editor do that
47
+ * only while it is null, so a tombstone stays one);
48
+ *
42
49
  * A DELETED screen gets none of them. The email templates list orders by
43
50
  * `nameLower`, and an email template's delete clears its name keys so the
44
51
  * tombstone leaves that order (a query cannot ask for `deletedAt` to be
@@ -92,6 +99,9 @@ const record = (value)=>value && typeof value === 'object' ? value : {};
92
99
  if (!isListedArtifactCollection(collection)) return {};
93
100
  if (collection === 'screens' && doc['deletedAt'] != null) return {};
94
101
  const keys = _extends({}, displayNameSearchFields(artifactSearchName(collection, doc)));
102
+ // Stored, not omitted: `deletedAt == null` matches only a document that
103
+ // holds the field (AGL-3321, a campaign's screens).
104
+ if (collection === 'screens') keys['deletedAt'] = null;
95
105
  if (collection === 'components' && doc['kind'] !== 'email') keys['kind'] = 'site';
96
106
  if (collection === 'templates') {
97
107
  if (!TEMPLATE_KINDS.includes(String(doc['kind']))) keys['kind'] = 'page';
@@ -1 +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"}
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 * deletedAt on a screen, `null` — the flag a campaign's screens list\n * asks for (`deletedAt == null`) to leave tombstones out. A\n * query cannot ask for a field to be absent, so a live\n * screen must STORE the null; a delete overwrites it with\n * the time (and the security rules let an editor do that\n * only while it is null, so a tombstone stays one);\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 // Stored, not omitted: `deletedAt == null` matches only a document that\n // holds the field (AGL-3321, a campaign's screens).\n if (collection === 'screens') keys['deletedAt'] = null\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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CC,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,wEAAwE;IACxE,oDAAoD;IACpD,IAAIP,eAAe,WAAWW,IAAI,CAAC,YAAY,GAAG;IAClD,IAAIX,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"}
@@ -866,6 +866,25 @@ export declare function isCrmFieldObject(value: unknown): value is CrmFieldObjec
866
866
  * under its key sit on contacts.
867
867
  */
868
868
  export declare function fieldDefinitionObject(definition: Pick<ContactFieldDefinition, 'object'> | null | undefined): CrmFieldObject;
869
+ /**
870
+ * What the CRM › Fields table's query reads on a definition (AGL-3335):
871
+ *
872
+ * object which tab it belongs to, stored even for a contact's, because
873
+ * a query cannot find a field's absence;
874
+ * required a boolean on every definition, for the Required filter;
875
+ * searchTokens every word prefix of its name and of its key, the key read
876
+ * whole and split at `_`, `-` and `.`, so "plan" finds
877
+ * `plan_interest` and "interest" does too.
878
+ *
879
+ * Its script-side twin is `crmFieldListFields` in
880
+ * `tools/scripts/lib/org-record-list-fields.mjs`; both answer the
881
+ * `fieldDefinitions` of `org-record-list-fields.fixtures.json`.
882
+ */
883
+ export declare function crmFieldListFields(definition: Record<string, unknown> | null | undefined): {
884
+ object: CrmFieldObject;
885
+ required: boolean;
886
+ searchTokens: string[];
887
+ };
869
888
  /**
870
889
  * The definition type under the name new code uses (AGL-2661). The type,
871
890
  * the collection (`contactFields`) and the rules match keep the contact
@@ -47,7 +47,7 @@ import { consentGroupScope } from "./consent-groups.js";
47
47
  import { CONTACT_LIFECYCLE_STAGES, isContactLifecycleStage } from "./crm-kinds.js";
48
48
  import { CONTACT_ALTERNATE_EMAILS_FIELD, CONTACT_FACETS_FIELD, CONTACT_SOURCE_LABELS, normalizeContactEmail, readContactFacet } from "./contacts.js";
49
49
  import { isEmailStateStatus } from "./email-state.js";
50
- import { NAME_TOKEN_MAX_PREFIX, nameSearchKey, SCOPED_SEARCH_JOIN, scopedSearchTokens } from "./name-search.js";
50
+ import { NAME_TOKEN_MAX_PREFIX, nameSearchKey, nameSearchTokens, SCOPED_SEARCH_JOIN, scopedSearchTokens } from "./name-search.js";
51
51
  import { MAX_SCOPE_HOSTS, ORG_SCOPE_TOKEN } from "./scope-tokens.js";
52
52
  // The fixed vocabularies and their guards live in a leaf module; every name
53
53
  // stays importable from here.
@@ -592,6 +592,32 @@ export function isCrmFieldObject(value) {
592
592
  */ export function fieldDefinitionObject(definition) {
593
593
  return isCrmFieldObject(definition == null ? void 0 : definition.object) ? definition.object : 'contact';
594
594
  }
595
+ /**
596
+ * What the CRM › Fields table's query reads on a definition (AGL-3335):
597
+ *
598
+ * object which tab it belongs to, stored even for a contact's, because
599
+ * a query cannot find a field's absence;
600
+ * required a boolean on every definition, for the Required filter;
601
+ * searchTokens every word prefix of its name and of its key, the key read
602
+ * whole and split at `_`, `-` and `.`, so "plan" finds
603
+ * `plan_interest` and "interest" does too.
604
+ *
605
+ * Its script-side twin is `crmFieldListFields` in
606
+ * `tools/scripts/lib/org-record-list-fields.mjs`; both answer the
607
+ * `fieldDefinitions` of `org-record-list-fields.fixtures.json`.
608
+ */ export function crmFieldListFields(definition) {
609
+ const label = typeof (definition == null ? void 0 : definition['label']) === 'string' ? definition['label'] : '';
610
+ const key = typeof (definition == null ? void 0 : definition['key']) === 'string' ? definition['key'] : '';
611
+ return {
612
+ object: fieldDefinitionObject(definition),
613
+ required: (definition == null ? void 0 : definition['required']) === true,
614
+ searchTokens: nameSearchTokens([
615
+ label,
616
+ key,
617
+ key.replace(/[_.-]+/g, ' ')
618
+ ].join(' '))
619
+ };
620
+ }
595
621
  /** What a stored field key must look like — a letter, then up to 39 of `[a-z0-9_]`. */ export const CONTACT_FIELD_KEY_PATTERN = /^[a-z][a-z0-9_]{0,39}$/;
596
622
  /**
597
623
  * A typed label, as the key it would be stored under, or `null` when nothing