@aglyn/tenant-runtime 1.0.0-beta.143

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 (120) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +26 -0
  3. package/package.json +39 -0
  4. package/src/index.d.ts +21 -0
  5. package/src/index.js +22 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/apply-publish-schedule.d.ts +34 -0
  8. package/src/lib/apply-publish-schedule.js +285 -0
  9. package/src/lib/apply-publish-schedule.js.map +1 -0
  10. package/src/lib/assign-contact-owner.d.ts +162 -0
  11. package/src/lib/assign-contact-owner.js +322 -0
  12. package/src/lib/assign-contact-owner.js.map +1 -0
  13. package/src/lib/associate-company-by-domain.d.ts +70 -0
  14. package/src/lib/associate-company-by-domain.js +150 -0
  15. package/src/lib/associate-company-by-domain.js.map +1 -0
  16. package/src/lib/author-page-nodes.d.ts +36 -0
  17. package/src/lib/author-page-nodes.js +296 -0
  18. package/src/lib/author-page-nodes.js.map +1 -0
  19. package/src/lib/built-in-page-layout.d.ts +48 -0
  20. package/src/lib/built-in-page-layout.js +107 -0
  21. package/src/lib/built-in-page-layout.js.map +1 -0
  22. package/src/lib/capture-host-contact.d.ts +95 -0
  23. package/src/lib/capture-host-contact.js +137 -0
  24. package/src/lib/capture-host-contact.js.map +1 -0
  25. package/src/lib/collection-fallback-nodes.d.ts +90 -0
  26. package/src/lib/collection-fallback-nodes.js +602 -0
  27. package/src/lib/collection-fallback-nodes.js.map +1 -0
  28. package/src/lib/compose-author-page.d.ts +109 -0
  29. package/src/lib/compose-author-page.js +225 -0
  30. package/src/lib/compose-author-page.js.map +1 -0
  31. package/src/lib/compose-collection-page.d.ts +94 -0
  32. package/src/lib/compose-collection-page.js +256 -0
  33. package/src/lib/compose-collection-page.js.map +1 -0
  34. package/src/lib/compose-screen-nodes.d.ts +156 -0
  35. package/src/lib/compose-screen-nodes.js +547 -0
  36. package/src/lib/compose-screen-nodes.js.map +1 -0
  37. package/src/lib/compose-search-page.d.ts +39 -0
  38. package/src/lib/compose-search-page.js +57 -0
  39. package/src/lib/compose-search-page.js.map +1 -0
  40. package/src/lib/convert-host-lead.d.ts +189 -0
  41. package/src/lib/convert-host-lead.js +443 -0
  42. package/src/lib/convert-host-lead.js.map +1 -0
  43. package/src/lib/defer-lazy-panels.d.ts +58 -0
  44. package/src/lib/defer-lazy-panels.js +171 -0
  45. package/src/lib/defer-lazy-panels.js.map +1 -0
  46. package/src/lib/element-animation-assets.d.ts +62 -0
  47. package/src/lib/element-animation-assets.js +290 -0
  48. package/src/lib/element-animation-assets.js.map +1 -0
  49. package/src/lib/emit-host-event.d.ts +29 -0
  50. package/src/lib/emit-host-event.js +30 -0
  51. package/src/lib/emit-host-event.js.map +1 -0
  52. package/src/lib/enrich-gated-page.d.ts +67 -0
  53. package/src/lib/enrich-gated-page.js +134 -0
  54. package/src/lib/enrich-gated-page.js.map +1 -0
  55. package/src/lib/entry-link-routes.d.ts +61 -0
  56. package/src/lib/entry-link-routes.js +169 -0
  57. package/src/lib/entry-link-routes.js.map +1 -0
  58. package/src/lib/get-author-content.d.ts +119 -0
  59. package/src/lib/get-author-content.js +288 -0
  60. package/src/lib/get-author-content.js.map +1 -0
  61. package/src/lib/get-collection-content.d.ts +290 -0
  62. package/src/lib/get-collection-content.js +565 -0
  63. package/src/lib/get-collection-content.js.map +1 -0
  64. package/src/lib/get-components.d.ts +24 -0
  65. package/src/lib/get-components.js +102 -0
  66. package/src/lib/get-components.js.map +1 -0
  67. package/src/lib/get-datasets.d.ts +45 -0
  68. package/src/lib/get-datasets.js +169 -0
  69. package/src/lib/get-datasets.js.map +1 -0
  70. package/src/lib/get-forms.d.ts +24 -0
  71. package/src/lib/get-forms.js +116 -0
  72. package/src/lib/get-forms.js.map +1 -0
  73. package/src/lib/get-layout-version.d.ts +26 -0
  74. package/src/lib/get-layout-version.js +99 -0
  75. package/src/lib/get-layout-version.js.map +1 -0
  76. package/src/lib/get-media-asset-facts.d.ts +74 -0
  77. package/src/lib/get-media-asset-facts.js +116 -0
  78. package/src/lib/get-media-asset-facts.js.map +1 -0
  79. package/src/lib/get-plugin-installs.d.ts +29 -0
  80. package/src/lib/get-plugin-installs.js +94 -0
  81. package/src/lib/get-plugin-installs.js.map +1 -0
  82. package/src/lib/get-screen-version.d.ts +27 -0
  83. package/src/lib/get-screen-version.js +66 -0
  84. package/src/lib/get-screen-version.js.map +1 -0
  85. package/src/lib/get-screen.d.ts +39 -0
  86. package/src/lib/get-screen.js +126 -0
  87. package/src/lib/get-screen.js.map +1 -0
  88. package/src/lib/get-variables.d.ts +41 -0
  89. package/src/lib/get-variables.js +128 -0
  90. package/src/lib/get-variables.js.map +1 -0
  91. package/src/lib/get-video-asset-facts.d.ts +22 -0
  92. package/src/lib/get-video-asset-facts.js +22 -0
  93. package/src/lib/get-video-asset-facts.js.map +1 -0
  94. package/src/lib/host-event-listeners.d.ts +83 -0
  95. package/src/lib/host-event-listeners.js +91 -0
  96. package/src/lib/host-event-listeners.js.map +1 -0
  97. package/src/lib/org-permissions.d.ts +63 -0
  98. package/src/lib/org-permissions.js +187 -0
  99. package/src/lib/org-permissions.js.map +1 -0
  100. package/src/lib/repeat-record-pages.d.ts +48 -0
  101. package/src/lib/repeat-record-pages.js +64 -0
  102. package/src/lib/repeat-record-pages.js.map +1 -0
  103. package/src/lib/required-site-plugins.d.ts +114 -0
  104. package/src/lib/required-site-plugins.js +121 -0
  105. package/src/lib/required-site-plugins.js.map +1 -0
  106. package/src/lib/resolve-dataset.d.ts +40 -0
  107. package/src/lib/resolve-dataset.js +64 -0
  108. package/src/lib/resolve-dataset.js.map +1 -0
  109. package/src/lib/search-results-nodes.d.ts +48 -0
  110. package/src/lib/search-results-nodes.js +302 -0
  111. package/src/lib/search-results-nodes.js.map +1 -0
  112. package/src/lib/social-image-facts.d.ts +107 -0
  113. package/src/lib/social-image-facts.js +133 -0
  114. package/src/lib/social-image-facts.js.map +1 -0
  115. package/src/lib/stamp-form-dataset-bindings.d.ts +32 -0
  116. package/src/lib/stamp-form-dataset-bindings.js +59 -0
  117. package/src/lib/stamp-form-dataset-bindings.js.map +1 -0
  118. package/src/lib/template-screens.d.ts +227 -0
  119. package/src/lib/template-screens.js +364 -0
  120. package/src/lib/template-screens.js.map +1 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 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 * as Aglyn from '@aglyn/aglyn/server';
