@aglyn/aglyn 1.0.0-beta.212 → 1.0.0-beta.213
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.
- package/package.json +11 -11
- package/src/lib/app-utils/docs-index.generated.js +12 -5
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/plugin-bundle-checks.d.ts +3 -2
- package/src/lib/app-utils/plugin-bundle-checks.js +242 -3
- package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -1
- package/src/lib/app-utils/plugin-host-abi.d.ts +61 -0
- package/src/lib/app-utils/plugin-host-abi.js +68 -0
- package/src/lib/app-utils/plugin-host-abi.js.map +1 -0
- package/src/lib/app-utils/realm-host-surface.generated.d.ts +9 -0
- package/src/lib/app-utils/realm-host-surface.generated.js +143 -0
- package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -0
- package/src/lib/app-utils/search-engine-verification.d.ts +98 -0
- package/src/lib/app-utils/search-engine-verification.js +140 -0
- package/src/lib/app-utils/search-engine-verification.js.map +1 -0
- package/src/lib/foundation/definitions/platform.types.d.ts +24 -0
- package/src/lib/foundation/definitions/platform.types.js +2 -1
- package/src/lib/foundation/definitions/platform.types.js.map +1 -1
- package/src/lib/plugin-manager/plugin-contributions.d.ts +21 -0
- package/src/lib/plugin-manager/plugin-contributions.js +27 -0
- package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
- package/src/lib/plugin-manager/realm-host-aglyn.generated.d.ts +8 -0
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js +101 -0
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -0
- package/src/lib/plugin-manager/realm-plugins.d.ts +3 -2
- package/src/lib/plugin-manager/realm-plugins.js +8 -3
- package/src/lib/plugin-manager/realm-plugins.js.map +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
* What the realm-plugin host ABI hands a signed bundle (AGL-420, AGL-3392):
|
|
18
|
+
* the keys of `globalThis.__AGLYN_PLUGIN_HOST__`, which every app composes
|
|
19
|
+
* from its OWN module instances.
|
|
20
|
+
*
|
|
21
|
+
* Two rules decide what is here. What must be ONE instance across the app and
|
|
22
|
+
* a bundle comes from the host, or the bundle splits it silently. And a
|
|
23
|
+
* library the site already runs is never compiled into a bundle a second time
|
|
24
|
+
* — so the host holds it, and the realm build refuses to compile it in:
|
|
25
|
+
*
|
|
26
|
+
* - `React`, `jsxRuntime` — two Reacts cannot render one tree.
|
|
27
|
+
* - `aglyn` — core's REALM PLUGIN SURFACE: the registries a bundle registers
|
|
28
|
+
* into and the vocabulary it registers with.
|
|
29
|
+
* - `mui` — MUI's realm plugin surface: the components a site element is laid
|
|
30
|
+
* out, filled in and printed with, their class names, `useMediaQuery`, and
|
|
31
|
+
* `createSvgIcon` (how an icon from `@mui/icons-material` draws).
|
|
32
|
+
* - `muiStyles` — the part of `@mui/material/styles` an element styles itself
|
|
33
|
+
* with: `styled`, `useTheme`, the color helpers, `css`, `keyframes`,
|
|
34
|
+
* `useColorScheme` — the site's theme and style cache behind them.
|
|
35
|
+
*
|
|
36
|
+
* Each is a short reviewed list of NAMES (`generate-realm-host-exports.mjs`),
|
|
37
|
+
* read member by member and imported STATICALLY by the host module a page
|
|
38
|
+
* loads only when it runs a realm plugin. Measured on the tenant build, both
|
|
39
|
+
* shapes that looked cheaper cost every published page: an `import()` reaching
|
|
40
|
+
* a library module re-cut the chunks every page loads, and a whole module
|
|
41
|
+
* passed as a value (`import * as styles`) kept every one of its exports alive
|
|
42
|
+
* in the page's own copy. Loading per export would also have tied every signed
|
|
43
|
+
* bundle to the host's exact MUI version.
|
|
44
|
+
*
|
|
45
|
+
* One list, read by the apps that compose the host, the realm Rollup configs
|
|
46
|
+
* that compile imports into lookups on it, and the bundle verifier that
|
|
47
|
+
* refuses a lookup on any other key, and a name its module does not hold.
|
|
48
|
+
* Adding a key is additive and keeps `PLUGIN_HOST_ABI_VERSION`; removing or
|
|
49
|
+
* reshaping one is not.
|
|
50
|
+
*/ export const PLUGIN_HOST_ABI_KEYS = [
|
|
51
|
+
'version',
|
|
52
|
+
'React',
|
|
53
|
+
'jsxRuntime',
|
|
54
|
+
'aglyn',
|
|
55
|
+
'mui',
|
|
56
|
+
'muiStyles'
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* The keys that are SURFACES, whose names the verifier checks against what the
|
|
60
|
+
* host holds (`realm-host-surface.generated.ts`).
|
|
61
|
+
*/ export const PLUGIN_HOST_MODULE_KEYS = [
|
|
62
|
+
'aglyn',
|
|
63
|
+
'mui',
|
|
64
|
+
'muiStyles'
|
|
65
|
+
];
|
|
66
|
+
/** The global the host ABI is published on. */ export const PLUGIN_HOST_GLOBAL = '__AGLYN_PLUGIN_HOST__';
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=plugin-host-abi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/plugin-host-abi.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 * What the realm-plugin host ABI hands a signed bundle (AGL-420, AGL-3392):\n * the keys of `globalThis.__AGLYN_PLUGIN_HOST__`, which every app composes\n * from its OWN module instances.\n *\n * Two rules decide what is here. What must be ONE instance across the app and\n * a bundle comes from the host, or the bundle splits it silently. And a\n * library the site already runs is never compiled into a bundle a second time\n * — so the host holds it, and the realm build refuses to compile it in:\n *\n * - `React`, `jsxRuntime` — two Reacts cannot render one tree.\n * - `aglyn` — core's REALM PLUGIN SURFACE: the registries a bundle registers\n * into and the vocabulary it registers with.\n * - `mui` — MUI's realm plugin surface: the components a site element is laid\n * out, filled in and printed with, their class names, `useMediaQuery`, and\n * `createSvgIcon` (how an icon from `@mui/icons-material` draws).\n * - `muiStyles` — the part of `@mui/material/styles` an element styles itself\n * with: `styled`, `useTheme`, the color helpers, `css`, `keyframes`,\n * `useColorScheme` — the site's theme and style cache behind them.\n *\n * Each is a short reviewed list of NAMES (`generate-realm-host-exports.mjs`),\n * read member by member and imported STATICALLY by the host module a page\n * loads only when it runs a realm plugin. Measured on the tenant build, both\n * shapes that looked cheaper cost every published page: an `import()` reaching\n * a library module re-cut the chunks every page loads, and a whole module\n * passed as a value (`import * as styles`) kept every one of its exports alive\n * in the page's own copy. Loading per export would also have tied every signed\n * bundle to the host's exact MUI version.\n *\n * One list, read by the apps that compose the host, the realm Rollup configs\n * that compile imports into lookups on it, and the bundle verifier that\n * refuses a lookup on any other key, and a name its module does not hold.\n * Adding a key is additive and keeps `PLUGIN_HOST_ABI_VERSION`; removing or\n * reshaping one is not.\n */\nexport const PLUGIN_HOST_ABI_KEYS = [\n 'version',\n 'React',\n 'jsxRuntime',\n 'aglyn',\n 'mui',\n 'muiStyles',\n] as const\n\nexport type PluginHostAbiKey = (typeof PLUGIN_HOST_ABI_KEYS)[number]\n\n/**\n * The keys that are SURFACES, whose names the verifier checks against what the\n * host holds (`realm-host-surface.generated.ts`).\n */\nexport const PLUGIN_HOST_MODULE_KEYS = ['aglyn', 'mui', 'muiStyles'] as const\n\nexport type PluginHostModuleKey = (typeof PLUGIN_HOST_MODULE_KEYS)[number]\n\n/** The global the host ABI is published on. */\nexport const PLUGIN_HOST_GLOBAL = '__AGLYN_PLUGIN_HOST__'\n"],"names":["PLUGIN_HOST_ABI_KEYS","PLUGIN_HOST_MODULE_KEYS","PLUGIN_HOST_GLOBAL"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GACD,OAAO,MAAMA,uBAAuB;IAClC;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAIV;;;CAGC,GACD,OAAO,MAAMC,0BAA0B;IAAC;IAAS;IAAO;CAAY,CAAS;AAI7E,6CAA6C,GAC7C,OAAO,MAAMC,qBAAqB,wBAAuB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED FILE — do not edit. Regenerate with:
|
|
3
|
+
* node tools/scripts/generate-realm-host-exports.mjs
|
|
4
|
+
*
|
|
5
|
+
* The names each realm host module holds (AGL-3392), for the bundle
|
|
6
|
+
* verifier: a bundle reading any other name from the host is refused,
|
|
7
|
+
* because it would read `undefined` on every site.
|
|
8
|
+
*/
|
|
9
|
+
export declare const REALM_HOST_SURFACE_NAMES: Readonly<Record<'aglyn' | 'mui' | 'muiStyles', ReadonlySet<string>>>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED FILE — do not edit. Regenerate with:
|
|
3
|
+
* node tools/scripts/generate-realm-host-exports.mjs
|
|
4
|
+
*
|
|
5
|
+
* The names each realm host module holds (AGL-3392), for the bundle
|
|
6
|
+
* verifier: a bundle reading any other name from the host is refused,
|
|
7
|
+
* because it would read `undefined` on every site.
|
|
8
|
+
*/ /* eslint-disable */ export const REALM_HOST_SURFACE_NAMES = {
|
|
9
|
+
aglyn: new Set([
|
|
10
|
+
"Aglyn",
|
|
11
|
+
"CATEGORY_REQUIRED_CAPABILITY",
|
|
12
|
+
"COMPONENT_CATEGORY_ORDER",
|
|
13
|
+
"CONSOLE_STAFF_WIDGET_SLOTS",
|
|
14
|
+
"CONSOLE_WIDGET_SLOTS",
|
|
15
|
+
"ComponentCategory",
|
|
16
|
+
"EnabledPluginsContext",
|
|
17
|
+
"FEATURE_FLAG",
|
|
18
|
+
"FUNCTION_BUILTIN_NAMES",
|
|
19
|
+
"FUNCTION_MAX_OPERATIONS",
|
|
20
|
+
"FUNCTION_TOKEN_PATTERN",
|
|
21
|
+
"FieldComponentType",
|
|
22
|
+
"FieldValidatorType",
|
|
23
|
+
"HOST_VARIABLE_TYPE_LABELS",
|
|
24
|
+
"LinealDirectiveFlag",
|
|
25
|
+
"MISSING_BINDING_LABEL",
|
|
26
|
+
"MUI_BUNDLE_ID",
|
|
27
|
+
"NAME_TOKEN_PATTERN",
|
|
28
|
+
"NodeIdentityContext",
|
|
29
|
+
"NodeType",
|
|
30
|
+
"PREVIEW_REFUSED_STATUS",
|
|
31
|
+
"PREVIEW_WRITE_BLOCKED_EVENT",
|
|
32
|
+
"REUSABLE_COMPONENT_CATEGORY",
|
|
33
|
+
"REUSABLE_EMAIL_BLOCK_CATEGORY",
|
|
34
|
+
"RICH_TEXT_COMMANDS",
|
|
35
|
+
"ScreenLinkContext",
|
|
36
|
+
"SiteContext",
|
|
37
|
+
"VARIABLE_ID_TOKEN_PATTERN",
|
|
38
|
+
"VARIABLE_NAME_PATTERN",
|
|
39
|
+
"aglyn",
|
|
40
|
+
"attachFunctionDefinitions",
|
|
41
|
+
"bindingTokenNeedsDeep",
|
|
42
|
+
"canvas",
|
|
43
|
+
"components",
|
|
44
|
+
"defineUiFeatureBundle",
|
|
45
|
+
"displayBindingTokens",
|
|
46
|
+
"editableBindingTokens",
|
|
47
|
+
"emitter",
|
|
48
|
+
"evaluateExpression",
|
|
49
|
+
"evaluateHostFunction",
|
|
50
|
+
"expressionIdentifiers",
|
|
51
|
+
"formatFunctionIdToken",
|
|
52
|
+
"formatFunctionParameterOptions",
|
|
53
|
+
"formatVariableIdToken",
|
|
54
|
+
"formatVariableValue",
|
|
55
|
+
"functionGlobals",
|
|
56
|
+
"functionReferencedNames",
|
|
57
|
+
"hasBindings",
|
|
58
|
+
"isCategoryCapabilityEnabled",
|
|
59
|
+
"isConsoleStaffWidgetSlot",
|
|
60
|
+
"isFromEnabledPlugin",
|
|
61
|
+
"isPreviewRefusal",
|
|
62
|
+
"isSwitchedOffForRenderedSite",
|
|
63
|
+
"keyById",
|
|
64
|
+
"listConsoleExtensions",
|
|
65
|
+
"listConsoleNavItems",
|
|
66
|
+
"listConsoleOrgNavItems",
|
|
67
|
+
"listConsoleProviders",
|
|
68
|
+
"listConsoleStaffPages",
|
|
69
|
+
"listConsoleWidgets",
|
|
70
|
+
"logger",
|
|
71
|
+
"nodesReferenceBinding",
|
|
72
|
+
"normalizeBindingTokens",
|
|
73
|
+
"parseFunctionParameterOptions",
|
|
74
|
+
"plugins",
|
|
75
|
+
"registerConsoleExtension",
|
|
76
|
+
"resolveBindings",
|
|
77
|
+
"resolveConsoleOrgPluginPage",
|
|
78
|
+
"resolveConsolePluginPage",
|
|
79
|
+
"resolveConsoleStaffPage",
|
|
80
|
+
"resolveNodesBindings",
|
|
81
|
+
"rewriteBindingTokensDeep",
|
|
82
|
+
"textReferencesBinding",
|
|
83
|
+
"unregisterConsoleExtension",
|
|
84
|
+
"useEnabledPlugins",
|
|
85
|
+
"useNodeId",
|
|
86
|
+
"useSite",
|
|
87
|
+
"useSiteFetch"
|
|
88
|
+
]),
|
|
89
|
+
mui: new Set([
|
|
90
|
+
"Alert",
|
|
91
|
+
"Box",
|
|
92
|
+
"Button",
|
|
93
|
+
"Card",
|
|
94
|
+
"CardContent",
|
|
95
|
+
"Checkbox",
|
|
96
|
+
"Chip",
|
|
97
|
+
"CircularProgress",
|
|
98
|
+
"Collapse",
|
|
99
|
+
"Divider",
|
|
100
|
+
"FormControl",
|
|
101
|
+
"FormControlLabel",
|
|
102
|
+
"FormHelperText",
|
|
103
|
+
"IconButton",
|
|
104
|
+
"InputAdornment",
|
|
105
|
+
"InputLabel",
|
|
106
|
+
"Link",
|
|
107
|
+
"List",
|
|
108
|
+
"ListItem",
|
|
109
|
+
"ListItemText",
|
|
110
|
+
"MenuItem",
|
|
111
|
+
"Paper",
|
|
112
|
+
"Radio",
|
|
113
|
+
"RadioGroup",
|
|
114
|
+
"Select",
|
|
115
|
+
"Stack",
|
|
116
|
+
"SvgIcon",
|
|
117
|
+
"Switch",
|
|
118
|
+
"Table",
|
|
119
|
+
"TableBody",
|
|
120
|
+
"TableCell",
|
|
121
|
+
"TableHead",
|
|
122
|
+
"TableRow",
|
|
123
|
+
"TextField",
|
|
124
|
+
"Tooltip",
|
|
125
|
+
"Typography",
|
|
126
|
+
"createSvgIcon",
|
|
127
|
+
"useMediaQuery"
|
|
128
|
+
]),
|
|
129
|
+
muiStyles: new Set([
|
|
130
|
+
"alpha",
|
|
131
|
+
"css",
|
|
132
|
+
"darken",
|
|
133
|
+
"emphasize",
|
|
134
|
+
"getContrastRatio",
|
|
135
|
+
"keyframes",
|
|
136
|
+
"lighten",
|
|
137
|
+
"styled",
|
|
138
|
+
"useColorScheme",
|
|
139
|
+
"useTheme"
|
|
140
|
+
])
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//# sourceMappingURL=realm-host-surface.generated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/realm-host-surface.generated.ts"],"sourcesContent":["/**\n * GENERATED FILE — do not edit. Regenerate with:\n * node tools/scripts/generate-realm-host-exports.mjs\n *\n * The names each realm host module holds (AGL-3392), for the bundle\n * verifier: a bundle reading any other name from the host is refused,\n * because it would read `undefined` on every site.\n */\n/* eslint-disable */\n\nexport const REALM_HOST_SURFACE_NAMES: Readonly<\n Record<'aglyn' | 'mui' | 'muiStyles', ReadonlySet<string>>\n> = {\n aglyn: new Set([\"Aglyn\",\"CATEGORY_REQUIRED_CAPABILITY\",\"COMPONENT_CATEGORY_ORDER\",\"CONSOLE_STAFF_WIDGET_SLOTS\",\"CONSOLE_WIDGET_SLOTS\",\"ComponentCategory\",\"EnabledPluginsContext\",\"FEATURE_FLAG\",\"FUNCTION_BUILTIN_NAMES\",\"FUNCTION_MAX_OPERATIONS\",\"FUNCTION_TOKEN_PATTERN\",\"FieldComponentType\",\"FieldValidatorType\",\"HOST_VARIABLE_TYPE_LABELS\",\"LinealDirectiveFlag\",\"MISSING_BINDING_LABEL\",\"MUI_BUNDLE_ID\",\"NAME_TOKEN_PATTERN\",\"NodeIdentityContext\",\"NodeType\",\"PREVIEW_REFUSED_STATUS\",\"PREVIEW_WRITE_BLOCKED_EVENT\",\"REUSABLE_COMPONENT_CATEGORY\",\"REUSABLE_EMAIL_BLOCK_CATEGORY\",\"RICH_TEXT_COMMANDS\",\"ScreenLinkContext\",\"SiteContext\",\"VARIABLE_ID_TOKEN_PATTERN\",\"VARIABLE_NAME_PATTERN\",\"aglyn\",\"attachFunctionDefinitions\",\"bindingTokenNeedsDeep\",\"canvas\",\"components\",\"defineUiFeatureBundle\",\"displayBindingTokens\",\"editableBindingTokens\",\"emitter\",\"evaluateExpression\",\"evaluateHostFunction\",\"expressionIdentifiers\",\"formatFunctionIdToken\",\"formatFunctionParameterOptions\",\"formatVariableIdToken\",\"formatVariableValue\",\"functionGlobals\",\"functionReferencedNames\",\"hasBindings\",\"isCategoryCapabilityEnabled\",\"isConsoleStaffWidgetSlot\",\"isFromEnabledPlugin\",\"isPreviewRefusal\",\"isSwitchedOffForRenderedSite\",\"keyById\",\"listConsoleExtensions\",\"listConsoleNavItems\",\"listConsoleOrgNavItems\",\"listConsoleProviders\",\"listConsoleStaffPages\",\"listConsoleWidgets\",\"logger\",\"nodesReferenceBinding\",\"normalizeBindingTokens\",\"parseFunctionParameterOptions\",\"plugins\",\"registerConsoleExtension\",\"resolveBindings\",\"resolveConsoleOrgPluginPage\",\"resolveConsolePluginPage\",\"resolveConsoleStaffPage\",\"resolveNodesBindings\",\"rewriteBindingTokensDeep\",\"textReferencesBinding\",\"unregisterConsoleExtension\",\"useEnabledPlugins\",\"useNodeId\",\"useSite\",\"useSiteFetch\"]),\n mui: new Set([\"Alert\",\"Box\",\"Button\",\"Card\",\"CardContent\",\"Checkbox\",\"Chip\",\"CircularProgress\",\"Collapse\",\"Divider\",\"FormControl\",\"FormControlLabel\",\"FormHelperText\",\"IconButton\",\"InputAdornment\",\"InputLabel\",\"Link\",\"List\",\"ListItem\",\"ListItemText\",\"MenuItem\",\"Paper\",\"Radio\",\"RadioGroup\",\"Select\",\"Stack\",\"SvgIcon\",\"Switch\",\"Table\",\"TableBody\",\"TableCell\",\"TableHead\",\"TableRow\",\"TextField\",\"Tooltip\",\"Typography\",\"createSvgIcon\",\"useMediaQuery\"]),\n muiStyles: new Set([\"alpha\",\"css\",\"darken\",\"emphasize\",\"getContrastRatio\",\"keyframes\",\"lighten\",\"styled\",\"useColorScheme\",\"useTheme\"]),\n}\n"],"names":["REALM_HOST_SURFACE_NAMES","aglyn","Set","mui","muiStyles"],"mappings":"AAAA;;;;;;;CAOC,GACD,kBAAkB,GAElB,OAAO,MAAMA,2BAET;IACFC,OAAO,IAAIC,IAAI;QAAC;QAAQ;QAA+B;QAA2B;QAA6B;QAAuB;QAAoB;QAAwB;QAAe;QAAyB;QAA0B;QAAyB;QAAqB;QAAqB;QAA4B;QAAsB;QAAwB;QAAgB;QAAqB;QAAsB;QAAW;QAAyB;QAA8B;QAA8B;QAAgC;QAAqB;QAAoB;QAAc;QAA4B;QAAwB;QAAQ;QAA4B;QAAwB;QAAS;QAAa;QAAwB;QAAuB;QAAwB;QAAU;QAAqB;QAAuB;QAAwB;QAAwB;QAAiC;QAAwB;QAAsB;QAAkB;QAA0B;QAAc;QAA8B;QAA2B;QAAsB;QAAmB;QAA+B;QAAU;QAAwB;QAAsB;QAAyB;QAAuB;QAAwB;QAAqB;QAAS;QAAwB;QAAyB;QAAgC;QAAU;QAA2B;QAAkB;QAA8B;QAA2B;QAA0B;QAAuB;QAA2B;QAAwB;QAA6B;QAAoB;QAAY;QAAU;KAAe;IACzsDC,KAAK,IAAID,IAAI;QAAC;QAAQ;QAAM;QAAS;QAAO;QAAc;QAAW;QAAO;QAAmB;QAAW;QAAU;QAAc;QAAmB;QAAiB;QAAa;QAAiB;QAAa;QAAO;QAAO;QAAW;QAAe;QAAW;QAAQ;QAAQ;QAAa;QAAS;QAAQ;QAAU;QAAS;QAAQ;QAAY;QAAY;QAAY;QAAW;QAAY;QAAU;QAAa;QAAgB;KAAgB;IAC/bE,WAAW,IAAIF,IAAI;QAAC;QAAQ;QAAM;QAAS;QAAY;QAAmB;QAAY;QAAU;QAAS;QAAiB;KAAW;AACvI,EAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
* Search engine ownership verification by HTML meta tag (AGL-3399).
|
|
19
|
+
*
|
|
20
|
+
* ## Why this exists
|
|
21
|
+
*
|
|
22
|
+
* Google Search Console and Bing Webmaster Tools each offer three ways to
|
|
23
|
+
* prove a site is yours: a DNS record, an uploaded file, or a meta tag in the
|
|
24
|
+
* home page's head. A site on a platform subdomain has no DNS of its own and
|
|
25
|
+
* no way to upload a file to the origin root, so the meta tag is the only
|
|
26
|
+
* method it can use — and until this existed there was nowhere to put one.
|
|
27
|
+
*
|
|
28
|
+
* ## What is stored, and what is emitted
|
|
29
|
+
*
|
|
30
|
+
* The tools hand out a whole tag —
|
|
31
|
+
* `<meta name="google-site-verification" content="…" />` — and most people
|
|
32
|
+
* paste exactly that. Only the `content` value is stored
|
|
33
|
+
* (`host.seo.verification`), so the tenant always writes the tag itself and
|
|
34
|
+
* never echoes markup a person pasted.
|
|
35
|
+
*
|
|
36
|
+
* The token is validated at BOTH ends with the same pattern: in the console,
|
|
37
|
+
* so a bad paste is refused at the field, and again in the tenant before it
|
|
38
|
+
* reaches the head, so a value that got into the document some other way is
|
|
39
|
+
* dropped rather than rendered.
|
|
40
|
+
*
|
|
41
|
+
* Deliberately NOT re-exported from the `@aglyn/aglyn` root barrel: that
|
|
42
|
+
* barrel is part of the published-page and realm bundles, and the tenant
|
|
43
|
+
* layout and the console settings card each import this by path.
|
|
44
|
+
*/
|
|
45
|
+
/** The engines a site can verify with. */
|
|
46
|
+
export type SearchEngineVerificationEngine = 'google' | 'bing';
|
|
47
|
+
/** `host.seo.verification`, one token per engine. */
|
|
48
|
+
export type SearchEngineVerification = Partial<Record<SearchEngineVerificationEngine, string>>;
|
|
49
|
+
/** The `<meta name>` each engine looks for. */
|
|
50
|
+
export declare const SEARCH_ENGINE_VERIFICATION_META_NAMES: Readonly<Record<SearchEngineVerificationEngine, string>>;
|
|
51
|
+
/** What each engine is called on screen. */
|
|
52
|
+
export declare const SEARCH_ENGINE_VERIFICATION_LABELS: Readonly<Record<SearchEngineVerificationEngine, string>>;
|
|
53
|
+
/**
|
|
54
|
+
* A verification token: letters, digits, `-` and `_`, at most 128.
|
|
55
|
+
*
|
|
56
|
+
* Google's tokens are URL-safe base64 (about 43 characters) and Bing's are 32
|
|
57
|
+
* hex digits; both fit, and nothing that could close an attribute does.
|
|
58
|
+
*/
|
|
59
|
+
export declare const SEARCH_ENGINE_VERIFICATION_TOKEN_PATTERN: RegExp;
|
|
60
|
+
/** What a paste into a verification field reads as. */
|
|
61
|
+
export interface ParsedVerificationInput {
|
|
62
|
+
/** The token — the `content` value of a pasted tag, or the paste itself. */
|
|
63
|
+
token: string;
|
|
64
|
+
/** The `name` of a pasted tag, lower-cased; absent for a bare token. */
|
|
65
|
+
metaName?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reads a paste as either a bare token or the whole tag the tools hand out.
|
|
69
|
+
*
|
|
70
|
+
* A paste that looks like markup (it has a `<` or a `content=`) is read as a
|
|
71
|
+
* tag and yields its `content` value, `''` when it has none. Anything else is
|
|
72
|
+
* the token itself, trimmed. Never validates — see
|
|
73
|
+
* {@link searchEngineVerificationError}.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseSearchEngineVerificationInput(input: unknown): ParsedVerificationInput;
|
|
76
|
+
/** The token a paste stores as: the tag's `content`, or the trimmed paste. */
|
|
77
|
+
export declare function extractSearchEngineVerificationToken(input: unknown): string;
|
|
78
|
+
/** Whether `value` is a token the tenant will emit. */
|
|
79
|
+
export declare function isSearchEngineVerificationToken(value: unknown): value is string;
|
|
80
|
+
/**
|
|
81
|
+
* The field error for a paste into `engine`'s field, or `undefined` when it
|
|
82
|
+
* is empty or reads as a valid token.
|
|
83
|
+
*
|
|
84
|
+
* A tag for the OTHER engine is named as such: a Bing tag pasted into the
|
|
85
|
+
* Google field holds a perfectly well-formed token, and storing it would make
|
|
86
|
+
* Search Console's Verify fail with nothing on this side saying why.
|
|
87
|
+
*/
|
|
88
|
+
export declare function searchEngineVerificationError(engine: SearchEngineVerificationEngine, input: unknown): string | undefined;
|
|
89
|
+
/** One `<meta>` a published page emits. */
|
|
90
|
+
export interface SearchEngineVerificationMeta {
|
|
91
|
+
name: string;
|
|
92
|
+
content: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The verification `<meta>` tags a published page emits: one per engine
|
|
96
|
+
* whose stored token passes the pattern, none for anything unset or invalid.
|
|
97
|
+
*/
|
|
98
|
+
export declare function searchEngineVerificationMeta(verification: unknown): SearchEngineVerificationMeta[];
|
|
@@ -0,0 +1,140 @@
|
|
|
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
|
+
* Search engine ownership verification by HTML meta tag (AGL-3399).
|
|
18
|
+
*
|
|
19
|
+
* ## Why this exists
|
|
20
|
+
*
|
|
21
|
+
* Google Search Console and Bing Webmaster Tools each offer three ways to
|
|
22
|
+
* prove a site is yours: a DNS record, an uploaded file, or a meta tag in the
|
|
23
|
+
* home page's head. A site on a platform subdomain has no DNS of its own and
|
|
24
|
+
* no way to upload a file to the origin root, so the meta tag is the only
|
|
25
|
+
* method it can use — and until this existed there was nowhere to put one.
|
|
26
|
+
*
|
|
27
|
+
* ## What is stored, and what is emitted
|
|
28
|
+
*
|
|
29
|
+
* The tools hand out a whole tag —
|
|
30
|
+
* `<meta name="google-site-verification" content="…" />` — and most people
|
|
31
|
+
* paste exactly that. Only the `content` value is stored
|
|
32
|
+
* (`host.seo.verification`), so the tenant always writes the tag itself and
|
|
33
|
+
* never echoes markup a person pasted.
|
|
34
|
+
*
|
|
35
|
+
* The token is validated at BOTH ends with the same pattern: in the console,
|
|
36
|
+
* so a bad paste is refused at the field, and again in the tenant before it
|
|
37
|
+
* reaches the head, so a value that got into the document some other way is
|
|
38
|
+
* dropped rather than rendered.
|
|
39
|
+
*
|
|
40
|
+
* Deliberately NOT re-exported from the `@aglyn/aglyn` root barrel: that
|
|
41
|
+
* barrel is part of the published-page and realm bundles, and the tenant
|
|
42
|
+
* layout and the console settings card each import this by path.
|
|
43
|
+
*/ /** The engines a site can verify with. */ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
44
|
+
/** The `<meta name>` each engine looks for. */ export const SEARCH_ENGINE_VERIFICATION_META_NAMES = {
|
|
45
|
+
google: 'google-site-verification',
|
|
46
|
+
bing: 'msvalidate.01'
|
|
47
|
+
};
|
|
48
|
+
/** What each engine is called on screen. */ export const SEARCH_ENGINE_VERIFICATION_LABELS = {
|
|
49
|
+
google: 'Google Search Console',
|
|
50
|
+
bing: 'Bing Webmaster Tools'
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* A verification token: letters, digits, `-` and `_`, at most 128.
|
|
54
|
+
*
|
|
55
|
+
* Google's tokens are URL-safe base64 (about 43 characters) and Bing's are 32
|
|
56
|
+
* hex digits; both fit, and nothing that could close an attribute does.
|
|
57
|
+
*/ export const SEARCH_ENGINE_VERIFICATION_TOKEN_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
58
|
+
/** Engines in the order their tags are emitted. */ const ENGINES = [
|
|
59
|
+
'google',
|
|
60
|
+
'bing'
|
|
61
|
+
];
|
|
62
|
+
/** One attribute of a pasted tag, quoted either way or not at all. */ const attribute = (tag, name)=>{
|
|
63
|
+
var _ref, _ref1, _match_;
|
|
64
|
+
const match = new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'>/]+))`, 'i').exec(tag);
|
|
65
|
+
if (!match) return undefined;
|
|
66
|
+
return ((_ref = (_ref1 = (_match_ = match[1]) != null ? _match_ : match[2]) != null ? _ref1 : match[3]) != null ? _ref : '').trim();
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Reads a paste as either a bare token or the whole tag the tools hand out.
|
|
70
|
+
*
|
|
71
|
+
* A paste that looks like markup (it has a `<` or a `content=`) is read as a
|
|
72
|
+
* tag and yields its `content` value, `''` when it has none. Anything else is
|
|
73
|
+
* the token itself, trimmed. Never validates — see
|
|
74
|
+
* {@link searchEngineVerificationError}.
|
|
75
|
+
*/ export function parseSearchEngineVerificationInput(input) {
|
|
76
|
+
var _attribute;
|
|
77
|
+
var _attribute1;
|
|
78
|
+
if (typeof input !== 'string') return {
|
|
79
|
+
token: ''
|
|
80
|
+
};
|
|
81
|
+
const trimmed = input.trim();
|
|
82
|
+
if (!trimmed.includes('<') && !/\bcontent\s*=/i.test(trimmed)) {
|
|
83
|
+
return {
|
|
84
|
+
token: trimmed
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const metaName = (_attribute1 = attribute(trimmed, 'name')) == null ? void 0 : _attribute1.toLowerCase();
|
|
88
|
+
return _extends({
|
|
89
|
+
token: (_attribute = attribute(trimmed, 'content')) != null ? _attribute : ''
|
|
90
|
+
}, metaName ? {
|
|
91
|
+
metaName
|
|
92
|
+
} : {});
|
|
93
|
+
}
|
|
94
|
+
/** The token a paste stores as: the tag's `content`, or the trimmed paste. */ export function extractSearchEngineVerificationToken(input) {
|
|
95
|
+
return parseSearchEngineVerificationInput(input).token;
|
|
96
|
+
}
|
|
97
|
+
/** Whether `value` is a token the tenant will emit. */ export function isSearchEngineVerificationToken(value) {
|
|
98
|
+
return typeof value === 'string' && SEARCH_ENGINE_VERIFICATION_TOKEN_PATTERN.test(value);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The field error for a paste into `engine`'s field, or `undefined` when it
|
|
102
|
+
* is empty or reads as a valid token.
|
|
103
|
+
*
|
|
104
|
+
* A tag for the OTHER engine is named as such: a Bing tag pasted into the
|
|
105
|
+
* Google field holds a perfectly well-formed token, and storing it would make
|
|
106
|
+
* Search Console's Verify fail with nothing on this side saying why.
|
|
107
|
+
*/ export function searchEngineVerificationError(engine, input) {
|
|
108
|
+
if (input == null || typeof input === 'string' && !input.trim()) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const { token, metaName } = parseSearchEngineVerificationInput(input);
|
|
112
|
+
if (metaName && metaName !== SEARCH_ENGINE_VERIFICATION_META_NAMES[engine]) {
|
|
113
|
+
const other = ENGINES.find((candidate)=>SEARCH_ENGINE_VERIFICATION_META_NAMES[candidate] === metaName);
|
|
114
|
+
return other ? `That tag is for ${SEARCH_ENGINE_VERIFICATION_LABELS[other]} — paste it in that field instead` : `That tag isn’t a ${SEARCH_ENGINE_VERIFICATION_LABELS[engine]} verification tag`;
|
|
115
|
+
}
|
|
116
|
+
if (!isSearchEngineVerificationToken(token)) {
|
|
117
|
+
return 'Paste the verification code, or the whole meta tag — the code is ' + 'letters, numbers, - and _ only';
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The verification `<meta>` tags a published page emits: one per engine
|
|
123
|
+
* whose stored token passes the pattern, none for anything unset or invalid.
|
|
124
|
+
*/ export function searchEngineVerificationMeta(verification) {
|
|
125
|
+
if (!verification || typeof verification !== 'object') return [];
|
|
126
|
+
const stored = verification;
|
|
127
|
+
const tags = [];
|
|
128
|
+
for (const engine of ENGINES){
|
|
129
|
+
const token = stored[engine];
|
|
130
|
+
if (isSearchEngineVerificationToken(token)) {
|
|
131
|
+
tags.push({
|
|
132
|
+
name: SEARCH_ENGINE_VERIFICATION_META_NAMES[engine],
|
|
133
|
+
content: token
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return tags;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//# sourceMappingURL=search-engine-verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/search-engine-verification.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 * Search engine ownership verification by HTML meta tag (AGL-3399).\n *\n * ## Why this exists\n *\n * Google Search Console and Bing Webmaster Tools each offer three ways to\n * prove a site is yours: a DNS record, an uploaded file, or a meta tag in the\n * home page's head. A site on a platform subdomain has no DNS of its own and\n * no way to upload a file to the origin root, so the meta tag is the only\n * method it can use — and until this existed there was nowhere to put one.\n *\n * ## What is stored, and what is emitted\n *\n * The tools hand out a whole tag —\n * `<meta name=\"google-site-verification\" content=\"…\" />` — and most people\n * paste exactly that. Only the `content` value is stored\n * (`host.seo.verification`), so the tenant always writes the tag itself and\n * never echoes markup a person pasted.\n *\n * The token is validated at BOTH ends with the same pattern: in the console,\n * so a bad paste is refused at the field, and again in the tenant before it\n * reaches the head, so a value that got into the document some other way is\n * dropped rather than rendered.\n *\n * Deliberately NOT re-exported from the `@aglyn/aglyn` root barrel: that\n * barrel is part of the published-page and realm bundles, and the tenant\n * layout and the console settings card each import this by path.\n */\n\n/** The engines a site can verify with. */\nexport type SearchEngineVerificationEngine = 'google' | 'bing'\n\n/** `host.seo.verification`, one token per engine. */\nexport type SearchEngineVerification = Partial<\n Record<SearchEngineVerificationEngine, string>\n>\n\n/** The `<meta name>` each engine looks for. */\nexport const SEARCH_ENGINE_VERIFICATION_META_NAMES: Readonly<\n Record<SearchEngineVerificationEngine, string>\n> = {\n google: 'google-site-verification',\n bing: 'msvalidate.01',\n}\n\n/** What each engine is called on screen. */\nexport const SEARCH_ENGINE_VERIFICATION_LABELS: Readonly<\n Record<SearchEngineVerificationEngine, string>\n> = {\n google: 'Google Search Console',\n bing: 'Bing Webmaster Tools',\n}\n\n/**\n * A verification token: letters, digits, `-` and `_`, at most 128.\n *\n * Google's tokens are URL-safe base64 (about 43 characters) and Bing's are 32\n * hex digits; both fit, and nothing that could close an attribute does.\n */\nexport const SEARCH_ENGINE_VERIFICATION_TOKEN_PATTERN = /^[A-Za-z0-9_-]{1,128}$/\n\n/** Engines in the order their tags are emitted. */\nconst ENGINES: readonly SearchEngineVerificationEngine[] = ['google', 'bing']\n\n/** One attribute of a pasted tag, quoted either way or not at all. */\nconst attribute = (tag: string, name: string): string | undefined => {\n const match = new RegExp(\n `\\\\b${name}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s\"'>/]+))`,\n 'i',\n ).exec(tag)\n if (!match) return undefined\n return (match[1] ?? match[2] ?? match[3] ?? '').trim()\n}\n\n/** What a paste into a verification field reads as. */\nexport interface ParsedVerificationInput {\n /** The token — the `content` value of a pasted tag, or the paste itself. */\n token: string\n /** The `name` of a pasted tag, lower-cased; absent for a bare token. */\n metaName?: string\n}\n\n/**\n * Reads a paste as either a bare token or the whole tag the tools hand out.\n *\n * A paste that looks like markup (it has a `<` or a `content=`) is read as a\n * tag and yields its `content` value, `''` when it has none. Anything else is\n * the token itself, trimmed. Never validates — see\n * {@link searchEngineVerificationError}.\n */\nexport function parseSearchEngineVerificationInput(\n input: unknown,\n): ParsedVerificationInput {\n if (typeof input !== 'string') return { token: '' }\n const trimmed = input.trim()\n if (!trimmed.includes('<') && !/\\bcontent\\s*=/i.test(trimmed)) {\n return { token: trimmed }\n }\n const metaName = attribute(trimmed, 'name')?.toLowerCase()\n return {\n token: attribute(trimmed, 'content') ?? '',\n ...(metaName ? { metaName } : {}),\n }\n}\n\n/** The token a paste stores as: the tag's `content`, or the trimmed paste. */\nexport function extractSearchEngineVerificationToken(input: unknown): string {\n return parseSearchEngineVerificationInput(input).token\n}\n\n/** Whether `value` is a token the tenant will emit. */\nexport function isSearchEngineVerificationToken(\n value: unknown,\n): value is string {\n return (\n typeof value === 'string' &&\n SEARCH_ENGINE_VERIFICATION_TOKEN_PATTERN.test(value)\n )\n}\n\n/**\n * The field error for a paste into `engine`'s field, or `undefined` when it\n * is empty or reads as a valid token.\n *\n * A tag for the OTHER engine is named as such: a Bing tag pasted into the\n * Google field holds a perfectly well-formed token, and storing it would make\n * Search Console's Verify fail with nothing on this side saying why.\n */\nexport function searchEngineVerificationError(\n engine: SearchEngineVerificationEngine,\n input: unknown,\n): string | undefined {\n if (input == null || (typeof input === 'string' && !input.trim())) {\n return undefined\n }\n const { token, metaName } = parseSearchEngineVerificationInput(input)\n if (metaName && metaName !== SEARCH_ENGINE_VERIFICATION_META_NAMES[engine]) {\n const other = ENGINES.find(\n (candidate) =>\n SEARCH_ENGINE_VERIFICATION_META_NAMES[candidate] === metaName,\n )\n return other\n ? `That tag is for ${SEARCH_ENGINE_VERIFICATION_LABELS[other]} — paste it in that field instead`\n : `That tag isn’t a ${SEARCH_ENGINE_VERIFICATION_LABELS[engine]} verification tag`\n }\n if (!isSearchEngineVerificationToken(token)) {\n return (\n 'Paste the verification code, or the whole meta tag — the code is ' +\n 'letters, numbers, - and _ only'\n )\n }\n return undefined\n}\n\n/** One `<meta>` a published page emits. */\nexport interface SearchEngineVerificationMeta {\n name: string\n content: string\n}\n\n/**\n * The verification `<meta>` tags a published page emits: one per engine\n * whose stored token passes the pattern, none for anything unset or invalid.\n */\nexport function searchEngineVerificationMeta(\n verification: unknown,\n): SearchEngineVerificationMeta[] {\n if (!verification || typeof verification !== 'object') return []\n const stored = verification as Record<string, unknown>\n const tags: SearchEngineVerificationMeta[] = []\n for (const engine of ENGINES) {\n const token = stored[engine]\n if (isSearchEngineVerificationToken(token)) {\n tags.push({\n name: SEARCH_ENGINE_VERIFICATION_META_NAMES[engine],\n content: token,\n })\n }\n }\n return tags\n}\n"],"names":["SEARCH_ENGINE_VERIFICATION_META_NAMES","google","bing","SEARCH_ENGINE_VERIFICATION_LABELS","SEARCH_ENGINE_VERIFICATION_TOKEN_PATTERN","ENGINES","attribute","tag","name","match","RegExp","exec","undefined","trim","parseSearchEngineVerificationInput","input","token","trimmed","includes","test","metaName","toLowerCase","extractSearchEngineVerificationToken","isSearchEngineVerificationToken","value","searchEngineVerificationError","engine","other","find","candidate","searchEngineVerificationMeta","verification","stored","tags","push","content"],"mappings":"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"}
|
|
@@ -167,6 +167,15 @@ export interface AglynHost extends AglynDocument {
|
|
|
167
167
|
* `seo.entity.logo`, which is publisher-semantic JSON-LD.
|
|
168
168
|
*/
|
|
169
169
|
logoUrl?: string;
|
|
170
|
+
/**
|
|
171
|
+
* The site logo for the DARK scheme (AGL-3400). A wordmark drawn for a
|
|
172
|
+
* light ground — dark ink on a transparent PNG — disappears on the dark
|
|
173
|
+
* loader scrim and error screens, so a site may name a second mark for
|
|
174
|
+
* them. Unset means {@link logoUrl} serves both schemes. Read only where a
|
|
175
|
+
* scheme is known; emails, `{{host.logo}}` and the install icon stay on
|
|
176
|
+
* `logoUrl`, because a mail client paints a light ground.
|
|
177
|
+
*/
|
|
178
|
+
logoDarkUrl?: string;
|
|
170
179
|
seo?: {
|
|
171
180
|
title?: string;
|
|
172
181
|
description?: string;
|
|
@@ -340,6 +349,21 @@ export interface AglynHost extends AglynDocument {
|
|
|
340
349
|
/** How an agent should call the site — endpoints, etiquette, limits. */
|
|
341
350
|
howToUse?: string;
|
|
342
351
|
};
|
|
352
|
+
/**
|
|
353
|
+
* Search engine ownership tokens (AGL-3399), emitted by the tenant as
|
|
354
|
+
* `<meta name="google-site-verification">` and `<meta name="msvalidate.01">`
|
|
355
|
+
* in the head of every published page.
|
|
356
|
+
*
|
|
357
|
+
* The HTML-tag method is the only one a site on a platform subdomain can
|
|
358
|
+
* use: it has no DNS of its own and cannot upload a file to the origin.
|
|
359
|
+
* Only the tag's `content` value is stored, and the tenant re-checks it
|
|
360
|
+
* against `SEARCH_ENGINE_VERIFICATION_TOKEN_PATTERN` before emitting, so a
|
|
361
|
+
* stored value is never echoed into the head as written.
|
|
362
|
+
*/
|
|
363
|
+
verification?: {
|
|
364
|
+
google?: string;
|
|
365
|
+
bing?: string;
|
|
366
|
+
};
|
|
343
367
|
};
|
|
344
368
|
/**
|
|
345
369
|
* Customer-configured third-party analytics (AGL-138/661). Long written by
|
|
@@ -69,7 +69,8 @@ export var HostRedirectParams = /*#__PURE__*/ function(HostRedirectParams) {
|
|
|
69
69
|
*/ export const HOST_CLIENT_WRITABLE_FIELDS = {
|
|
70
70
|
displayName: 'The site name shown in the console. Cosmetic, org-scoped, and decides ' + 'nothing — the public address is `subdomain`/`cname`, both denied.',
|
|
71
71
|
logoUrl: 'The site brand mark rendered by the tenant nav (AGL-594). Authored ' + 'content pointing at already-public media; no gate reads it.',
|
|
72
|
-
|
|
72
|
+
logoDarkUrl: 'The dark-scheme variant of `logoUrl` (AGL-3400). Same reasoning: ' + 'authored content pointing at already-public media; no gate reads it.',
|
|
73
|
+
seo: 'Title, description, favicon, app icon, social card, search engine ' + 'verification tokens and the AGL-1263 `discourageSearchEngines` switch. ' + 'All of it is authoring: the values end up in the page the editor is ' + 'already free to write.',
|
|
73
74
|
// `screens` LEFT this map in AGL-2334 and is now classified as denied,
|
|
74
75
|
// following the `disabledPlugins` precedent: it is TIERED, not freely
|
|
75
76
|
// client-writable. The reason it used to carry — "an editor who may add a
|