@aglyn/aglyn 1.0.0-beta.270 → 1.0.0-beta.272

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 (90) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/account-preferences.d.ts +50 -0
  3. package/src/lib/app-utils/account-preferences.js +96 -0
  4. package/src/lib/app-utils/account-preferences.js.map +1 -1
  5. package/src/lib/app-utils/activity-labels.js +2 -0
  6. package/src/lib/app-utils/activity-labels.js.map +1 -1
  7. package/src/lib/app-utils/analytics-events.d.ts +16 -2
  8. package/src/lib/app-utils/analytics-events.js +2 -0
  9. package/src/lib/app-utils/analytics-events.js.map +1 -1
  10. package/src/lib/app-utils/append-only-history.d.ts +94 -0
  11. package/src/lib/app-utils/append-only-history.js +73 -0
  12. package/src/lib/app-utils/append-only-history.js.map +1 -0
  13. package/src/lib/app-utils/bundle-intent.d.ts +186 -0
  14. package/src/lib/app-utils/bundle-intent.js +386 -0
  15. package/src/lib/app-utils/bundle-intent.js.map +1 -0
  16. package/src/lib/app-utils/client-build.d.ts +207 -0
  17. package/src/lib/app-utils/client-build.js +237 -0
  18. package/src/lib/app-utils/client-build.js.map +1 -0
  19. package/src/lib/app-utils/console-routes.d.ts +4 -0
  20. package/src/lib/app-utils/console-routes.js +2 -0
  21. package/src/lib/app-utils/console-routes.js.map +1 -1
  22. package/src/lib/app-utils/docs-help-sections.generated.js +13 -6
  23. package/src/lib/app-utils/docs-help-sections.generated.js.map +1 -1
  24. package/src/lib/app-utils/docs-help.generated.d.ts +11 -5
  25. package/src/lib/app-utils/docs-help.generated.js +31 -2
  26. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  27. package/src/lib/app-utils/docs-index.generated.js +105 -35
  28. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  29. package/src/lib/app-utils/margin-utilization.js +6 -4
  30. package/src/lib/app-utils/margin-utilization.js.map +1 -1
  31. package/src/lib/app-utils/media-filter.d.ts +26 -0
  32. package/src/lib/app-utils/media-filter.js +32 -0
  33. package/src/lib/app-utils/media-filter.js.map +1 -1
  34. package/src/lib/app-utils/onboarding-deep-link.d.ts +36 -2
  35. package/src/lib/app-utils/onboarding-deep-link.js +48 -5
  36. package/src/lib/app-utils/onboarding-deep-link.js.map +1 -1
  37. package/src/lib/app-utils/operator-alerts.js +21 -0
  38. package/src/lib/app-utils/operator-alerts.js.map +1 -1
  39. package/src/lib/app-utils/owner-approval-record.d.ts +102 -0
  40. package/src/lib/app-utils/owner-approval-record.js +148 -0
  41. package/src/lib/app-utils/owner-approval-record.js.map +1 -0
  42. package/src/lib/app-utils/plan-card-meters.d.ts +96 -0
  43. package/src/lib/app-utils/plan-card-meters.js +197 -0
  44. package/src/lib/app-utils/plan-card-meters.js.map +1 -0
  45. package/src/lib/app-utils/plan-entitlements.d.ts +183 -67
  46. package/src/lib/app-utils/plan-entitlements.js +213 -58
  47. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  48. package/src/lib/app-utils/release-flags.d.ts +1 -1
  49. package/src/lib/app-utils/release-flags.js +22 -0
  50. package/src/lib/app-utils/release-flags.js.map +1 -1
  51. package/src/lib/app-utils/server.d.ts +4 -0
  52. package/src/lib/app-utils/server.js +7 -0
  53. package/src/lib/app-utils/server.js.map +1 -1
  54. package/src/lib/app-utils/site-care-project.d.ts +227 -0
  55. package/src/lib/app-utils/site-care-project.js +423 -0
  56. package/src/lib/app-utils/site-care-project.js.map +1 -0
  57. package/src/lib/app-utils/site-care.d.ts +291 -0
  58. package/src/lib/app-utils/site-care.js +310 -0
  59. package/src/lib/app-utils/site-care.js.map +1 -0
  60. package/src/lib/app-utils/upgrade-proposal.d.ts +29 -4
  61. package/src/lib/app-utils/upgrade-proposal.js +60 -9
  62. package/src/lib/app-utils/upgrade-proposal.js.map +1 -1
  63. package/src/lib/foundation/definitions/org-billing.types.d.ts +40 -10
  64. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  65. package/src/lib/foundation/definitions/organization.types.d.ts +3 -1
  66. package/src/lib/foundation/definitions/organization.types.js.map +1 -1
  67. package/src/lib/foundation/definitions/platform.types.d.ts +20 -0
  68. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  69. package/src/lib/foundation/index.d.ts +1 -1
  70. package/src/lib/foundation/index.js.map +1 -1
  71. package/src/lib/plugin-manager/feature-plugins.d.ts +48 -0
  72. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  73. package/src/lib/plugin-manager/first-party-plugins.generated.js +16 -0
  74. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  75. package/src/lib/plugin-manager/plugin-ai-capabilities.d.ts +19 -0
  76. package/src/lib/plugin-manager/plugin-ai-capabilities.js.map +1 -1
  77. package/src/lib/plugin-manager/plugin-domain-search.d.ts +81 -0
  78. package/src/lib/plugin-manager/plugin-domain-search.js +63 -0
  79. package/src/lib/plugin-manager/plugin-domain-search.js.map +1 -0
  80. package/src/lib/plugin-manager/plugin-entitlements.d.ts +23 -5
  81. package/src/lib/plugin-manager/plugin-entitlements.js +10 -0
  82. package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -1
  83. package/src/lib/plugin-manager/plugin-media-ingest.d.ts +18 -1
  84. package/src/lib/plugin-manager/plugin-media-ingest.js.map +1 -1
  85. package/src/lib/plugin-manager/plugin-site-resources.d.ts +74 -0
  86. package/src/lib/plugin-manager/plugin-site-resources.js +135 -0
  87. package/src/lib/plugin-manager/plugin-site-resources.js.map +1 -0
  88. package/src/lib/plugin-manager/purchase-gate-preclearance.d.ts +61 -0
  89. package/src/lib/plugin-manager/purchase-gate-preclearance.js +102 -0
  90. package/src/lib/plugin-manager/purchase-gate-preclearance.js.map +1 -0