18
+ export declare function getScreenVersion(options: {
19
+ hostId: Aglyn.HostUid;
20
+ screenId: Aglyn.ScreenUid;
21
+ versionId: Aglyn.VersionUid;
22
+ }): Promise<{
23
+ version: Aglyn.AglynScreen;
24
+ nextPageToken: string;
25
+ error: any;
26
+ }>;
27
+ export default getScreenVersion;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 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 { firebaseAdmin, screenVersionConverter } from "@aglyn/tenant-data-admin";
17
+ import { PUBLISHED_SITE_DATA_TTL_SECONDS, tenantDataTag, withRenderCache } from "@aglyn/tenant-data-admin/render-cache";
18
+ /**
19
+ * The version doc is the LARGEST read of a render (the whole node tree), and
20
+ * it is keyed by `versionId` — a publish points the screen doc at a NEW
21
+ * version, so a re-publish lands under a new cache key on its own. The TTL
22
+ * is finite rather than treating versions as immutable, because the editor
23
+ * can save into an already-published version doc. It is
24
+ * `PUBLISHED_SITE_DATA_TTL_SECONDS`, an HOUR — long enough that a save into
25
+ * a live version is NOT what makes it visible; the publish-path
26
+ * `revalidateTag(tenantDataTag(hostId))` bust is (AGL-1302). A write that
27
+ * reaches Firestore without going through publish waits out the full TTL.
28
+ */ const SCREEN_VERSION_TTL_SECONDS = PUBLISHED_SITE_DATA_TTL_SECONDS;
29
+ async function readScreenVersionDoc(hostId, screenId, versionId) {
30
+ const snapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).collection('screens').doc(screenId).collection('versions').withConverter(screenVersionConverter).doc(versionId).get();
31
+ return snapshot.exists ? snapshot.data() : null;
32
+ }
33
+ export async function getScreenVersion(options) {
34
+ const hostId = options == null ? void 0 : options.hostId;
35
+ const screenId = options == null ? void 0 : options.screenId;
36
+ const versionId = options == null ? void 0 : options.versionId;
37
+ const data = {
38
+ version: undefined,
39
+ nextPageToken: '',
40
+ error: null
41
+ };
42
+ try {
43
+ const version = await withRenderCache({
44
+ key: [
45
+ 'tenant-screen-version',
46
+ hostId,
47
+ screenId,
48
+ versionId
49
+ ],
50
+ revalidate: SCREEN_VERSION_TTL_SECONDS,
51
+ tags: [
52
+ tenantDataTag(hostId)
53
+ ],
54
+ read: ()=>readScreenVersionDoc(hostId, screenId, versionId),
55
+ store: (value)=>value !== null
56
+ });
57
+ if (version) data.version = version;
58
+ } catch (error) {
59
+ console.error(error);
60
+ data.error = error;
61
+ }
62
+ return data;
63
+ }
64
+ export default getScreenVersion;
65
+
66
+ //# sourceMappingURL=get-screen-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/tenant/runtime/src/lib/get-screen-version.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2024 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 * as Aglyn from '@aglyn/aglyn/server'\nimport { firebaseAdmin, screenVersionConverter } from '@aglyn/tenant-data-admin'\nimport {\n PUBLISHED_SITE_DATA_TTL_SECONDS,\n tenantDataTag,\n withRenderCache,\n} from '@aglyn/tenant-data-admin/render-cache'\n\n/**\n * The version doc is the LARGEST read of a render (the whole node tree), and\n * it is keyed by `versionId` — a publish points the screen doc at a NEW\n * version, so a re-publish lands under a new cache key on its own. The TTL\n * is finite rather than treating versions as immutable, because the editor\n * can save into an already-published version doc. It is\n * `PUBLISHED_SITE_DATA_TTL_SECONDS`, an HOUR — long enough that a save into\n * a live version is NOT what makes it visible; the publish-path\n * `revalidateTag(tenantDataTag(hostId))` bust is (AGL-1302). A write that\n * reaches Firestore without going through publish waits out the full TTL.\n */\nconst SCREEN_VERSION_TTL_SECONDS = PUBLISHED_SITE_DATA_TTL_SECONDS\n\nasync function readScreenVersionDoc(\n hostId: string,\n screenId: string,\n versionId: string,\n): Promise<Aglyn.AglynScreen | null> {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('screens')\n .doc(screenId)\n .collection('versions')\n .withConverter(screenVersionConverter)\n .doc(versionId)\n .get()\n return snapshot.exists ? (snapshot.data() as Aglyn.AglynScreen) : null\n}\n\nexport async function getScreenVersion(options: {\n hostId: Aglyn.HostUid\n screenId: Aglyn.ScreenUid\n versionId: Aglyn.VersionUid\n}) {\n const hostId = options?.hostId as string\n const screenId = options?.screenId as string\n const versionId = options?.versionId as string\n const data = {\n version: undefined as Aglyn.AglynScreen,\n nextPageToken: '',\n error: null,\n }\n\n try {\n const version = await withRenderCache({\n key: ['tenant-screen-version', hostId, screenId, versionId],\n revalidate: SCREEN_VERSION_TTL_SECONDS,\n tags: [tenantDataTag(hostId)],\n read: () => readScreenVersionDoc(hostId, screenId, versionId),\n store: (value) => value !== null,\n })\n if (version) data.version = version\n } catch (error) {\n console.error(error)\n data.error = error\n }\n\n return data\n}\n\nexport default getScreenVersion\n"],"names":["firebaseAdmin","screenVersionConverter","PUBLISHED_SITE_DATA_TTL_SECONDS","tenantDataTag","withRenderCache","SCREEN_VERSION_TTL_SECONDS","readScreenVersionDoc","hostId","screenId","versionId","snapshot","app","firestore","collection","doc","withConverter","get","exists","data","getScreenVersion","options","version","undefined","nextPageToken","error","key","revalidate","tags","read","store","value","console"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,aAAa,EAAEC,sBAAsB,QAAQ,2BAA0B;AAChF,SACEC,+BAA+B,EAC/BC,aAAa,EACbC,eAAe,QACV,wCAAuC;AAE9C;;;;;;;;;;CAUC,GACD,MAAMC,6BAA6BH;AAEnC,eAAeI,qBACbC,MAAc,EACdC,QAAgB,EAChBC,SAAiB;IAEjB,MAAMC,WAAW,MAAMV,cACpBW,GAAG,GACHC,SAAS,GACTC,UAAU,CAAC,SACXC,GAAG,CAACP,QACJM,UAAU,CAAC,WACXC,GAAG,CAACN,UACJK,UAAU,CAAC,YACXE,aAAa,CAACd,wBACda,GAAG,CAACL,WACJO,GAAG;IACN,OAAON,SAASO,MAAM,GAAIP,SAASQ,IAAI,KAA2B;AACpE;AAEA,OAAO,eAAeC,iBAAiBC,OAItC;IACC,MAAMb,SAASa,2BAAAA,QAASb,MAAM;IAC9B,MAAMC,WAAWY,2BAAAA,QAASZ,QAAQ;IAClC,MAAMC,YAAYW,2BAAAA,QAASX,SAAS;IACpC,MAAMS,OAAO;QACXG,SAASC;QACTC,eAAe;QACfC,OAAO;IACT;IAEA,IAAI;QACF,MAAMH,UAAU,MAAMjB,gBAAgB;YACpCqB,KAAK;gBAAC;gBAAyBlB;gBAAQC;gBAAUC;aAAU;YAC3DiB,YAAYrB;YACZsB,MAAM;gBAACxB,cAAcI;aAAQ;YAC7BqB,MAAM,IAAMtB,qBAAqBC,QAAQC,UAAUC;YACnDoB,OAAO,CAACC,QAAUA,UAAU;QAC9B;QACA,IAAIT,SAASH,KAAKG,OAAO,GAAGA;IAC9B,EAAE,OAAOG,OAAO;QACdO,QAAQP,KAAK,CAACA;QACdN,KAAKM,KAAK,GAAGA;IACf;IAEA,OAAON;AACT;AAEA,eAAeC,iBAAgB"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022 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 * as Aglyn from '@aglyn/aglyn/server';
18
+ export declare function getScreen(options: {
19
+ screenId: Aglyn.ScreenUid;
20
+ hostId: Aglyn.HostUid;
21
+ /**
22
+ * Serve a `kind: 'template'` document too (AGL-1400).
23
+ *
24
+ * Set ONLY by the two composers that render a template against a routed
25
+ * subject — the collection entry page and commerce's PDP/catalog pages. A
26
+ * caller resolving a request path must leave it off: that is what makes the
27
+ * exclusion honest (an excluded screen genuinely is not a page) and it is the
28
+ * same trade AGL-1383 made for `kind: 'email'`.
29
+ *
30
+ * It also keys the render cache, so a template fetched for composition can
31
+ * never be handed back to a path-resolving caller out of the same slot.
32
+ */
33
+ allowTemplate?: boolean;
34
+ }): Promise<{
35
+ screen: Aglyn.AglynScreen;
36
+ nextPageToken: string;
37
+ error: any;
38
+ }>;
39
+ export default getScreen;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022 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 * as Aglyn from "@aglyn/aglyn/server";
17
+ import { firebaseAdmin, screenConverter } from "@aglyn/tenant-data-admin";
18
+ import { PUBLISH_POINTER_TTL_SECONDS, tenantDataTag, withRenderCache } from "@aglyn/tenant-data-admin/render-cache";
19
+ /**
20
+ * Backstop TTL only (AGL-1302): a screen publish flips `versionId` on this
21
+ * doc, and the publish path already busts `tenant-data:{hostId}` through the
22
+ * tenant `/api/revalidate` route, so publishes stay as instant as AGL-1150
23
+ * made them.
24
+ *
25
+ * This is the ONE read whose data a screen publish changes without changing
26
+ * its cache key — the version body next door is keyed by `versionId` and
27
+ * misses on its own — so when the announce does not arrive, this document is
28
+ * the entire reason a published page keeps serving the old one. It is held to
29
+ * the page's own ISR window rather than the hour every other published read
30
+ * gets (AGL-2573); `PUBLISH_POINTER_TTL_SECONDS` carries that argument.
31
+ */ const SCREEN_DOC_TTL_SECONDS = PUBLISH_POINTER_TTL_SECONDS;
32
+ async function readScreenDoc(hostId, screenId, /** Compose-time reads that WANT the template document — see {@link getScreen}. */ allowTemplate = false) {
33
+ const snapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).collection('screens').withConverter(screenConverter).doc(screenId).get();
34
+ if (!snapshot.exists) return null;
35
+ const screen = snapshot.data();
36
+ // Serve-side agreement with the quota (AGL-1383). `countBillableScreens`
37
+ // subtracts soft-deleted and `kind: 'email'` screens from `screensPerHost`,
38
+ // and both fields are client-writable — so before this, one `updateDoc` on
39
+ // the screen's own document took it off the plan while leaving it live: the
40
+ // host's routing map still pointed at it and nothing on this path ever
41
+ // looked. Refusing them here is what makes the exclusion honest — an
42
+ // excluded screen genuinely is not a page — and it turns the bypass into a
43
+ // trade (the field costs you the page) rather than a discount.
44
+ //
45
+ // Indistinguishable from a missing screen on purpose: every caller already
46
+ // treats `null` as a 404, and this must not become a probe for which
47
+ // screens exist. Not cached either (`store` refuses `null`), so clearing
48
+ // the field brings the page back on the next request rather than in 60s.
49
+ //
50
+ // The Emails page is untouched: campaign sends read the email screen and
51
+ // its version straight off Firestore in `loadEmailTemplate`, never through
52
+ // the tenant runtime — email documents are not, and never were, URLs.
53
+ //
54
+ // `kind: 'template'` joins them (AGL-1400) and needs the escape hatch they do
55
+ // not, because a template IS rendered by this runtime — just never at an
56
+ // address of its own. `composeCollectionTemplatePage` and commerce's
57
+ // site-page resolver ask for it deliberately, against a routed entry or
58
+ // product; every other caller is resolving a REQUEST PATH, and for those a
59
+ // template is a 404 exactly as an email document is.
60
+ if (allowTemplate && (screen == null ? void 0 : screen.kind) === Aglyn.SCREEN_KIND_TEMPLATE) {
61
+ return screen.deletedAt == null ? screen : null;
62
+ }
63
+ // `kind: 'error'` (AGL-2092) is served, and served UNCONDITIONALLY — no
64
+ // opt-in flag like `allowTemplate` above, on purpose.
65
+ //
66
+ // The two exclusions this function refuses are refused because refusing is
67
+ // what makes them honest: they are client-writable, so `kind: 'email'` had to
68
+ // cost the page or it would have been a discount (AGL-1383). Neither half of
69
+ // that argument holds here. A client cannot write `kind` at all — the rules
70
+ // have frozen it since AGL-1383 and only /api/hosts/screens stamps this
71
+ // value, bounded at `ERROR_SCREEN_MAX_PER_HOST` — and, decisively, an error
72
+ // screen that is still in the host's routing map still COUNTS, because the
73
+ // map outranks the document for this value. So there is no page being had for
74
+ // free to refuse, and refusing would only mean that assigning a 404 screen
75
+ // silently broke it: the very error-render callers below fetch it through
76
+ // this function, and a host that ALSO still publishes it at `/404` (which is
77
+ // exactly how every error screen on the platform exists today) would find
78
+ // that address 404ing the moment somebody used the Error pages card.
79
+ //
80
+ // What keeps the exclusion honest instead is the routing map: the screen
81
+ // stops counting when, and only when, it stops having an address of its own.
82
+ if ((screen == null ? void 0 : screen.kind) === Aglyn.SCREEN_KIND_ERROR) {
83
+ return screen.deletedAt == null ? screen : null;
84
+ }
85
+ return Aglyn.screenClaimsToBeAPage(screen) ? screen : null;
86
+ }
87
+ export async function getScreen(options) {
88
+ const { screenId, hostId, allowTemplate = false } = options;
89
+ const data = {
90
+ screen: undefined,
91
+ nextPageToken: '',
92
+ error: null
93
+ };
94
+ try {
95
+ const screen = await withRenderCache({
96
+ key: [
97
+ allowTemplate ? 'tenant-screen-doc-template' : 'tenant-screen-doc',
98
+ hostId,
99
+ screenId
100
+ ],
101
+ revalidate: SCREEN_DOC_TTL_SECONDS,
102
+ tags: [
103
+ tenantDataTag(hostId)
104
+ ],
105
+ read: ()=>readScreenDoc(hostId, screenId, allowTemplate),
106
+ // Never store a missing screen, and never store a doc carrying a
107
+ // PENDING publish schedule: `applyDuePublishSchedule` reads
108
+ // `publishSchedule.publishAt.seconds` off a live Timestamp, and the
109
+ // JSON round trip a cache hit implies decays that to `_seconds` —
110
+ // which would make every pending schedule look already due. A pending
111
+ // schedule therefore keeps its doc read fresh until it resolves.
112
+ store: (value)=>{
113
+ var _value_publishSchedule;
114
+ return value !== null && ((_value_publishSchedule = value.publishSchedule) == null ? void 0 : _value_publishSchedule.status) !== 'pending';
115
+ }
116
+ });
117
+ if (screen) data.screen = screen;
118
+ } catch (error) {
119
+ console.error(error);
120
+ data.error = error;
121
+ }
122
+ return data;
123
+ }
124
+ export default getScreen;
125
+
126
+ //# sourceMappingURL=get-screen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/tenant/runtime/src/lib/get-screen.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2022 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 * as Aglyn from '@aglyn/aglyn/server'\nimport { firebaseAdmin, screenConverter } from '@aglyn/tenant-data-admin'\nimport {\n PUBLISH_POINTER_TTL_SECONDS,\n tenantDataTag,\n withRenderCache,\n} from '@aglyn/tenant-data-admin/render-cache'\n\n/**\n * Backstop TTL only (AGL-1302): a screen publish flips `versionId` on this\n * doc, and the publish path already busts `tenant-data:{hostId}` through the\n * tenant `/api/revalidate` route, so publishes stay as instant as AGL-1150\n * made them.\n *\n * This is the ONE read whose data a screen publish changes without changing\n * its cache key — the version body next door is keyed by `versionId` and\n * misses on its own — so when the announce does not arrive, this document is\n * the entire reason a published page keeps serving the old one. It is held to\n * the page's own ISR window rather than the hour every other published read\n * gets (AGL-2573); `PUBLISH_POINTER_TTL_SECONDS` carries that argument.\n */\nconst SCREEN_DOC_TTL_SECONDS = PUBLISH_POINTER_TTL_SECONDS\n\nasync function readScreenDoc(\n hostId: string,\n screenId: string,\n /** Compose-time reads that WANT the template document — see {@link getScreen}. */\n allowTemplate = false,\n): Promise<Aglyn.AglynScreen | null> {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('screens')\n .withConverter(screenConverter)\n .doc(screenId)\n .get()\n if (!snapshot.exists) return null\n const screen = snapshot.data() as Aglyn.AglynScreen\n // Serve-side agreement with the quota (AGL-1383). `countBillableScreens`\n // subtracts soft-deleted and `kind: 'email'` screens from `screensPerHost`,\n // and both fields are client-writable — so before this, one `updateDoc` on\n // the screen's own document took it off the plan while leaving it live: the\n // host's routing map still pointed at it and nothing on this path ever\n // looked. Refusing them here is what makes the exclusion honest — an\n // excluded screen genuinely is not a page — and it turns the bypass into a\n // trade (the field costs you the page) rather than a discount.\n //\n // Indistinguishable from a missing screen on purpose: every caller already\n // treats `null` as a 404, and this must not become a probe for which\n // screens exist. Not cached either (`store` refuses `null`), so clearing\n // the field brings the page back on the next request rather than in 60s.\n //\n // The Emails page is untouched: campaign sends read the email screen and\n // its version straight off Firestore in `loadEmailTemplate`, never through\n // the tenant runtime — email documents are not, and never were, URLs.\n //\n // `kind: 'template'` joins them (AGL-1400) and needs the escape hatch they do\n // not, because a template IS rendered by this runtime — just never at an\n // address of its own. `composeCollectionTemplatePage` and commerce's\n // site-page resolver ask for it deliberately, against a routed entry or\n // product; every other caller is resolving a REQUEST PATH, and for those a\n // template is a 404 exactly as an email document is.\n if (allowTemplate && screen?.kind === Aglyn.SCREEN_KIND_TEMPLATE) {\n return screen.deletedAt == null ? screen : null\n }\n // `kind: 'error'` (AGL-2092) is served, and served UNCONDITIONALLY — no\n // opt-in flag like `allowTemplate` above, on purpose.\n //\n // The two exclusions this function refuses are refused because refusing is\n // what makes them honest: they are client-writable, so `kind: 'email'` had to\n // cost the page or it would have been a discount (AGL-1383). Neither half of\n // that argument holds here. A client cannot write `kind` at all — the rules\n // have frozen it since AGL-1383 and only /api/hosts/screens stamps this\n // value, bounded at `ERROR_SCREEN_MAX_PER_HOST` — and, decisively, an error\n // screen that is still in the host's routing map still COUNTS, because the\n // map outranks the document for this value. So there is no page being had for\n // free to refuse, and refusing would only mean that assigning a 404 screen\n // silently broke it: the very error-render callers below fetch it through\n // this function, and a host that ALSO still publishes it at `/404` (which is\n // exactly how every error screen on the platform exists today) would find\n // that address 404ing the moment somebody used the Error pages card.\n //\n // What keeps the exclusion honest instead is the routing map: the screen\n // stops counting when, and only when, it stops having an address of its own.\n if (screen?.kind === Aglyn.SCREEN_KIND_ERROR) {\n return screen.deletedAt == null ? screen : null\n }\n return Aglyn.screenClaimsToBeAPage(screen as Aglyn.ScreenPageClaim)\n ? screen\n : null\n}\n\nexport async function getScreen(options: {\n screenId: Aglyn.ScreenUid\n hostId: Aglyn.HostUid\n /**\n * Serve a `kind: 'template'` document too (AGL-1400).\n *\n * Set ONLY by the two composers that render a template against a routed\n * subject — the collection entry page and commerce's PDP/catalog pages. A\n * caller resolving a request path must leave it off: that is what makes the\n * exclusion honest (an excluded screen genuinely is not a page) and it is the\n * same trade AGL-1383 made for `kind: 'email'`.\n *\n * It also keys the render cache, so a template fetched for composition can\n * never be handed back to a path-resolving caller out of the same slot.\n */\n allowTemplate?: boolean\n}) {\n const { screenId, hostId, allowTemplate = false } = options\n const data = {\n screen: undefined as Aglyn.AglynScreen,\n nextPageToken: '',\n error: null,\n }\n\n try {\n const screen = await withRenderCache({\n key: [\n allowTemplate ? 'tenant-screen-doc-template' : 'tenant-screen-doc',\n hostId as string,\n screenId as string,\n ],\n revalidate: SCREEN_DOC_TTL_SECONDS,\n tags: [tenantDataTag(hostId as string)],\n read: () =>\n readScreenDoc(hostId as string, screenId as string, allowTemplate),\n // Never store a missing screen, and never store a doc carrying a\n // PENDING publish schedule: `applyDuePublishSchedule` reads\n // `publishSchedule.publishAt.seconds` off a live Timestamp, and the\n // JSON round trip a cache hit implies decays that to `_seconds` —\n // which would make every pending schedule look already due. A pending\n // schedule therefore keeps its doc read fresh until it resolves.\n store: (value) =>\n value !== null &&\n (value as { publishSchedule?: { status?: string } }).publishSchedule\n ?.status !== 'pending',\n })\n if (screen) data.screen = screen\n } catch (error) {\n console.error(error)\n data.error = error\n }\n\n return data\n}\n\nexport default getScreen\n"],"names":["Aglyn","firebaseAdmin","screenConverter","PUBLISH_POINTER_TTL_SECONDS","tenantDataTag","withRenderCache","SCREEN_DOC_TTL_SECONDS","readScreenDoc","hostId","screenId","allowTemplate","snapshot","app","firestore","collection","doc","withConverter","get","exists","screen","data","kind","SCREEN_KIND_TEMPLATE","deletedAt","SCREEN_KIND_ERROR","screenClaimsToBeAPage","getScreen","options","undefined","nextPageToken","error","key","revalidate","tags","read","store","value","publishSchedule","status","console"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,sBAAqB;AAC5C,SAASC,aAAa,EAAEC,eAAe,QAAQ,2BAA0B;AACzE,SACEC,2BAA2B,EAC3BC,aAAa,EACbC,eAAe,QACV,wCAAuC;AAE9C;;;;;;;;;;;;CAYC,GACD,MAAMC,yBAAyBH;AAE/B,eAAeI,cACbC,MAAc,EACdC,QAAgB,EAChB,gFAAgF,GAChFC,gBAAgB,KAAK;IAErB,MAAMC,WAAW,MAAMV,cACpBW,GAAG,GACHC,SAAS,GACTC,UAAU,CAAC,SACXC,GAAG,CAACP,QACJM,UAAU,CAAC,WACXE,aAAa,CAACd,iBACda,GAAG,CAACN,UACJQ,GAAG;IACN,IAAI,CAACN,SAASO,MAAM,EAAE,OAAO;IAC7B,MAAMC,SAASR,SAASS,IAAI;IAC5B,yEAAyE;IACzE,4EAA4E;IAC5E,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,qEAAqE;IACrE,2EAA2E;IAC3E,+DAA+D;IAC/D,EAAE;IACF,2EAA2E;IAC3E,qEAAqE;IACrE,yEAAyE;IACzE,yEAAyE;IACzE,EAAE;IACF,yEAAyE;IACzE,2EAA2E;IAC3E,sEAAsE;IACtE,EAAE;IACF,8EAA8E;IAC9E,yEAAyE;IACzE,qEAAqE;IACrE,wEAAwE;IACxE,2EAA2E;IAC3E,qDAAqD;IACrD,IAAIV,iBAAiBS,CAAAA,0BAAAA,OAAQE,IAAI,MAAKrB,MAAMsB,oBAAoB,EAAE;QAChE,OAAOH,OAAOI,SAAS,IAAI,OAAOJ,SAAS;IAC7C;IACA,wEAAwE;IACxE,sDAAsD;IACtD,EAAE;IACF,2EAA2E;IAC3E,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,wEAAwE;IACxE,4EAA4E;IAC5E,2EAA2E;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E,0EAA0E;IAC1E,6EAA6E;IAC7E,0EAA0E;IAC1E,qEAAqE;IACrE,EAAE;IACF,yEAAyE;IACzE,6EAA6E;IAC7E,IAAIA,CAAAA,0BAAAA,OAAQE,IAAI,MAAKrB,MAAMwB,iBAAiB,EAAE;QAC5C,OAAOL,OAAOI,SAAS,IAAI,OAAOJ,SAAS;IAC7C;IACA,OAAOnB,MAAMyB,qBAAqB,CAACN,UAC/BA,SACA;AACN;AAEA,OAAO,eAAeO,UAAUC,OAgB/B;IACC,MAAM,EAAElB,QAAQ,EAAED,MAAM,EAAEE,gBAAgB,KAAK,EAAE,GAAGiB;IACpD,MAAMP,OAAO;QACXD,QAAQS;QACRC,eAAe;QACfC,OAAO;IACT;IAEA,IAAI;QACF,MAAMX,SAAS,MAAMd,gBAAgB;YACnC0B,KAAK;gBACHrB,gBAAgB,+BAA+B;gBAC/CF;gBACAC;aACD;YACDuB,YAAY1B;YACZ2B,MAAM;gBAAC7B,cAAcI;aAAkB;YACvC0B,MAAM,IACJ3B,cAAcC,QAAkBC,UAAoBC;YACtD,iEAAiE;YACjE,4DAA4D;YAC5D,oEAAoE;YACpE,kEAAkE;YAClE,sEAAsE;YACtE,iEAAiE;YACjEyB,OAAO,CAACC;oBAEN;uBADAA,UAAU,QACV,EAAA,yBAAA,AAACA,MAAoDC,eAAe,qBAApE,uBACIC,MAAM,MAAK;;QACnB;QACA,IAAInB,QAAQC,KAAKD,MAAM,GAAGA;IAC5B,EAAE,OAAOW,OAAO;QACdS,QAAQT,KAAK,CAACA;QACdV,KAAKU,KAAK,GAAGA;IACf;IAEA,OAAOV;AACT;AAEA,eAAeM,UAAS"}
@@ -0,0 +1,41 @@
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 * as Aglyn from '@aglyn/aglyn/server';
18
+ /**
19
+ * Fetches the host's variables keyed by doc id (AGL-185/194) for
20
+ * `{{var:id}}` tokens; expression scopes read names off the values.
21
+ * Fail-open: on error an empty map is returned and tokens render
22
+ * literally/empty.
23
+ */
24
+ export declare function getVariables(options: {
25
+ hostId: string;
26
+ }): Promise<Record<string, Aglyn.HostVariable>>;
27
+ /**
28
+ * Fetches the host's function definitions keyed by name for
29
+ * `{{fn:name(...)}}` bindings (AGL-93). Fail-open like variables.
30
+ */
31
+ export declare function getFunctions(options: {
32
+ hostId: string;
33
+ }): Promise<Record<string, Aglyn.HostFunction>>;
34
+ export default getVariables;
35
+ /**
36
+ * Fetches the host's workflows keyed by name for computed variables and
37
+ * function→workflow calls (AGL-129). Fail-open like variables.
38
+ */
39
+ export declare function getWorkflows(options: {
40
+ hostId: string;
41
+ }): Promise<Record<string, Aglyn.HostWorkflow>>;
@@ -0,0 +1,128 @@
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 { _ as _extends } from "@swc/helpers/_/_extends";
17
+ import { firebaseAdmin } from "@aglyn/tenant-data-admin";
18
+ import { PUBLISHED_SITE_DATA_TTL_SECONDS, tenantDataTag, withRenderCache } from "@aglyn/tenant-data-admin/render-cache";
19
+ /**
20
+ * Part of the per-host compose bundle every page render pays (AGL-1302):
21
+ * variables, functions and workflows were three ~100-doc queries per
22
+ * render. All three fail open, so a cache-layer failure degrades to the
23
+ * uncached read rather than an empty map. 60s matches the page's own ISR
24
+ * window; the publish path busts `tenant-data:{hostId}` on top.
25
+ */ const HOST_BINDINGS_TTL_SECONDS = PUBLISHED_SITE_DATA_TTL_SECONDS;
26
+ const cachedBindingsRead = async (name, hostId, read)=>{
27
+ try {
28
+ return await withRenderCache({
29
+ key: [
30
+ name,
31
+ hostId
32
+ ],
33
+ revalidate: HOST_BINDINGS_TTL_SECONDS,
34
+ tags: [
35
+ tenantDataTag(hostId)
36
+ ],
37
+ read
38
+ });
39
+ } catch (error) {
40
+ console.error(error);
41
+ return read();
42
+ }
43
+ };
44
+ /**
45
+ * Fetches the host's variables keyed by doc id (AGL-185/194) for
46
+ * `{{var:id}}` tokens; expression scopes read names off the values.
47
+ * Fail-open: on error an empty map is returned and tokens render
48
+ * literally/empty.
49
+ */ export async function getVariables(options) {
50
+ return cachedBindingsRead('tenant-variables', options.hostId, ()=>readVariables(options));
51
+ }
52
+ async function readVariables(options) {
53
+ const variables = {};
54
+ try {
55
+ const snapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(options.hostId).collection('variables').limit(100).get();
56
+ for (const docSnapshot of snapshot.docs){
57
+ const data = docSnapshot.data();
58
+ if ((data == null ? void 0 : data.name) && !data.deletedAt) {
59
+ variables[docSnapshot.id] = data;
60
+ }
61
+ }
62
+ } catch (error) {
63
+ console.error(error);
64
+ }
65
+ return variables;
66
+ }
67
+ /**
68
+ * Fetches the host's function definitions keyed by name for
69
+ * `{{fn:name(...)}}` bindings (AGL-93). Fail-open like variables.
70
+ */ export async function getFunctions(options) {
71
+ return cachedBindingsRead('tenant-functions', options.hostId, ()=>readFunctions(options));
72
+ }
73
+ async function readFunctions(options) {
74
+ const functions = {};
75
+ try {
76
+ const snapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(options.hostId).collection('functions').limit(100).get();
77
+ for (const docSnapshot of snapshot.docs){
78
+ const data = docSnapshot.data();
79
+ if ((data == null ? void 0 : data.name) && !data.deletedAt) {
80
+ var _data_parameters, _data_variables, _data_operations;
81
+ // Plain evaluator fields only: doc timestamps aren't
82
+ // JSON-serializable and definitions embed into page props.
83
+ const definition = _extends({
84
+ name: data.name,
85
+ parameters: (_data_parameters = data.parameters) != null ? _data_parameters : [],
86
+ variables: (_data_variables = data.variables) != null ? _data_variables : [],
87
+ operations: (_data_operations = data.operations) != null ? _data_operations : []
88
+ }, data.returnValue && {
89
+ returnValue: data.returnValue
90
+ });
91
+ // Keyed by doc id ({{fn:id(...)}}, AGL-185/194) AND name so
92
+ // legacy workflow steps that stored functionName keep resolving
93
+ // (AGL-261).
94
+ functions[docSnapshot.id] = definition;
95
+ if (!functions[data.name]) functions[data.name] = definition;
96
+ }
97
+ }
98
+ } catch (error) {
99
+ console.error(error);
100
+ }
101
+ return functions;
102
+ }
103
+ export default getVariables;
104
+ /**
105
+ * Fetches the host's workflows keyed by name for computed variables and
106
+ * function→workflow calls (AGL-129). Fail-open like variables.
107
+ */ export async function getWorkflows(options) {
108
+ return cachedBindingsRead('tenant-workflows', options.hostId, ()=>readWorkflows(options));
109
+ }
110
+ async function readWorkflows(options) {
111
+ const workflows = {};
112
+ try {
113
+ const snapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(options.hostId).collection('workflows').limit(100).get();
114
+ for (const docSnapshot of snapshot.docs){
115
+ const data = docSnapshot.data();
116
+ if (data.deletedAt) continue;
117
+ // Double-keyed by doc id AND name (AGL-261): id references are
118
+ // rename-safe; legacy name references keep resolving.
119
+ workflows[docSnapshot.id] = data;
120
+ if (data == null ? void 0 : data.name) workflows[data.name] = data;
121
+ }
122
+ } catch (error) {
123
+ console.error(error);
124
+ }
125
+ return workflows;
126
+ }
127
+
128
+ //# sourceMappingURL=get-variables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/tenant/runtime/src/lib/get-variables.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 type * as Aglyn from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport {\n PUBLISHED_SITE_DATA_TTL_SECONDS,\n tenantDataTag,\n withRenderCache,\n} from '@aglyn/tenant-data-admin/render-cache'\n\n/**\n * Part of the per-host compose bundle every page render pays (AGL-1302):\n * variables, functions and workflows were three ~100-doc queries per\n * render. All three fail open, so a cache-layer failure degrades to the\n * uncached read rather than an empty map. 60s matches the page's own ISR\n * window; the publish path busts `tenant-data:{hostId}` on top.\n */\nconst HOST_BINDINGS_TTL_SECONDS = PUBLISHED_SITE_DATA_TTL_SECONDS\n\nconst cachedBindingsRead = async <T>(\n name: string,\n hostId: string,\n read: () => Promise<Record<string, T>>,\n): Promise<Record<string, T>> => {\n try {\n return await withRenderCache({\n key: [name, hostId],\n revalidate: HOST_BINDINGS_TTL_SECONDS,\n tags: [tenantDataTag(hostId)],\n read,\n })\n } catch (error) {\n console.error(error)\n return read()\n }\n}\n\n/**\n * Fetches the host's variables keyed by doc id (AGL-185/194) for\n * `{{var:id}}` tokens; expression scopes read names off the values.\n * Fail-open: on error an empty map is returned and tokens render\n * literally/empty.\n */\nexport async function getVariables(options: {\n hostId: string\n}): Promise<Record<string, Aglyn.HostVariable>> {\n return cachedBindingsRead('tenant-variables', options.hostId, () =>\n readVariables(options),\n )\n}\n\nasync function readVariables(options: {\n hostId: string\n}): Promise<Record<string, Aglyn.HostVariable>> {\n const variables: Record<string, Aglyn.HostVariable> = {}\n try {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(options.hostId)\n .collection('variables')\n .limit(100)\n .get()\n for (const docSnapshot of snapshot.docs) {\n const data = docSnapshot.data() as Aglyn.HostVariable\n if (data?.name && !((data as any).deletedAt)) {\n variables[docSnapshot.id] = data\n }\n }\n } catch (error) {\n console.error(error)\n }\n return variables\n}\n\n/**\n * Fetches the host's function definitions keyed by name for\n * `{{fn:name(...)}}` bindings (AGL-93). Fail-open like variables.\n */\nexport async function getFunctions(options: {\n hostId: string\n}): Promise<Record<string, Aglyn.HostFunction>> {\n return cachedBindingsRead('tenant-functions', options.hostId, () =>\n readFunctions(options),\n )\n}\n\nasync function readFunctions(options: {\n hostId: string\n}): Promise<Record<string, Aglyn.HostFunction>> {\n const functions: Record<string, Aglyn.HostFunction> = {}\n try {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(options.hostId)\n .collection('functions')\n .limit(100)\n .get()\n for (const docSnapshot of snapshot.docs) {\n const data = docSnapshot.data() as Aglyn.HostFunction\n if (data?.name && !(data as any).deletedAt) {\n // Plain evaluator fields only: doc timestamps aren't\n // JSON-serializable and definitions embed into page props.\n const definition = {\n name: data.name,\n parameters: data.parameters ?? [],\n variables: data.variables ?? [],\n operations: data.operations ?? [],\n ...(data.returnValue && { returnValue: data.returnValue }),\n }\n // Keyed by doc id ({{fn:id(...)}}, AGL-185/194) AND name so\n // legacy workflow steps that stored functionName keep resolving\n // (AGL-261).\n functions[docSnapshot.id] = definition\n if (!functions[data.name]) functions[data.name] = definition\n }\n }\n } catch (error) {\n console.error(error)\n }\n return functions\n}\n\nexport default getVariables\n\n/**\n * Fetches the host's workflows keyed by name for computed variables and\n * function→workflow calls (AGL-129). Fail-open like variables.\n */\nexport async function getWorkflows(options: {\n hostId: string\n}): Promise<Record<string, Aglyn.HostWorkflow>> {\n return cachedBindingsRead('tenant-workflows', options.hostId, () =>\n readWorkflows(options),\n )\n}\n\nasync function readWorkflows(options: {\n hostId: string\n}): Promise<Record<string, Aglyn.HostWorkflow>> {\n const workflows: Record<string, Aglyn.HostWorkflow> = {}\n try {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(options.hostId)\n .collection('workflows')\n .limit(100)\n .get()\n for (const docSnapshot of snapshot.docs) {\n const data = docSnapshot.data() as Aglyn.HostWorkflow\n if ((data as any).deletedAt) continue\n // Double-keyed by doc id AND name (AGL-261): id references are\n // rename-safe; legacy name references keep resolving.\n workflows[docSnapshot.id] = data\n if (data?.name) workflows[data.name] = data\n }\n } catch (error) {\n console.error(error)\n }\n return workflows\n}\n"],"names":["firebaseAdmin","PUBLISHED_SITE_DATA_TTL_SECONDS","tenantDataTag","withRenderCache","HOST_BINDINGS_TTL_SECONDS","cachedBindingsRead","name","hostId","read","key","revalidate","tags","error","console","getVariables","options","readVariables","variables","snapshot","app","firestore","collection","doc","limit","get","docSnapshot","docs","data","deletedAt","id","getFunctions","readFunctions","functions","definition","parameters","operations","returnValue","getWorkflows","readWorkflows","workflows"],"mappings":"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"}
@@ -0,0 +1,22 @@
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 film name for `get-media-asset-facts.ts`, which reads every placed image
19
+ * and film in one projected batch. A film's entry is the same answer under
20
+ * either name, so a caller that only lays films can keep importing this one.
21
+ */
22
+ export { getMediaAssetFacts as getVideoAssetFacts, MEDIA_ASSET_FACTS_PER_RENDER as VIDEO_ASSET_FACTS_PER_RENDER, getMediaAssetFacts as default, } from './get-media-asset-facts';
@@ -0,0 +1,22 @@
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
+ * The film name for `get-media-asset-facts.ts`, which reads every placed image
18
+ * and film in one projected batch. A film's entry is the same answer under
19
+ * either name, so a caller that only lays films can keep importing this one.
20
+ */ export { getMediaAssetFacts as getVideoAssetFacts, MEDIA_ASSET_FACTS_PER_RENDER as VIDEO_ASSET_FACTS_PER_RENDER, getMediaAssetFacts as default } from "./get-media-asset-facts.js";
21
+
22
+ //# sourceMappingURL=get-video-asset-facts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/tenant/runtime/src/lib/get-video-asset-facts.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 * The film name for `get-media-asset-facts.ts`, which reads every placed image\n * and film in one projected batch. A film's entry is the same answer under\n * either name, so a caller that only lays films can keep importing this one.\n */\nexport {\n getMediaAssetFacts as getVideoAssetFacts,\n MEDIA_ASSET_FACTS_PER_RENDER as VIDEO_ASSET_FACTS_PER_RENDER,\n getMediaAssetFacts as default,\n} from './get-media-asset-facts'\n"],"names":["getMediaAssetFacts","getVideoAssetFacts","MEDIA_ASSET_FACTS_PER_RENDER","VIDEO_ASSET_FACTS_PER_RENDER","default"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;CAIC,GACD,SACEA,sBAAsBC,kBAAkB,EACxCC,gCAAgCC,4BAA4B,EAC5DH,sBAAsBI,OAAO,QACxB,6BAAyB"}
@@ -0,0 +1,83 @@
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 { HostActionAlert } from '@aglyn/aglyn/server';
18
+ /**
19
+ * WHO HEARS A HOST EVENT.
20
+ *
21
+ * A host event — a form submitted, a member signed up, a deal won — is raised
22
+ * by the server door that performed the write, through `emitHostEvent`. What
23
+ * runs because of it is not the runtime's business: an automation engine, an
24
+ * integration, a marketplace plugin. Each registers a listener here and the
25
+ * runtime fans every event out to all of them, knowing none by name.
26
+ *
27
+ * ## Registered by a call, at boot
28
+ *
29
+ * A listener is registered by a function the platform calls BY NAME — a
30
+ * plugin's `serverDeclarations` entry, which both apps run once per process
31
+ * from `instrumentation.ts`, and its API register functions, which the plugin
32
+ * loader runs. Never by a bare `import './x'`: a bundler drops a module its
33
+ * package does not list as a side effect, and jest does not, so a registration
34
+ * that relies on one passes every spec and runs in no build (AGL-3025).
35
+ *
36
+ * Boot registration is what lets a core route that never loads a plugin — the
37
+ * form submission, the page-view collector — still reach every listener.
38
+ *
39
+ * ## Isolated, and never thrown into the emitter
40
+ *
41
+ * The event has already happened: the submission is stored, the member exists.
42
+ * A listener that throws is logged against its plugin and the others still
43
+ * run, and the emitting request never sees the failure.
44
+ */
45
+ /** What an event carries into everything that listens for it. */
46
+ export type HostEventPayload = Record<string, string | number | boolean>;
47
+ export interface HostEventListener {
48
+ /**
49
+ * Runs whatever this plugin does when `event` happens on the site.
50
+ *
51
+ * Resolves the site alerts a request/response emitter shows its visitor —
52
+ * a form submission returns them in its response — or nothing.
53
+ */
54
+ onEvent(hostId: string, event: string, payload: HostEventPayload): Promise<readonly HostActionAlert[] | void>;
55
+ /**
56
+ * Runs ONE automation, by id, that a published page fired for a site event
57
+ * it evaluated itself — a scroll depth, a click, exit intent. The page has
58
+ * already decided the trigger matched, so the listener runs that one
59
+ * automation rather than every automation on the event; a listener that
60
+ * does not own the id resolves nothing.
61
+ */
62
+ onDispatch?(hostId: string, automationId: string, event: string, payload: HostEventPayload): Promise<readonly HostActionAlert[] | void>;
63
+ }
64
+ /**
65
+ * Subscribes a plugin to every host event. Registering again under the same
66
+ * plugin id replaces the earlier listener in place, so a register function
67
+ * the platform calls twice — boot, then the API surface — leaves one.
68
+ */
69
+ export declare function registerHostEventListener(pluginId: string, listener: HostEventListener): void;
70
+ /** The plugins listening, in registration order, for diagnostics. */
71
+ export declare function listHostEventListeners(): string[];
72
+ /** Test seam: forget every listener. */
73
+ export declare function resetHostEventListenersForTests(): void;
74
+ /**
75
+ * Hands one event to every listener, concurrently, and gathers the alerts
76
+ * they produced. Never rejects.
77
+ */
78
+ export declare function runHostEventListeners(hostId: string, event: string, payload?: HostEventPayload): Promise<HostActionAlert[]>;
79
+ /**
80
+ * Hands one page-dispatched automation to the listeners that dispatch, and
81
+ * gathers the alerts it produced. Never rejects.
82
+ */
83
+ export declare function dispatchHostAutomation(hostId: string, automationId: string, event: string, payload?: HostEventPayload): Promise<HostActionAlert[]>;