@@ -0,0 +1,135 @@
1
+ var _globalScope_ERASERS_KEY;
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
+ */ /**
18
+ * A PLUGIN'S SHARE OF DELETING A SITE (AGL-4003).
19
+ *
20
+ * Deleting a site (`eraseHost`) destroys what the platform holds for it: its
21
+ * document tree, its Storage, its routing, and the organization documents a
22
+ * plugin declares as the site's (`orgCollections` with a `siteField`). Some
23
+ * plugin resources belong to the ORGANIZATION and are shared with sites by a
24
+ * scope list instead — a dataset shared with one site, or with several. Which
25
+ * of those are the site's own, and what becomes of the rest, is the plugin's
26
+ * to say, so the plugin registers a site-resource eraser here:
27
+ *
28
+ * - `preview` lists what deleting the site would take — the resources shared
29
+ * with this site alone, each with how many items it holds — and how many it
30
+ * shares with other sites, which stay. The Delete site card shows it before
31
+ * anyone confirms, so it must change nothing.
32
+ * - `erase` erases the resources shared with this site alone, items and all,
33
+ * and takes the site off the sharing of the ones it shares with others. It
34
+ * answers how many it erased.
35
+ *
36
+ * Erasers run in registration order and are ISOLATED, like every trailing
37
+ * cleanup of a site delete: a throw is logged against its plugin and does not
38
+ * stop the next one or the delete. A preview that throws is reported as
39
+ * `null` for its plugin, so the card can say the list is incomplete rather
40
+ * than show an empty one.
41
+ *
42
+ * Registered from a plugin's `serverDeclarations`, so the eraser is in place
43
+ * in every server process that can delete a site.
44
+ */ import { getRegisteringPluginId } from "../app-utils/registering-plugin.js";
45
+ import { runPluginDeclarationsRepair } from "./plugin-declarations-repair.js";
46
+ /**
47
+ * One list per process, on `globalThis` (AGL-3464): the app registers its
48
+ * plugins' declarations from `instrumentation.ts`, which Next compiles apart
49
+ * from the route that deletes a site, and a module-scoped list is filled in
50
+ * one copy and read empty in the other.
51
+ */ const ERASERS_KEY = Symbol.for('@aglyn/aglyn:plugin-site-resource-erasers');
52
+ const globalScope = globalThis;
53
+ const registrations = (_globalScope_ERASERS_KEY = globalScope[ERASERS_KEY]) != null ? _globalScope_ERASERS_KEY : globalScope[ERASERS_KEY] = [];
54
+ /**
55
+ * Registers a plugin's site-resource eraser. Owner = the loader's marker
56
+ * inside a register fn, else `options.pluginId`; an eraser with neither
57
+ * throws. One per plugin: registering again replaces the plugin's earlier
58
+ * eraser in place, so a module evaluated twice does not erase twice.
59
+ */ export function registerPluginSiteResourceEraser(eraser, options) {
60
+ var _ref, _getRegisteringPluginId;
61
+ const pluginId = ((_ref = (_getRegisteringPluginId = getRegisteringPluginId()) != null ? _getRegisteringPluginId : options == null ? void 0 : options.pluginId) != null ? _ref : '').trim();
62
+ if (!pluginId) {
63
+ throw new Error('a plugin site-resource eraser was registered with no owner: pass ' + '{ pluginId } when registering outside a plugin register fn');
64
+ }
65
+ if (typeof (eraser == null ? void 0 : eraser.preview) !== 'function' || typeof (eraser == null ? void 0 : eraser.erase) !== 'function') {
66
+ throw new Error('a site-resource eraser needs a preview and an erase function');
67
+ }
68
+ const index = registrations.findIndex((entry)=>entry.pluginId === pluginId);
69
+ if (index >= 0) registrations[index] = {
70
+ pluginId,
71
+ eraser
72
+ };
73
+ else registrations.push({
74
+ pluginId,
75
+ eraser
76
+ });
77
+ }
78
+ /**
79
+ * The erasers in place, after running the app's boot step once when none is:
80
+ * a boot whose declarations failed must not read as a workspace with nothing
81
+ * shared with the site.
82
+ */ async function registered() {
83
+ if (!registrations.length) {
84
+ await runPluginDeclarationsRepair().catch((error)=>{
85
+ console.error('[plugins] the declarations repair failed before a site delete', error);
86
+ });
87
+ }
88
+ return [
89
+ ...registrations
90
+ ];
91
+ }
92
+ /**
93
+ * Every plugin's preview, in registration order. Never throws: a preview that
94
+ * fails is logged and answered as `null` for its plugin.
95
+ */ export async function previewPluginSiteResources(request) {
96
+ const rows = [];
97
+ for (const { pluginId, eraser } of (await registered())){
98
+ try {
99
+ rows.push({
100
+ pluginId,
101
+ preview: await eraser.preview(request)
102
+ });
103
+ } catch (error) {
104
+ console.error(`[plugins] ${pluginId} could not list what goes with site ${request.hostId}`, error);
105
+ rows.push({
106
+ pluginId,
107
+ preview: null
108
+ });
109
+ }
110
+ }
111
+ return rows;
112
+ }
113
+ /**
114
+ * Runs every eraser, in registration order, and answers how many resources
115
+ * each erased by plugin id, or `null` for one that threw. Never throws.
116
+ */ export async function erasePluginSiteResources(request) {
117
+ const erased = {};
118
+ for (const { pluginId, eraser } of (await registered())){
119
+ try {
120
+ erased[pluginId] = await eraser.erase(request);
121
+ } catch (error) {
122
+ erased[pluginId] = null;
123
+ console.error(`[plugins] ${pluginId} failed to erase site ${request.hostId}'s resources`, error);
124
+ }
125
+ }
126
+ return erased;
127
+ }
128
+ /** The plugins with a site-resource eraser, in the order they run. */ export function listPluginSiteResourceErasers() {
129
+ return registrations.map((entry)=>entry.pluginId);
130
+ }
131
+ /** Test seam: forget every eraser. */ export function resetPluginSiteResourceErasersForTests() {
132
+ registrations.length = 0;
133
+ }
134
+
135
+ //# sourceMappingURL=plugin-site-resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-site-resources.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 * A PLUGIN'S SHARE OF DELETING A SITE (AGL-4003).\n *\n * Deleting a site (`eraseHost`) destroys what the platform holds for it: its\n * document tree, its Storage, its routing, and the organization documents a\n * plugin declares as the site's (`orgCollections` with a `siteField`). Some\n * plugin resources belong to the ORGANIZATION and are shared with sites by a\n * scope list instead — a dataset shared with one site, or with several. Which\n * of those are the site's own, and what becomes of the rest, is the plugin's\n * to say, so the plugin registers a site-resource eraser here:\n *\n * - `preview` lists what deleting the site would take — the resources shared\n * with this site alone, each with how many items it holds — and how many it\n * shares with other sites, which stay. The Delete site card shows it before\n * anyone confirms, so it must change nothing.\n * - `erase` erases the resources shared with this site alone, items and all,\n * and takes the site off the sharing of the ones it shares with others. It\n * answers how many it erased.\n *\n * Erasers run in registration order and are ISOLATED, like every trailing\n * cleanup of a site delete: a throw is logged against its plugin and does not\n * stop the next one or the delete. A preview that throws is reported as\n * `null` for its plugin, so the card can say the list is incomplete rather\n * than show an empty one.\n *\n * Registered from a plugin's `serverDeclarations`, so the eraser is in place\n * in every server process that can delete a site.\n */\n\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport { runPluginDeclarationsRepair } from './plugin-declarations-repair'\n\n/** The site a preview or an erase is about, and the workspace that owns it. */\nexport interface PluginSiteResourceRequest {\n orgId: string\n hostId: string\n}\n\n/** One resource that goes with the site. */\nexport interface PluginSiteResourceGoing {\n id: string\n /** Its name, as the plugin's own page shows it. */\n name: string\n /** How many items it holds; `null` where they could not be counted. */\n items: number | null\n}\n\n/** What deleting the site does to one plugin's resources. */\nexport interface PluginSiteResourcePreview {\n /** What one resource is called, and several: `['dataset', 'datasets']`. */\n noun: readonly [string, string]\n /** What one of its items is called, and several: `['record', 'records']`. */\n itemNoun: readonly [string, string]\n /** Shared with this site alone: erased with every item. */\n going: PluginSiteResourceGoing[]\n /** Shared with this site and others: they stay, and lose this site. */\n staying: number\n}\n\nexport interface PluginSiteResourceEraser {\n /** What deleting the site would take. Reads only. */\n preview(request: PluginSiteResourceRequest): Promise<PluginSiteResourcePreview>\n /** Erases the site's own resources; answers how many it erased. */\n erase(request: PluginSiteResourceRequest): Promise<number>\n}\n\ninterface Registration {\n pluginId: string\n eraser: PluginSiteResourceEraser\n}\n\n/**\n * One list per process, on `globalThis` (AGL-3464): the app registers its\n * plugins' declarations from `instrumentation.ts`, which Next compiles apart\n * from the route that deletes a site, and a module-scoped list is filled in\n * one copy and read empty in the other.\n */\nconst ERASERS_KEY = Symbol.for('@aglyn/aglyn:plugin-site-resource-erasers')\n\nconst globalScope = globalThis as typeof globalThis & {\n [ERASERS_KEY]?: Registration[]\n}\n\nconst registrations: Registration[] =\n globalScope[ERASERS_KEY] ?? (globalScope[ERASERS_KEY] = [])\n\n/**\n * Registers a plugin's site-resource eraser. Owner = the loader's marker\n * inside a register fn, else `options.pluginId`; an eraser with neither\n * throws. One per plugin: registering again replaces the plugin's earlier\n * eraser in place, so a module evaluated twice does not erase twice.\n */\nexport function registerPluginSiteResourceEraser(\n eraser: PluginSiteResourceEraser,\n options?: { pluginId?: string },\n): void {\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n if (!pluginId) {\n throw new Error(\n 'a plugin site-resource eraser was registered with no owner: pass ' +\n '{ pluginId } when registering outside a plugin register fn',\n )\n }\n if (typeof eraser?.preview !== 'function' || typeof eraser?.erase !== 'function') {\n throw new Error('a site-resource eraser needs a preview and an erase function')\n }\n const index = registrations.findIndex((entry) => entry.pluginId === pluginId)\n if (index >= 0) registrations[index] = { pluginId, eraser }\n else registrations.push({ pluginId, eraser })\n}\n\n/**\n * The erasers in place, after running the app's boot step once when none is:\n * a boot whose declarations failed must not read as a workspace with nothing\n * shared with the site.\n */\nasync function registered(): Promise<Registration[]> {\n if (!registrations.length) {\n await runPluginDeclarationsRepair().catch((error: unknown) => {\n console.error('[plugins] the declarations repair failed before a site delete', error)\n })\n }\n return [...registrations]\n}\n\n/** One plugin's preview, or `null` where its preview failed. */\nexport interface PluginSiteResourcePreviewRow {\n pluginId: string\n preview: PluginSiteResourcePreview | null\n}\n\n/**\n * Every plugin's preview, in registration order. Never throws: a preview that\n * fails is logged and answered as `null` for its plugin.\n */\nexport async function previewPluginSiteResources(\n request: PluginSiteResourceRequest,\n): Promise<PluginSiteResourcePreviewRow[]> {\n const rows: PluginSiteResourcePreviewRow[] = []\n for (const { pluginId, eraser } of await registered()) {\n try {\n rows.push({ pluginId, preview: await eraser.preview(request) })\n } catch (error) {\n console.error(\n `[plugins] ${pluginId} could not list what goes with site ${request.hostId}`,\n error,\n )\n rows.push({ pluginId, preview: null })\n }\n }\n return rows\n}\n\n/**\n * Runs every eraser, in registration order, and answers how many resources\n * each erased by plugin id, or `null` for one that threw. Never throws.\n */\nexport async function erasePluginSiteResources(\n request: PluginSiteResourceRequest,\n): Promise<Record<string, number | null>> {\n const erased: Record<string, number | null> = {}\n for (const { pluginId, eraser } of await registered()) {\n try {\n erased[pluginId] = await eraser.erase(request)\n } catch (error) {\n erased[pluginId] = null\n console.error(`[plugins] ${pluginId} failed to erase site ${request.hostId}'s resources`, error)\n }\n }\n return erased\n}\n\n/** The plugins with a site-resource eraser, in the order they run. */\nexport function listPluginSiteResourceErasers(): string[] {\n return registrations.map((entry) => entry.pluginId)\n}\n\n/** Test seam: forget every eraser. */\nexport function resetPluginSiteResourceErasersForTests(): void {\n registrations.length = 0\n}\n"],"names":["globalScope","getRegisteringPluginId","runPluginDeclarationsRepair","ERASERS_KEY","Symbol","for","globalThis","registrations","registerPluginSiteResourceEraser","eraser","options","pluginId","trim","Error","preview","erase","index","findIndex","entry","push","registered","length","catch","error","console","previewPluginSiteResources","request","rows","hostId","erasePluginSiteResources","erased","listPluginSiteResourceErasers","map","resetPluginSiteResourceErasersForTests"],"mappings":"IAqGEA;AArGF;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GAED,SAASC,sBAAsB,QAAQ,qCAAiC;AACxE,SAASC,2BAA2B,QAAQ,kCAA8B;AAyC1E;;;;;CAKC,GACD,MAAMC,cAAcC,OAAOC,GAAG,CAAC;AAE/B,MAAML,cAAcM;AAIpB,MAAMC,iBACJP,2BAAAA,WAAW,CAACG,YAAY,YAAxBH,2BAA6BA,WAAW,CAACG,YAAY,GAAG,EAAE;AAE5D;;;;;CAKC,GACD,OAAO,SAASK,iCACdC,MAAgC,EAChCC,OAA+B;QAEbT,MAAAA;IAAlB,MAAMU,WAAW,EAACV,QAAAA,0BAAAA,oCAAAA,0BAA4BS,2BAAAA,QAASC,QAAQ,YAA7CV,OAAiD,IAAIW,IAAI;IAC3E,IAAI,CAACD,UAAU;QACb,MAAM,IAAIE,MACR,sEACE;IAEN;IACA,IAAI,QAAOJ,0BAAAA,OAAQK,OAAO,MAAK,cAAc,QAAOL,0BAAAA,OAAQM,KAAK,MAAK,YAAY;QAChF,MAAM,IAAIF,MAAM;IAClB;IACA,MAAMG,QAAQT,cAAcU,SAAS,CAAC,CAACC,QAAUA,MAAMP,QAAQ,KAAKA;IACpE,IAAIK,SAAS,GAAGT,aAAa,CAACS,MAAM,GAAG;QAAEL;QAAUF;IAAO;SACrDF,cAAcY,IAAI,CAAC;QAAER;QAAUF;IAAO;AAC7C;AAEA;;;;CAIC,GACD,eAAeW;IACb,IAAI,CAACb,cAAcc,MAAM,EAAE;QACzB,MAAMnB,8BAA8BoB,KAAK,CAAC,CAACC;YACzCC,QAAQD,KAAK,CAAC,iEAAiEA;QACjF;IACF;IACA,OAAO;WAAIhB;KAAc;AAC3B;AAQA;;;CAGC,GACD,OAAO,eAAekB,2BACpBC,OAAkC;IAElC,MAAMC,OAAuC,EAAE;IAC/C,KAAK,MAAM,EAAEhB,QAAQ,EAAEF,MAAM,EAAE,IAAI,CAAA,MAAMW,YAAW,EAAG;QACrD,IAAI;YACFO,KAAKR,IAAI,CAAC;gBAAER;gBAAUG,SAAS,MAAML,OAAOK,OAAO,CAACY;YAAS;QAC/D,EAAE,OAAOH,OAAO;YACdC,QAAQD,KAAK,CACX,CAAC,UAAU,EAAEZ,SAAS,oCAAoC,EAAEe,QAAQE,MAAM,EAAE,EAC5EL;YAEFI,KAAKR,IAAI,CAAC;gBAAER;gBAAUG,SAAS;YAAK;QACtC;IACF;IACA,OAAOa;AACT;AAEA;;;CAGC,GACD,OAAO,eAAeE,yBACpBH,OAAkC;IAElC,MAAMI,SAAwC,CAAC;IAC/C,KAAK,MAAM,EAAEnB,QAAQ,EAAEF,MAAM,EAAE,IAAI,CAAA,MAAMW,YAAW,EAAG;QACrD,IAAI;YACFU,MAAM,CAACnB,SAAS,GAAG,MAAMF,OAAOM,KAAK,CAACW;QACxC,EAAE,OAAOH,OAAO;YACdO,MAAM,CAACnB,SAAS,GAAG;YACnBa,QAAQD,KAAK,CAAC,CAAC,UAAU,EAAEZ,SAAS,sBAAsB,EAAEe,QAAQE,MAAM,CAAC,YAAY,CAAC,EAAEL;QAC5F;IACF;IACA,OAAOO;AACT;AAEA,oEAAoE,GACpE,OAAO,SAASC;IACd,OAAOxB,cAAcyB,GAAG,CAAC,CAACd,QAAUA,MAAMP,QAAQ;AACpD;AAEA,oCAAoC,GACpC,OAAO,SAASsB;IACd1B,cAAcc,MAAM,GAAG;AACzB"}
@@ -0,0 +1,61 @@
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 gates a preclearance may waive. One today. */
18
+ export type PurchaseGate = 'domain-payment-history';
19
+ export interface PurchaseGatePreclearanceInput {
20
+ /** The workspace the purchase is for. */
21
+ orgId: string;
22
+ gate: PurchaseGate;
23
+ }
24
+ export interface PurchaseGatePreclearance {
25
+ /**
26
+ * A live, unused waiver for this workspace and gate, or null. Reads only:
27
+ * asking does not spend it.
28
+ */
29
+ check(input: PurchaseGatePreclearanceInput): Promise<{
30
+ preclearanceId: string;
31
+ } | null>;
32
+ /**
33
+ * Spend the waiver: true exactly once per id, in a transaction that marks it
34
+ * used only if nothing has. False for an id this implementation does not
35
+ * hold, one already spent, or one that expired.
36
+ */
37
+ consume(preclearanceId: string): Promise<boolean>;
38
+ }
39
+ /**
40
+ * A set: more than one surface may mint a waiver (a bundle link today), and
41
+ * the gate asks each in turn.
42
+ */
43
+ export declare const PURCHASE_GATE_PRECLEARANCE: import("./plugin-services").PluginServiceContract<PurchaseGatePreclearance>;
44
+ /** Installs an implementation. The console registers the bundle links at boot. */
45
+ export declare function registerPurchaseGatePreclearance(impl: PurchaseGatePreclearance, options?: {
46
+ pluginId?: string;
47
+ key?: string;
48
+ }): void;
49
+ /**
50
+ * The first live waiver any implementation holds for the workspace and gate,
51
+ * or null. Never throws; an implementation that throws is skipped and
52
+ * logged, so it can only ever cost a waiver, never grant one.
53
+ */
54
+ export declare function checkPurchaseGatePreclearance(input: PurchaseGatePreclearanceInput): Promise<{
55
+ preclearanceId: string;
56
+ } | null>;
57
+ /**
58
+ * Spend a waiver. True only when an implementation marked it used just now.
59
+ * Never throws: a failure answers false, which refuses the purchase.
60
+ */
61
+ export declare function consumePurchaseGatePreclearance(preclearanceId: string): Promise<boolean>;
@@ -0,0 +1,102 @@
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
+ */ /**
18
+ * WHO MAY WAIVE A PURCHASE GATE, ONCE (AGL-3955).
19
+ *
20
+ * A plugin that sells something can hold a purchase behind a gate the
21
+ * workspace has to earn: the domains plugin refuses to register a name until
22
+ * the workspace has a paid platform invoice at least 72 hours old. Core owns
23
+ * the one thing that can stand in for that history: a staff member who met
24
+ * the owner in person and minted a single-use bundle link for that one
25
+ * workspace, accountable for it in `adminAudit`.
26
+ *
27
+ * The plugin cannot read core's bundle-link records (a plugin never imports
28
+ * core's storage, and never another plugin), so the waiver is a contract
29
+ * here. The plugin asks `check` while deciding whether the gate refuses, and
30
+ * calls `consume` after it has taken its own hold and before it charges: a
31
+ * `false` from `consume` refuses the purchase, so one preclearance buys
32
+ * exactly one thing.
33
+ *
34
+ * Only the named gate is waived. Every other refusal the plugin makes (a
35
+ * brand lookalike, its per-workspace caps) stands, and an implementation has
36
+ * no say in them.
37
+ *
38
+ * ## FAILS CLOSED
39
+ *
40
+ * {@link checkPurchaseGatePreclearance} answers null on any error and when
41
+ * nothing is registered, and {@link consumePurchaseGatePreclearance} answers
42
+ * false: a waiver that cannot be confirmed is no waiver, and the gate keeps
43
+ * refusing.
44
+ */ import { definePluginServiceContract, registerPluginService, resolvePluginServices } from "./plugin-services.js";
45
+ /**
46
+ * A set: more than one surface may mint a waiver (a bundle link today), and
47
+ * the gate asks each in turn.
48
+ */ export const PURCHASE_GATE_PRECLEARANCE = definePluginServiceContract('core.purchase-gate-preclearance', {
49
+ multiple: true
50
+ });
51
+ /** Installs an implementation. The console registers the bundle links at boot. */ export function registerPurchaseGatePreclearance(impl, options) {
52
+ if (typeof (impl == null ? void 0 : impl.check) !== 'function' || typeof (impl == null ? void 0 : impl.consume) !== 'function') {
53
+ throw new Error('a purchase gate preclearance needs check and consume functions');
54
+ }
55
+ registerPluginService(PURCHASE_GATE_PRECLEARANCE, impl, _extends({}, (options == null ? void 0 : options.pluginId) ? {
56
+ pluginId: options.pluginId
57
+ } : {}, (options == null ? void 0 : options.key) ? {
58
+ key: options.key
59
+ } : {}));
60
+ }
61
+ /**
62
+ * The first live waiver any implementation holds for the workspace and gate,
63
+ * or null. Never throws; an implementation that throws is skipped and
64
+ * logged, so it can only ever cost a waiver, never grant one.
65
+ */ export async function checkPurchaseGatePreclearance(input) {
66
+ var _ref;
67
+ const orgId = String((_ref = input == null ? void 0 : input.orgId) != null ? _ref : '').trim();
68
+ if (!orgId || (input == null ? void 0 : input.gate) !== 'domain-payment-history') return null;
69
+ for (const registration of resolvePluginServices(PURCHASE_GATE_PRECLEARANCE)){
70
+ try {
71
+ var _ref1;
72
+ const found = await registration.impl.check({
73
+ orgId,
74
+ gate: input.gate
75
+ });
76
+ const preclearanceId = String((_ref1 = found == null ? void 0 : found.preclearanceId) != null ? _ref1 : '').trim();
77
+ if (preclearanceId) return {
78
+ preclearanceId
79
+ };
80
+ } catch (error) {
81
+ console.error(`[purchase-gate-preclearance] check failed in "${registration.pluginId}"`, error);
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ /**
87
+ * Spend a waiver. True only when an implementation marked it used just now.
88
+ * Never throws: a failure answers false, which refuses the purchase.
89
+ */ export async function consumePurchaseGatePreclearance(preclearanceId) {
90
+ const id = String(preclearanceId != null ? preclearanceId : '').trim();
91
+ if (!id) return false;
92
+ for (const registration of resolvePluginServices(PURCHASE_GATE_PRECLEARANCE)){
93
+ try {
94
+ if (await registration.impl.consume(id) === true) return true;
95
+ } catch (error) {
96
+ console.error(`[purchase-gate-preclearance] consume failed in "${registration.pluginId}"`, error);
97
+ }
98
+ }
99
+ return false;
100
+ }
101
+
102
+ //# sourceMappingURL=purchase-gate-preclearance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/purchase-gate-preclearance.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 * WHO MAY WAIVE A PURCHASE GATE, ONCE (AGL-3955).\n *\n * A plugin that sells something can hold a purchase behind a gate the\n * workspace has to earn: the domains plugin refuses to register a name until\n * the workspace has a paid platform invoice at least 72 hours old. Core owns\n * the one thing that can stand in for that history: a staff member who met\n * the owner in person and minted a single-use bundle link for that one\n * workspace, accountable for it in `adminAudit`.\n *\n * The plugin cannot read core's bundle-link records (a plugin never imports\n * core's storage, and never another plugin), so the waiver is a contract\n * here. The plugin asks `check` while deciding whether the gate refuses, and\n * calls `consume` after it has taken its own hold and before it charges: a\n * `false` from `consume` refuses the purchase, so one preclearance buys\n * exactly one thing.\n *\n * Only the named gate is waived. Every other refusal the plugin makes (a\n * brand lookalike, its per-workspace caps) stands, and an implementation has\n * no say in them.\n *\n * ## FAILS CLOSED\n *\n * {@link checkPurchaseGatePreclearance} answers null on any error and when\n * nothing is registered, and {@link consumePurchaseGatePreclearance} answers\n * false: a waiver that cannot be confirmed is no waiver, and the gate keeps\n * refusing.\n */\n\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/** The gates a preclearance may waive. One today. */\nexport type PurchaseGate = 'domain-payment-history'\n\nexport interface PurchaseGatePreclearanceInput {\n /** The workspace the purchase is for. */\n orgId: string\n gate: PurchaseGate\n}\n\nexport interface PurchaseGatePreclearance {\n /**\n * A live, unused waiver for this workspace and gate, or null. Reads only:\n * asking does not spend it.\n */\n check(input: PurchaseGatePreclearanceInput): Promise<{ preclearanceId: string } | null>\n /**\n * Spend the waiver: true exactly once per id, in a transaction that marks it\n * used only if nothing has. False for an id this implementation does not\n * hold, one already spent, or one that expired.\n */\n consume(preclearanceId: string): Promise<boolean>\n}\n\n/**\n * A set: more than one surface may mint a waiver (a bundle link today), and\n * the gate asks each in turn.\n */\nexport const PURCHASE_GATE_PRECLEARANCE =\n definePluginServiceContract<PurchaseGatePreclearance>('core.purchase-gate-preclearance', {\n multiple: true,\n })\n\n/** Installs an implementation. The console registers the bundle links at boot. */\nexport function registerPurchaseGatePreclearance(\n impl: PurchaseGatePreclearance,\n options?: { pluginId?: string; key?: string },\n): void {\n if (typeof impl?.check !== 'function' || typeof impl?.consume !== 'function') {\n throw new Error('a purchase gate preclearance needs check and consume functions')\n }\n registerPluginService(PURCHASE_GATE_PRECLEARANCE, impl, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n ...(options?.key ? { key: options.key } : {}),\n })\n}\n\n/**\n * The first live waiver any implementation holds for the workspace and gate,\n * or null. Never throws; an implementation that throws is skipped and\n * logged, so it can only ever cost a waiver, never grant one.\n */\nexport async function checkPurchaseGatePreclearance(\n input: PurchaseGatePreclearanceInput,\n): Promise<{ preclearanceId: string } | null> {\n const orgId = String(input?.orgId ?? '').trim()\n if (!orgId || input?.gate !== 'domain-payment-history') return null\n for (const registration of resolvePluginServices(PURCHASE_GATE_PRECLEARANCE)) {\n try {\n const found = await registration.impl.check({ orgId, gate: input.gate })\n const preclearanceId = String(found?.preclearanceId ?? '').trim()\n if (preclearanceId) return { preclearanceId }\n } catch (error) {\n console.error(\n `[purchase-gate-preclearance] check failed in \"${registration.pluginId}\"`,\n error,\n )\n }\n }\n return null\n}\n\n/**\n * Spend a waiver. True only when an implementation marked it used just now.\n * Never throws: a failure answers false, which refuses the purchase.\n */\nexport async function consumePurchaseGatePreclearance(\n preclearanceId: string,\n): Promise<boolean> {\n const id = String(preclearanceId ?? '').trim()\n if (!id) return false\n for (const registration of resolvePluginServices(PURCHASE_GATE_PRECLEARANCE)) {\n try {\n if ((await registration.impl.consume(id)) === true) return true\n } catch (error) {\n console.error(\n `[purchase-gate-preclearance] consume failed in \"${registration.pluginId}\"`,\n error,\n )\n }\n }\n return false\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PURCHASE_GATE_PRECLEARANCE","multiple","registerPurchaseGatePreclearance","impl","options","check","consume","Error","pluginId","key","checkPurchaseGatePreclearance","input","orgId","String","trim","gate","registration","found","preclearanceId","error","console","consumePurchaseGatePreclearance","id"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAyB1B;;;CAGC,GACD,OAAO,MAAMC,6BACXH,4BAAsD,mCAAmC;IACvFI,UAAU;AACZ,GAAE;AAEJ,gFAAgF,GAChF,OAAO,SAASC,iCACdC,IAA8B,EAC9BC,OAA6C;IAE7C,IAAI,QAAOD,wBAAAA,KAAME,KAAK,MAAK,cAAc,QAAOF,wBAAAA,KAAMG,OAAO,MAAK,YAAY;QAC5E,MAAM,IAAIC,MAAM;IAClB;IACAT,sBAAsBE,4BAA4BG,MAAM,aAClDC,CAAAA,2BAAAA,QAASI,QAAQ,IAAG;QAAEA,UAAUJ,QAAQI,QAAQ;IAAC,IAAI,CAAC,GACtDJ,CAAAA,2BAAAA,QAASK,GAAG,IAAG;QAAEA,KAAKL,QAAQK,GAAG;IAAC,IAAI,CAAC;AAE/C;AAEA;;;;CAIC,GACD,OAAO,eAAeC,8BACpBC,KAAoC;;IAEpC,MAAMC,QAAQC,eAAOF,yBAAAA,MAAOC,KAAK,mBAAI,IAAIE,IAAI;IAC7C,IAAI,CAACF,SAASD,CAAAA,yBAAAA,MAAOI,IAAI,MAAK,0BAA0B,OAAO;IAC/D,KAAK,MAAMC,gBAAgBjB,sBAAsBC,4BAA6B;QAC5E,IAAI;;YACF,MAAMiB,QAAQ,MAAMD,aAAab,IAAI,CAACE,KAAK,CAAC;gBAAEO;gBAAOG,MAAMJ,MAAMI,IAAI;YAAC;YACtE,MAAMG,iBAAiBL,gBAAOI,yBAAAA,MAAOC,cAAc,oBAAI,IAAIJ,IAAI;YAC/D,IAAII,gBAAgB,OAAO;gBAAEA;YAAe;QAC9C,EAAE,OAAOC,OAAO;YACdC,QAAQD,KAAK,CACX,CAAC,8CAA8C,EAAEH,aAAaR,QAAQ,CAAC,CAAC,CAAC,EACzEW;QAEJ;IACF;IACA,OAAO;AACT;AAEA;;;CAGC,GACD,OAAO,eAAeE,gCACpBH,cAAsB;IAEtB,MAAMI,KAAKT,OAAOK,yBAAAA,iBAAkB,IAAIJ,IAAI;IAC5C,IAAI,CAACQ,IAAI,OAAO;IAChB,KAAK,MAAMN,gBAAgBjB,sBAAsBC,4BAA6B;QAC5E,IAAI;YACF,IAAI,AAAC,MAAMgB,aAAab,IAAI,CAACG,OAAO,CAACgB,QAAS,MAAM,OAAO;QAC7D,EAAE,OAAOH,OAAO;YACdC,QAAQD,KAAK,CACX,CAAC,gDAAgD,EAAEH,aAAaR,QAAQ,CAAC,CAAC,CAAC,EAC3EW;QAEJ;IACF;IACA,OAAO;AACT"}