waybetter 0.1.1 → 0.6.0
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/README.md +140 -6
- package/bin/waybetter.mjs +46 -0
- package/build/runtime.mjs +105 -0
- package/cli/buildenv.mjs +130 -0
- package/cli/dev.mjs +97 -0
- package/cli/i18n.mjs +382 -0
- package/cli/manifests.mjs +383 -0
- package/cli/privacy.mjs +768 -0
- package/cli/release.mjs +444 -0
- package/cli/remote.mjs +260 -0
- package/cli/store.mjs +326 -0
- package/cli/strings.mjs +121 -0
- package/dist/runtime/column.d.ts +93 -0
- package/dist/runtime/column.js +451 -0
- package/dist/runtime/column.js.map +1 -0
- package/dist/runtime/commandReport.d.ts +19 -0
- package/dist/runtime/commandReport.js +47 -0
- package/dist/runtime/commandReport.js.map +1 -0
- package/dist/runtime/commands.d.ts +72 -0
- package/dist/runtime/commands.js +214 -0
- package/dist/runtime/commands.js.map +1 -0
- package/dist/runtime/devErrors.d.ts +16 -0
- package/dist/runtime/devErrors.js +30 -0
- package/dist/runtime/devErrors.js.map +1 -0
- package/dist/runtime/domHub.d.ts +21 -0
- package/dist/runtime/domHub.js +139 -4
- package/dist/runtime/domHub.js.map +1 -1
- package/dist/runtime/featureModules.d.ts +157 -0
- package/dist/runtime/featureModules.js +484 -0
- package/dist/runtime/featureModules.js.map +1 -0
- package/dist/runtime/featureTable.d.ts +144 -0
- package/dist/runtime/featureTable.js +223 -0
- package/dist/runtime/featureTable.js.map +1 -0
- package/dist/runtime/fonts.d.ts +22 -0
- package/dist/runtime/fonts.js +72 -0
- package/dist/runtime/fonts.js.map +1 -0
- package/dist/runtime/i18n.d.ts +138 -0
- package/dist/runtime/i18n.js +614 -0
- package/dist/runtime/i18n.js.map +1 -0
- package/dist/runtime/i18nCheck.d.ts +30 -0
- package/dist/runtime/i18nCheck.js +224 -0
- package/dist/runtime/i18nCheck.js.map +1 -0
- package/dist/runtime/pageLang.d.ts +8 -0
- package/dist/runtime/pageLang.js +64 -0
- package/dist/runtime/pageLang.js.map +1 -0
- package/dist/runtime/pageStrings.d.ts +35 -0
- package/dist/runtime/pageStrings.js +76 -0
- package/dist/runtime/pageStrings.js.map +1 -0
- package/dist/runtime/panelButton.d.ts +33 -0
- package/dist/runtime/panelButton.js +83 -0
- package/dist/runtime/panelButton.js.map +1 -0
- package/dist/runtime/permissions.d.ts +47 -0
- package/dist/runtime/permissions.js +141 -0
- package/dist/runtime/permissions.js.map +1 -0
- package/dist/runtime/pluginApi.d.ts +51 -2
- package/dist/runtime/pluginApi.js +137 -22
- package/dist/runtime/pluginApi.js.map +1 -1
- package/dist/runtime/pluginI18n.d.ts +29 -0
- package/dist/runtime/pluginI18n.js +119 -0
- package/dist/runtime/pluginI18n.js.map +1 -0
- package/dist/runtime/settingsForm.d.ts +66 -0
- package/dist/runtime/settingsForm.js +114 -0
- package/dist/runtime/settingsForm.js.map +1 -0
- package/dist/runtime/site.d.ts +5 -0
- package/dist/runtime/site.js +1 -1
- package/dist/runtime/site.js.map +1 -1
- package/dist/runtime/siteRuntime.d.ts +144 -0
- package/dist/runtime/siteRuntime.js +175 -0
- package/dist/runtime/siteRuntime.js.map +1 -0
- package/dist/runtime/stringPackHook.d.ts +84 -0
- package/dist/runtime/stringPackHook.js +220 -0
- package/dist/runtime/stringPackHook.js.map +1 -0
- package/dist/runtime/stringPackTranslate.d.ts +59 -0
- package/dist/runtime/stringPackTranslate.js +264 -0
- package/dist/runtime/stringPackTranslate.js.map +1 -0
- package/dist/runtime/stringPacks.d.ts +200 -0
- package/dist/runtime/stringPacks.js +474 -0
- package/dist/runtime/stringPacks.js.map +1 -0
- package/dist/runtime/styles.d.ts +15 -0
- package/dist/runtime/styles.js +81 -0
- package/dist/runtime/styles.js.map +1 -0
- package/dist/runtime/textNet.d.ts +15 -0
- package/dist/runtime/textNet.js +131 -0
- package/dist/runtime/textNet.js.map +1 -0
- package/dist/runtime/themeApi.d.ts +121 -0
- package/dist/runtime/themeApi.js +220 -0
- package/dist/runtime/themeApi.js.map +1 -0
- package/dist/runtime/themeChoice.d.ts +52 -0
- package/dist/runtime/themeChoice.js +69 -0
- package/dist/runtime/themeChoice.js.map +1 -0
- package/dist/runtime/themeComponents.d.ts +435 -0
- package/dist/runtime/themeComponents.js +463 -0
- package/dist/runtime/themeComponents.js.map +1 -0
- package/dist/runtime/themeDecor.d.ts +73 -0
- package/dist/runtime/themeDecor.js +131 -0
- package/dist/runtime/themeDecor.js.map +1 -0
- package/dist/runtime/themeDoctor.d.ts +11 -0
- package/dist/runtime/themeDoctor.js +51 -0
- package/dist/runtime/themeDoctor.js.map +1 -0
- package/dist/runtime/themeIcons.d.ts +130 -0
- package/dist/runtime/themeIcons.js +263 -0
- package/dist/runtime/themeIcons.js.map +1 -0
- package/dist/runtime/themeRuntime.d.ts +100 -0
- package/dist/runtime/themeRuntime.js +497 -0
- package/dist/runtime/themeRuntime.js.map +1 -0
- package/dist/runtime/themeSchema.d.ts +10 -0
- package/dist/runtime/themeSchema.js +150 -0
- package/dist/runtime/themeSchema.js.map +1 -0
- package/dist/runtime/themeSpec.d.ts +259 -0
- package/dist/runtime/themeSpec.js +439 -0
- package/dist/runtime/themeSpec.js.map +1 -0
- package/dist/runtime/themeTokens.d.ts +12 -0
- package/dist/runtime/themeTokens.js +14 -6
- package/dist/runtime/themeTokens.js.map +1 -1
- package/dist/runtime/themeVariants.d.ts +30 -0
- package/dist/runtime/themeVariants.js +53 -0
- package/dist/runtime/themeVariants.js.map +1 -0
- package/dist/runtime/ui.d.ts +192 -0
- package/dist/runtime/ui.js +926 -0
- package/dist/runtime/ui.js.map +1 -0
- package/dist/shell/InstallDialog.d.ts +40 -0
- package/dist/shell/InstallDialog.js +170 -0
- package/dist/shell/InstallDialog.js.map +1 -0
- package/dist/shell/StoreView.d.ts +66 -0
- package/dist/shell/StoreView.js +103 -0
- package/dist/shell/StoreView.js.map +1 -0
- package/dist/shell/addressModel.js.map +1 -1
- package/dist/shell/chrome.js.map +1 -1
- package/dist/shell/deckModel.d.ts +117 -0
- package/dist/shell/deckModel.js +267 -0
- package/dist/shell/deckModel.js.map +1 -0
- package/dist/shell/frame/AccountSwitcher.d.ts +42 -0
- package/dist/shell/frame/AccountSwitcher.js +243 -0
- package/dist/shell/frame/AccountSwitcher.js.map +1 -0
- package/dist/shell/frame/AddColumnDialog.d.ts +8 -0
- package/dist/shell/frame/AddColumnDialog.js +81 -0
- package/dist/shell/frame/AddColumnDialog.js.map +1 -0
- package/dist/shell/frame/AppMenu.d.ts +21 -0
- package/dist/shell/frame/AppMenu.js +642 -0
- package/dist/shell/frame/AppMenu.js.map +1 -0
- package/dist/shell/frame/CommandPalette.d.ts +7 -0
- package/dist/shell/frame/CommandPalette.js +118 -0
- package/dist/shell/frame/CommandPalette.js.map +1 -0
- package/dist/shell/frame/DeckView.d.ts +13 -0
- package/dist/shell/frame/DeckView.js +287 -0
- package/dist/shell/frame/DeckView.js.map +1 -0
- package/dist/shell/frame/FindBar.d.ts +10 -0
- package/dist/shell/frame/FindBar.js +108 -0
- package/dist/shell/frame/FindBar.js.map +1 -0
- package/dist/shell/frame/MediaPanel.d.ts +8 -0
- package/dist/shell/frame/MediaPanel.js +76 -0
- package/dist/shell/frame/MediaPanel.js.map +1 -0
- package/dist/shell/frame/NewTabPage.d.ts +9 -0
- package/dist/shell/frame/NewTabPage.js +71 -0
- package/dist/shell/frame/NewTabPage.js.map +1 -0
- package/dist/shell/frame/PaneTheme.d.ts +16 -0
- package/dist/shell/frame/PaneTheme.js +67 -0
- package/dist/shell/frame/PaneTheme.js.map +1 -0
- package/dist/shell/frame/ReportDialog.d.ts +3 -0
- package/dist/shell/frame/ReportDialog.js +95 -0
- package/dist/shell/frame/ReportDialog.js.map +1 -0
- package/dist/shell/frame/Shell.d.ts +8 -0
- package/dist/shell/frame/Shell.js +1131 -0
- package/dist/shell/frame/Shell.js.map +1 -0
- package/dist/shell/frame/Titlebar.d.ts +16 -0
- package/dist/shell/frame/Titlebar.js +77 -0
- package/dist/shell/frame/Titlebar.js.map +1 -0
- package/dist/shell/frame/TitlebarExtras.d.ts +4 -0
- package/dist/shell/frame/TitlebarExtras.js +202 -0
- package/dist/shell/frame/TitlebarExtras.js.map +1 -0
- package/dist/shell/frame/WebPanel.d.ts +4 -0
- package/dist/shell/frame/WebPanel.js +182 -0
- package/dist/shell/frame/WebPanel.js.map +1 -0
- package/dist/shell/frame/accountModel.d.ts +49 -0
- package/dist/shell/frame/accountModel.js +67 -0
- package/dist/shell/frame/accountModel.js.map +1 -0
- package/dist/shell/frame/accountSwitcher.css +91 -0
- package/dist/shell/frame/appI18n.d.ts +44 -0
- package/dist/shell/frame/appI18n.js +64 -0
- package/dist/shell/frame/appI18n.js.map +1 -0
- package/dist/shell/frame/appMenu.css +121 -0
- package/dist/shell/frame/backupModel.d.ts +38 -0
- package/dist/shell/frame/backupModel.js +192 -0
- package/dist/shell/frame/backupModel.js.map +1 -0
- package/dist/shell/frame/boot.d.ts +8 -0
- package/dist/shell/frame/boot.js +35 -0
- package/dist/shell/frame/boot.js.map +1 -0
- package/dist/shell/frame/brand.d.ts +1 -0
- package/dist/shell/frame/brand.js +4 -0
- package/dist/shell/frame/brand.js.map +1 -0
- package/dist/shell/frame/bridge.d.ts +35 -0
- package/dist/shell/frame/bridge.js +70 -0
- package/dist/shell/frame/bridge.js.map +1 -0
- package/dist/shell/frame/chromeTheme.d.ts +29 -0
- package/dist/shell/frame/chromeTheme.js +81 -0
- package/dist/shell/frame/chromeTheme.js.map +1 -0
- package/dist/shell/frame/deck.css +209 -0
- package/dist/shell/frame/deckStore.d.ts +89 -0
- package/dist/shell/frame/deckStore.js +239 -0
- package/dist/shell/frame/deckStore.js.map +1 -0
- package/dist/shell/frame/devHost.d.ts +35 -0
- package/dist/shell/frame/devHost.js +80 -0
- package/dist/shell/frame/devHost.js.map +1 -0
- package/dist/shell/frame/devModel.d.ts +7 -0
- package/dist/shell/frame/devModel.js +17 -0
- package/dist/shell/frame/devModel.js.map +1 -0
- package/dist/shell/frame/diag.d.ts +7 -0
- package/dist/shell/frame/diag.js +39 -0
- package/dist/shell/frame/diag.js.map +1 -0
- package/dist/shell/frame/findBar.css +14 -0
- package/dist/shell/frame/frameMessages.d.ts +248 -0
- package/dist/shell/frame/frameMessages.js +252 -0
- package/dist/shell/frame/frameMessages.js.map +1 -0
- package/dist/shell/frame/hooks.d.ts +39 -0
- package/dist/shell/frame/hooks.js +97 -0
- package/dist/shell/frame/hooks.js.map +1 -0
- package/dist/shell/frame/i18n.d.ts +27 -0
- package/dist/shell/frame/i18n.js +66 -0
- package/dist/shell/frame/i18n.js.map +1 -0
- package/dist/shell/frame/iconPack.d.ts +11 -0
- package/dist/shell/frame/iconPack.js +45 -0
- package/dist/shell/frame/iconPack.js.map +1 -0
- package/dist/shell/frame/icons.d.ts +71 -0
- package/dist/shell/frame/icons.js +75 -0
- package/dist/shell/frame/icons.js.map +1 -0
- package/dist/shell/frame/index.d.ts +34 -0
- package/dist/shell/frame/index.js +43 -0
- package/dist/shell/frame/index.js.map +1 -0
- package/dist/shell/frame/language.d.ts +58 -0
- package/dist/shell/frame/language.js +165 -0
- package/dist/shell/frame/language.js.map +1 -0
- package/dist/shell/frame/mediaModel.d.ts +34 -0
- package/dist/shell/frame/mediaModel.js +53 -0
- package/dist/shell/frame/mediaModel.js.map +1 -0
- package/dist/shell/frame/menuMessages.en.json +54 -0
- package/dist/shell/frame/menuModel.d.ts +277 -0
- package/dist/shell/frame/menuModel.js +293 -0
- package/dist/shell/frame/menuModel.js.map +1 -0
- package/dist/shell/frame/paletteModel.d.ts +139 -0
- package/dist/shell/frame/paletteModel.js +112 -0
- package/dist/shell/frame/paletteModel.js.map +1 -0
- package/dist/shell/frame/paneTheme.css +9 -0
- package/dist/shell/frame/pluginCommandStore.d.ts +23 -0
- package/dist/shell/frame/pluginCommandStore.js +70 -0
- package/dist/shell/frame/pluginCommandStore.js.map +1 -0
- package/dist/shell/frame/reportModel.d.ts +52 -0
- package/dist/shell/frame/reportModel.js +105 -0
- package/dist/shell/frame/reportModel.js.map +1 -0
- package/dist/shell/frame/safeStore.d.ts +7 -0
- package/dist/shell/frame/safeStore.js +40 -0
- package/dist/shell/frame/safeStore.js.map +1 -0
- package/dist/shell/frame/searchInput.d.ts +2 -0
- package/dist/shell/frame/searchInput.js +11 -0
- package/dist/shell/frame/searchInput.js.map +1 -0
- package/dist/shell/frame/shell.css +629 -0
- package/dist/shell/frame/site.d.ts +206 -0
- package/dist/shell/frame/site.js +22 -0
- package/dist/shell/frame/site.js.map +1 -0
- package/dist/shell/frame/state.d.ts +99 -0
- package/dist/shell/frame/state.js +284 -0
- package/dist/shell/frame/state.js.map +1 -0
- package/dist/shell/frame/syncGate.d.ts +10 -0
- package/dist/shell/frame/syncGate.js +36 -0
- package/dist/shell/frame/syncGate.js.map +1 -0
- package/dist/shell/frame/syncHost.d.ts +35 -0
- package/dist/shell/frame/syncHost.js +85 -0
- package/dist/shell/frame/syncHost.js.map +1 -0
- package/dist/shell/frame/tabStill.d.ts +2 -0
- package/dist/shell/frame/tabStill.js +62 -0
- package/dist/shell/frame/tabStill.js.map +1 -0
- package/dist/shell/frame/themeScopes.d.ts +29 -0
- package/dist/shell/frame/themeScopes.js +61 -0
- package/dist/shell/frame/themeScopes.js.map +1 -0
- package/dist/shell/frame/themeStore.d.ts +99 -0
- package/dist/shell/frame/themeStore.js +252 -0
- package/dist/shell/frame/themeStore.js.map +1 -0
- package/dist/shell/frame/windowBinding.d.ts +2 -0
- package/dist/shell/frame/windowBinding.js +7 -0
- package/dist/shell/frame/windowBinding.js.map +1 -0
- package/dist/shell/frame/workspaceModel.d.ts +82 -0
- package/dist/shell/frame/workspaceModel.js +250 -0
- package/dist/shell/frame/workspaceModel.js.map +1 -0
- package/dist/shell/frame/workspaces.css +84 -0
- package/dist/shell/frame/workspaces.d.ts +22 -0
- package/dist/shell/frame/workspaces.js +125 -0
- package/dist/shell/frame/workspaces.js.map +1 -0
- package/dist/shell/frame/zoomStore.d.ts +10 -0
- package/dist/shell/frame/zoomStore.js +65 -0
- package/dist/shell/frame/zoomStore.js.map +1 -0
- package/dist/shell/installController.d.ts +122 -0
- package/dist/shell/installController.js +146 -0
- package/dist/shell/installController.js.map +1 -0
- package/dist/shell/installerMessages.d.ts +30 -0
- package/dist/shell/installerMessages.js +36 -0
- package/dist/shell/installerMessages.js.map +1 -0
- package/dist/shell/linkDrop.js.map +1 -1
- package/dist/shell/messages.d.ts +16 -0
- package/dist/shell/messages.js +27 -0
- package/dist/shell/messages.js.map +1 -0
- package/dist/shell/palette.js.map +1 -1
- package/dist/shell/paneModel.d.ts +22 -0
- package/dist/shell/paneModel.js +24 -1
- package/dist/shell/paneModel.js.map +1 -1
- package/dist/shell/panel/Appearance.d.ts +8 -0
- package/dist/shell/panel/Appearance.js +256 -0
- package/dist/shell/panel/Appearance.js.map +1 -0
- package/dist/shell/panel/BackupPanel.d.ts +5 -0
- package/dist/shell/panel/BackupPanel.js +198 -0
- package/dist/shell/panel/BackupPanel.js.map +1 -0
- package/dist/shell/panel/DeveloperPanel.d.ts +4 -0
- package/dist/shell/panel/DeveloperPanel.js +51 -0
- package/dist/shell/panel/DeveloperPanel.js.map +1 -0
- package/dist/shell/panel/FeatureToggles.d.ts +13 -0
- package/dist/shell/panel/FeatureToggles.js +42 -0
- package/dist/shell/panel/FeatureToggles.js.map +1 -0
- package/dist/shell/panel/Home.d.ts +3 -0
- package/dist/shell/panel/Home.js +23 -0
- package/dist/shell/panel/Home.js.map +1 -0
- package/dist/shell/panel/IconPackInstall.d.ts +15 -0
- package/dist/shell/panel/IconPackInstall.js +41 -0
- package/dist/shell/panel/IconPackInstall.js.map +1 -0
- package/dist/shell/panel/LanguageSettings.d.ts +1 -0
- package/dist/shell/panel/LanguageSettings.js +54 -0
- package/dist/shell/panel/LanguageSettings.js.map +1 -0
- package/dist/shell/panel/MakePanel.d.ts +3 -0
- package/dist/shell/panel/MakePanel.js +237 -0
- package/dist/shell/panel/MakePanel.js.map +1 -0
- package/dist/shell/panel/MarkdownDoc.d.ts +6 -0
- package/dist/shell/panel/MarkdownDoc.js +35 -0
- package/dist/shell/panel/MarkdownDoc.js.map +1 -0
- package/dist/shell/panel/Modes.d.ts +40 -0
- package/dist/shell/panel/Modes.js +270 -0
- package/dist/shell/panel/Modes.js.map +1 -0
- package/dist/shell/panel/NavIcon.d.ts +4 -0
- package/dist/shell/panel/NavIcon.js +6 -0
- package/dist/shell/panel/NavIcon.js.map +1 -0
- package/dist/shell/panel/Panel.d.ts +7 -0
- package/dist/shell/panel/Panel.js +139 -0
- package/dist/shell/panel/Panel.js.map +1 -0
- package/dist/shell/panel/PluginFeatures.d.ts +24 -0
- package/dist/shell/panel/PluginFeatures.js +165 -0
- package/dist/shell/panel/PluginFeatures.js.map +1 -0
- package/dist/shell/panel/Plugins.d.ts +33 -0
- package/dist/shell/panel/Plugins.js +112 -0
- package/dist/shell/panel/Plugins.js.map +1 -0
- package/dist/shell/panel/PowerSettings.d.ts +4 -0
- package/dist/shell/panel/PowerSettings.js +52 -0
- package/dist/shell/panel/PowerSettings.js.map +1 -0
- package/dist/shell/panel/Settings.d.ts +23 -0
- package/dist/shell/panel/Settings.js +116 -0
- package/dist/shell/panel/Settings.js.map +1 -0
- package/dist/shell/panel/ShareSetup.d.ts +8 -0
- package/dist/shell/panel/ShareSetup.js +130 -0
- package/dist/shell/panel/ShareSetup.js.map +1 -0
- package/dist/shell/panel/StoreSection.d.ts +6 -0
- package/dist/shell/panel/StoreSection.js +48 -0
- package/dist/shell/panel/StoreSection.js.map +1 -0
- package/dist/shell/panel/StringPacks.d.ts +11 -0
- package/dist/shell/panel/StringPacks.js +106 -0
- package/dist/shell/panel/StringPacks.js.map +1 -0
- package/dist/shell/panel/ThemeBuilder.d.ts +5 -0
- package/dist/shell/panel/ThemeBuilder.js +205 -0
- package/dist/shell/panel/ThemeBuilder.js.map +1 -0
- package/dist/shell/panel/backup.css +34 -0
- package/dist/shell/panel/builder.css +232 -0
- package/dist/shell/panel/builderModel.d.ts +55 -0
- package/dist/shell/panel/builderModel.js +95 -0
- package/dist/shell/panel/builderModel.js.map +1 -0
- package/dist/shell/panel/compare.d.ts +10 -0
- package/dist/shell/panel/compare.js +51 -0
- package/dist/shell/panel/compare.js.map +1 -0
- package/dist/shell/panel/controls.d.ts +135 -0
- package/dist/shell/panel/controls.js +213 -0
- package/dist/shell/panel/controls.js.map +1 -0
- package/dist/shell/panel/featureModel.d.ts +69 -0
- package/dist/shell/panel/featureModel.js +79 -0
- package/dist/shell/panel/featureModel.js.map +1 -0
- package/dist/shell/panel/fonts/FontBrowser.d.ts +10 -0
- package/dist/shell/panel/fonts/FontBrowser.js +189 -0
- package/dist/shell/panel/fonts/FontBrowser.js.map +1 -0
- package/dist/shell/panel/fonts/FontRoles.d.ts +20 -0
- package/dist/shell/panel/fonts/FontRoles.js +77 -0
- package/dist/shell/panel/fonts/FontRoles.js.map +1 -0
- package/dist/shell/panel/fonts/catalog.d.ts +37 -0
- package/dist/shell/panel/fonts/catalog.js +68 -0
- package/dist/shell/panel/fonts/catalog.js.map +1 -0
- package/dist/shell/panel/fonts/googleFontsCatalog.json +1 -0
- package/dist/shell/panel/index.d.ts +20 -0
- package/dist/shell/panel/index.js +34 -0
- package/dist/shell/panel/index.js.map +1 -0
- package/dist/shell/panel/languageModel.d.ts +5 -0
- package/dist/shell/panel/languageModel.js +7 -0
- package/dist/shell/panel/languageModel.js.map +1 -0
- package/dist/shell/panel/make.css +72 -0
- package/dist/shell/panel/makeModel.d.ts +32 -0
- package/dist/shell/panel/makeModel.js +84 -0
- package/dist/shell/panel/makeModel.js.map +1 -0
- package/dist/shell/panel/markdown.d.ts +46 -0
- package/dist/shell/panel/markdown.js +100 -0
- package/dist/shell/panel/markdown.js.map +1 -0
- package/dist/shell/panel/nav.d.ts +12 -0
- package/dist/shell/panel/nav.js +50 -0
- package/dist/shell/panel/nav.js.map +1 -0
- package/dist/shell/panel/panel.css +401 -0
- package/dist/shell/panel/panelMessages.d.ts +763 -0
- package/dist/shell/panel/panelMessages.js +767 -0
- package/dist/shell/panel/panelMessages.js.map +1 -0
- package/dist/shell/panel/plugins.css +59 -0
- package/dist/shell/panel/setupModel.d.ts +87 -0
- package/dist/shell/panel/setupModel.js +152 -0
- package/dist/shell/panel/setupModel.js.map +1 -0
- package/dist/shell/panel/site.d.ts +259 -0
- package/dist/shell/panel/site.js +25 -0
- package/dist/shell/panel/site.js.map +1 -0
- package/dist/shell/panel/strings.css +7 -0
- package/dist/shell/panel/themes.d.ts +10 -0
- package/dist/shell/panel/themes.js +8 -0
- package/dist/shell/panel/themes.js.map +1 -0
- package/dist/shell/profileModel.d.ts +3 -2
- package/dist/shell/profileModel.js +7 -5
- package/dist/shell/profileModel.js.map +1 -1
- package/dist/shell/store.css +230 -0
- package/dist/shell/storeMessages.d.ts +169 -0
- package/dist/shell/storeMessages.js +172 -0
- package/dist/shell/storeMessages.js.map +1 -0
- package/dist/shell/storeModel.d.ts +181 -0
- package/dist/shell/storeModel.js +133 -0
- package/dist/shell/storeModel.js.map +1 -0
- package/dist/shell/stringPacksModel.d.ts +44 -0
- package/dist/shell/stringPacksModel.js +94 -0
- package/dist/shell/stringPacksModel.js.map +1 -0
- package/dist/shell/updateModel.d.ts +2 -1
- package/dist/shell/updateModel.js +5 -4
- package/dist/shell/updateModel.js.map +1 -1
- package/dist/shell/windowControls.d.ts +4 -0
- package/dist/shell/windowControls.js +5 -0
- package/dist/shell/windowControls.js.map +1 -1
- package/i18n-config.schema.json +29 -0
- package/i18n-meta.schema.json +26 -0
- package/i18n.schema.json +42 -0
- package/icons.schema.json +646 -0
- package/languages.schema.json +23 -0
- package/locales/ar-x-fm.json +11 -0
- package/locales/ar.json +1281 -0
- package/locales/bg.json +1280 -0
- package/locales/bn.json +1280 -0
- package/locales/ca.json +1280 -0
- package/locales/cs.json +1280 -0
- package/locales/da.json +1280 -0
- package/locales/de.json +1280 -0
- package/locales/el.json +1280 -0
- package/locales/en-GB.json +27 -0
- package/locales/en.json +1280 -0
- package/locales/en.meta.json +51 -0
- package/locales/es.json +1280 -0
- package/locales/eu.json +1280 -0
- package/locales/fa.json +1281 -0
- package/locales/fi.json +1280 -0
- package/locales/fil.json +1280 -0
- package/locales/fr.json +1280 -0
- package/locales/ga.json +1280 -0
- package/locales/gl.json +1280 -0
- package/locales/gu.json +1280 -0
- package/locales/ha.json +1280 -0
- package/locales/he.json +1281 -0
- package/locales/hi.json +1280 -0
- package/locales/hr.json +1280 -0
- package/locales/hu.json +1279 -0
- package/locales/id.json +1280 -0
- package/locales/ig.json +1280 -0
- package/locales/it.json +1280 -0
- package/locales/ja.json +1280 -0
- package/locales/kn.json +1280 -0
- package/locales/ko.json +1280 -0
- package/locales/mr.json +1280 -0
- package/locales/ms.json +1280 -0
- package/locales/nb.json +1280 -0
- package/locales/nl.json +1280 -0
- package/locales/pl.json +1280 -0
- package/locales/pt.json +1280 -0
- package/locales/ro.json +1280 -0
- package/locales/ru.json +1280 -0
- package/locales/sk.json +1280 -0
- package/locales/sr.json +1280 -0
- package/locales/sv.json +1280 -0
- package/locales/ta.json +1280 -0
- package/locales/th.json +1280 -0
- package/locales/tr.json +1280 -0
- package/locales/uk.json +1280 -0
- package/locales/ur.json +1281 -0
- package/locales/vi.json +1280 -0
- package/locales/yo.json +1280 -0
- package/locales/zh-Hans.json +1280 -0
- package/locales/zh-Hant.json +1280 -0
- package/package.json +139 -5
- package/store-catalog.schema.json +65 -0
- package/theme.schema.json +4659 -0
- package/windows/installer.nsi +926 -0
- package/windows/languages/Basque.nsh +28 -0
- package/windows/languages/Catalan.nsh +28 -0
- package/windows/languages/Croatian.nsh +28 -0
- package/windows/languages/Czech.nsh +28 -0
- package/windows/languages/Danish.nsh +28 -0
- package/windows/languages/Finnish.nsh +28 -0
- package/windows/languages/Galician.nsh +28 -0
- package/windows/languages/Greek.nsh +28 -0
- package/windows/languages/Hungarian.nsh +28 -0
- package/windows/languages/Indonesian.nsh +28 -0
- package/windows/languages/Irish.nsh +28 -0
- package/windows/languages/Malay.nsh +28 -0
- package/windows/languages/Polish.nsh +28 -0
- package/windows/languages/Romanian.nsh +28 -0
- package/windows/languages/Serbian.nsh +28 -0
- package/windows/languages/Slovak.nsh +28 -0
- package/windows/languages/Thai.nsh +28 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
// String packs: data-only packs that replace the wrapped site's own UI strings (published as
|
|
2
|
+
// `waybetter/runtime/stringPacks`). A pack adds a language the site does not offer, fixes its
|
|
3
|
+
// translation or rewords it. This file is the data side: the pack and inventory formats, the
|
|
4
|
+
// validator, the ordered application engine, coverage and the drift report. It touches no DOM and
|
|
5
|
+
// runs the same in the page, in the shell and in Node (`waybetter strings check`).
|
|
6
|
+
//
|
|
7
|
+
// The rules (they are the owner's, and every function here keeps them):
|
|
8
|
+
// - Idempotent. The result is a pure function of (the site's own values, the ordered pack list):
|
|
9
|
+
// applying twice equals applying once, and a later pack wins over an earlier one per key.
|
|
10
|
+
// - The site's own language is the fallback. A key no pack changes keeps exactly the site's value
|
|
11
|
+
// (the same object, not a copy).
|
|
12
|
+
// - An inventory of the site's known keys. A pack may change only inventory keys, and only keys
|
|
13
|
+
// whose kind can be replaced by data; unknown keys are reported, never applied.
|
|
14
|
+
// - Packs are data: plain text in an ICU MessageFormat subset ({param}, plural, select). Never
|
|
15
|
+
// HTML, never code. A message is interpreted by closures here; nothing is evaluated.
|
|
16
|
+
//
|
|
17
|
+
// One site can have several catalogs (each with its own key space, inventory and hook adapter,
|
|
18
|
+
// see stringPackHook.ts); a pack names the one it is for.
|
|
19
|
+
import { parseMessage } from "./i18n.js";
|
|
20
|
+
export const STRING_PACK_LIMITS = Object.freeze({ strings: 40000, value: 2000, name: 80, authors: 16 });
|
|
21
|
+
export const MAIN_GROUP = 'main';
|
|
22
|
+
const TAG = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/;
|
|
23
|
+
const IDENT = /^[a-z][a-z0-9-]{0,31}$/;
|
|
24
|
+
const CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f--]/;
|
|
25
|
+
const CATEGORIES = new Set(['zero', 'one', 'two', 'few', 'many', 'other']);
|
|
26
|
+
/** Whether a pack can replace a value of this kind. */
|
|
27
|
+
export function translatable(kind) {
|
|
28
|
+
return kind === 'text' || kind === 'message';
|
|
29
|
+
}
|
|
30
|
+
/** 53-bit hash of a text as 11 base-36 characters. Same result in every runtime. */
|
|
31
|
+
export function hashText(text) {
|
|
32
|
+
let h1 = 0xdeadbeef;
|
|
33
|
+
let h2 = 0x41c6ce57;
|
|
34
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
35
|
+
const ch = text.charCodeAt(i);
|
|
36
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
37
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
38
|
+
}
|
|
39
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
40
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
41
|
+
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).padStart(11, '0');
|
|
42
|
+
}
|
|
43
|
+
function compact(site, catalog, shapes) {
|
|
44
|
+
const text = [];
|
|
45
|
+
const closed = [];
|
|
46
|
+
const message = {};
|
|
47
|
+
for (const [id, entry] of shapes) {
|
|
48
|
+
if (entry.kind === 'text' && !entry.tags?.length)
|
|
49
|
+
text.push(id);
|
|
50
|
+
else if (entry.kind === 'message' && !entry.tags?.length) {
|
|
51
|
+
message[id] = (entry.params ?? []).map(name => `${name}${entry.plural?.includes(name) ? '*' : ''}${entry.select?.includes(name) ? '?' : ''}`).join(' ');
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
closed.push(id);
|
|
55
|
+
}
|
|
56
|
+
return { site, catalog, text: text.join(' '), message, closed: closed.join(' ') };
|
|
57
|
+
}
|
|
58
|
+
export function packInventory(inventory) {
|
|
59
|
+
return compact(inventory.site, inventory.catalog, Object.entries(inventory.entries));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The part of an inventory that the given keys need: what travels to a page together with the
|
|
63
|
+
* packs it should apply (a page never needs the rest). Keys the inventory does not have are left
|
|
64
|
+
* out, so they stay unknown there too.
|
|
65
|
+
*/
|
|
66
|
+
export function sliceInventory(inventory, ids) {
|
|
67
|
+
const lookup = inventoryLookup(inventory);
|
|
68
|
+
const shapes = [];
|
|
69
|
+
for (const id of new Set(ids)) {
|
|
70
|
+
const shape = lookup(id);
|
|
71
|
+
if (shape)
|
|
72
|
+
shapes.push([id, shape]);
|
|
73
|
+
}
|
|
74
|
+
return { ...compact(inventory.site, inventory.catalog, shapes), partial: true };
|
|
75
|
+
}
|
|
76
|
+
/** Whether untrusted data has the shape of a page inventory (ids and parameter names only). */
|
|
77
|
+
export function isPageInventory(input) {
|
|
78
|
+
if (!isRecord(input))
|
|
79
|
+
return false;
|
|
80
|
+
const ids = (value) => typeof value === 'string' && value.length <= 2_000_000 && /^[\w$. -]*$/.test(value);
|
|
81
|
+
return typeof input.site === 'string' && IDENT.test(input.site) && typeof input.catalog === 'string' && IDENT.test(input.catalog)
|
|
82
|
+
&& ids(input.text) && ids(input.closed) && isRecord(input.message)
|
|
83
|
+
&& Object.entries(input.message).every(([id, names]) => /^[\w$.-]+$/.test(id) && typeof names === 'string' && /^[\w$*? ]*$/.test(names))
|
|
84
|
+
&& (input.partial === undefined || typeof input.partial === 'boolean');
|
|
85
|
+
}
|
|
86
|
+
const TEXT_SHAPE = Object.freeze({ kind: 'text' });
|
|
87
|
+
const CLOSED_SHAPE = Object.freeze({ kind: 'rich' });
|
|
88
|
+
/** A lookup over either form. The page form is decoded on first use. */
|
|
89
|
+
export function inventoryLookup(inventory) {
|
|
90
|
+
if ('entries' in inventory) {
|
|
91
|
+
const entries = inventory.entries;
|
|
92
|
+
return id => (Object.prototype.hasOwnProperty.call(entries, id) ? entries[id] : undefined);
|
|
93
|
+
}
|
|
94
|
+
let table = null;
|
|
95
|
+
const decode = () => {
|
|
96
|
+
const map = new Map();
|
|
97
|
+
if (inventory.text)
|
|
98
|
+
for (const id of inventory.text.split(' '))
|
|
99
|
+
map.set(id, TEXT_SHAPE);
|
|
100
|
+
if (inventory.closed)
|
|
101
|
+
for (const id of inventory.closed.split(' '))
|
|
102
|
+
map.set(id, CLOSED_SHAPE);
|
|
103
|
+
for (const [id, spec] of Object.entries(inventory.message)) {
|
|
104
|
+
const names = spec ? spec.split(' ') : [];
|
|
105
|
+
map.set(id, {
|
|
106
|
+
kind: 'message',
|
|
107
|
+
params: names.map(name => name.replace(/[*?]+$/, '')),
|
|
108
|
+
plural: names.filter(name => name.includes('*')).map(name => name.replace(/[*?]+$/, '')),
|
|
109
|
+
select: names.filter(name => name.includes('?')).map(name => name.replace(/[*?]+$/, '')),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return map;
|
|
113
|
+
};
|
|
114
|
+
return id => (table ??= decode()).get(id);
|
|
115
|
+
}
|
|
116
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
117
|
+
/** Checks the document envelope. Errors mean the pack is not applied at all. */
|
|
118
|
+
export function checkPackShape(input) {
|
|
119
|
+
const issues = [];
|
|
120
|
+
const error = (message) => { issues.push({ level: 'error', message }); };
|
|
121
|
+
if (!isRecord(input))
|
|
122
|
+
return [{ level: 'error', message: 'a string pack is a JSON object' }];
|
|
123
|
+
if (input.format !== 1)
|
|
124
|
+
error('"format" must be 1');
|
|
125
|
+
for (const field of ['site', 'catalog']) {
|
|
126
|
+
if (typeof input[field] !== 'string' || !IDENT.test(input[field]))
|
|
127
|
+
error(`"${field}" must be a short lowercase id`);
|
|
128
|
+
}
|
|
129
|
+
if (typeof input.language !== 'string' || !TAG.test(input.language))
|
|
130
|
+
error('"language" must be a language tag (en, pt-BR, en-x-pirate)');
|
|
131
|
+
if (input.base !== undefined && (typeof input.base !== 'string' || !TAG.test(input.base)))
|
|
132
|
+
error('"base" must be a language tag');
|
|
133
|
+
if (typeof input.name !== 'string' || !input.name.trim() || input.name.length > STRING_PACK_LIMITS.name || CONTROL.test(input.name) || /[<>]/.test(input.name)) {
|
|
134
|
+
error(`"name" must be 1 to ${STRING_PACK_LIMITS.name} characters of plain text`);
|
|
135
|
+
}
|
|
136
|
+
if (!Array.isArray(input.authors) || input.authors.length > STRING_PACK_LIMITS.authors
|
|
137
|
+
|| input.authors.some(author => typeof author !== 'string' || !author.trim() || author.length > STRING_PACK_LIMITS.name || CONTROL.test(author) || /[<>]/.test(author))) {
|
|
138
|
+
error(`"authors" must be a list of at most ${STRING_PACK_LIMITS.authors} plain-text names`);
|
|
139
|
+
}
|
|
140
|
+
for (const field of ['version', 'description']) {
|
|
141
|
+
const value = input[field];
|
|
142
|
+
if (value !== undefined && (typeof value !== 'string' || value.length > 400 || CONTROL.test(value) || /[<>]/.test(value)))
|
|
143
|
+
error(`"${field}" must be plain text`);
|
|
144
|
+
}
|
|
145
|
+
if (!isRecord(input.strings))
|
|
146
|
+
error('"strings" must be an object of key id -> text');
|
|
147
|
+
else if (Object.keys(input.strings).length > STRING_PACK_LIMITS.strings)
|
|
148
|
+
error(`"strings" has more than ${STRING_PACK_LIMITS.strings} entries`);
|
|
149
|
+
const known = new Set(['$schema', 'format', 'site', 'catalog', 'language', 'base', 'name', 'authors', 'version', 'description', 'strings']);
|
|
150
|
+
for (const key of Object.keys(input))
|
|
151
|
+
if (!known.has(key))
|
|
152
|
+
issues.push({ level: 'warning', message: `unknown field "${key}" is ignored` });
|
|
153
|
+
return issues;
|
|
154
|
+
}
|
|
155
|
+
const TAG_TOKEN = /<\/?([A-Za-z][A-Za-z0-9-]*)>/g;
|
|
156
|
+
/** Tag rule: only the original's tag slots, each opened and closed; any other `<` or `>` is refused. */
|
|
157
|
+
function checkTags(value, tags) {
|
|
158
|
+
if (!value.includes('<') && !value.includes('>')) {
|
|
159
|
+
return tags.length ? `the original's tags are missing: ${tags.map(tag => `<${tag}>`).join(' ')}` : null;
|
|
160
|
+
}
|
|
161
|
+
if (!tags.length)
|
|
162
|
+
return 'markup is not allowed: a pack is text, < and > cannot be used';
|
|
163
|
+
const open = [];
|
|
164
|
+
const seen = new Set();
|
|
165
|
+
for (const match of value.matchAll(TAG_TOKEN)) {
|
|
166
|
+
const name = match[1];
|
|
167
|
+
if (!tags.includes(name))
|
|
168
|
+
return `<${name}> is not a tag of the original`;
|
|
169
|
+
if (match[0].startsWith('</')) {
|
|
170
|
+
if (open.pop() !== name)
|
|
171
|
+
return `</${name}> does not close an open tag`;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
open.push(name);
|
|
175
|
+
seen.add(name);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (open.length)
|
|
179
|
+
return `<${open[open.length - 1]}> is not closed`;
|
|
180
|
+
if (/[<>]/.test(value.replace(TAG_TOKEN, '')))
|
|
181
|
+
return '< and > may only be used for the original\'s tags';
|
|
182
|
+
const missing = tags.filter(tag => !seen.has(tag));
|
|
183
|
+
return missing.length ? `the original's tags are missing: ${missing.map(tag => `<${tag}>`).join(' ')}` : null;
|
|
184
|
+
}
|
|
185
|
+
/** Walks a parsed message against the key's shape; returns the first problem. */
|
|
186
|
+
function checkParts(parts, shape) {
|
|
187
|
+
const params = shape.params ?? [];
|
|
188
|
+
for (const part of parts) {
|
|
189
|
+
if (typeof part === 'string' || part.kind === 'pound')
|
|
190
|
+
continue;
|
|
191
|
+
if (!params.includes(part.name)) {
|
|
192
|
+
return params.length ? `{${part.name}} is not a parameter of the original (it has: ${params.join(', ')})` : `{${part.name}}: the original takes no parameters`;
|
|
193
|
+
}
|
|
194
|
+
if (part.kind === 'arg') {
|
|
195
|
+
if (part.format)
|
|
196
|
+
return `{${part.name}, ${part.format}} is not supported: only {name}, plural and select`;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (part.kind === 'plural') {
|
|
200
|
+
if (part.ordinal)
|
|
201
|
+
return `{${part.name}, selectordinal} is not supported: only {name}, plural and select`;
|
|
202
|
+
if (part.offset)
|
|
203
|
+
return `{${part.name}, plural, offset:…} is not supported`;
|
|
204
|
+
if (!shape.plural?.includes(part.name))
|
|
205
|
+
return `{${part.name}} is not a plural of the original`;
|
|
206
|
+
for (const selector of Object.keys(part.options)) {
|
|
207
|
+
if (!CATEGORIES.has(selector) && !/^=\d+$/.test(selector))
|
|
208
|
+
return `"${selector}" is not a plural form (zero, one, two, few, many, other, =N)`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else if (!shape.select?.includes(part.name)) {
|
|
212
|
+
return `{${part.name}} is not a select of the original`;
|
|
213
|
+
}
|
|
214
|
+
for (const option of Object.values(part.options)) {
|
|
215
|
+
const nested = checkParts(option, shape);
|
|
216
|
+
if (nested)
|
|
217
|
+
return nested;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Validates one pack value against the key's inventory entry. Returns the parsed message, or the
|
|
224
|
+
* reason it is refused (the original then stays).
|
|
225
|
+
*/
|
|
226
|
+
export function validateString(shape, value) {
|
|
227
|
+
if (!translatable(shape.kind))
|
|
228
|
+
return { reason: `not translatable yet (${shape.kind})` };
|
|
229
|
+
if (typeof value !== 'string')
|
|
230
|
+
return { reason: 'the value must be text' };
|
|
231
|
+
if (!value)
|
|
232
|
+
return { reason: 'the value is empty' };
|
|
233
|
+
if (value.length > STRING_PACK_LIMITS.value)
|
|
234
|
+
return { reason: `longer than ${STRING_PACK_LIMITS.value} characters` };
|
|
235
|
+
if (CONTROL.test(value))
|
|
236
|
+
return { reason: 'control or bidi-override characters are not allowed' };
|
|
237
|
+
const tags = checkTags(value, shape.tags ?? []);
|
|
238
|
+
if (tags)
|
|
239
|
+
return { reason: tags };
|
|
240
|
+
let parts;
|
|
241
|
+
try {
|
|
242
|
+
parts = parseMessage(value);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return { reason: `invalid message syntax: ${error instanceof Error ? error.message : String(error)}` };
|
|
246
|
+
}
|
|
247
|
+
const problem = checkParts(parts, shape.kind === 'message' ? shape : { kind: 'text' });
|
|
248
|
+
return problem ? { reason: problem } : { parts };
|
|
249
|
+
}
|
|
250
|
+
/** A message without arguments as its literal text (quotes resolved). */
|
|
251
|
+
function literal(parts) {
|
|
252
|
+
return parts.map(part => (typeof part === 'string' ? part : '')).join('');
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Validates a pack against an inventory. Throws on a malformed envelope or a pack for another
|
|
256
|
+
* site/catalog; every other problem is per key and lands in `report`.
|
|
257
|
+
*/
|
|
258
|
+
export function compilePack(input, inventory) {
|
|
259
|
+
const shapeErrors = checkPackShape(input).filter(issue => issue.level === 'error');
|
|
260
|
+
if (shapeErrors.length)
|
|
261
|
+
throw new Error(`invalid string pack: ${shapeErrors.map(issue => issue.message).join('; ')}`);
|
|
262
|
+
const pack = input;
|
|
263
|
+
if (pack.site !== inventory.site || pack.catalog !== inventory.catalog) {
|
|
264
|
+
throw new Error(`the pack is for ${pack.site}/${pack.catalog}, the inventory is ${inventory.site}/${inventory.catalog}`);
|
|
265
|
+
}
|
|
266
|
+
const lookup = inventoryLookup(inventory);
|
|
267
|
+
const values = new Map();
|
|
268
|
+
const report = { total: 0, valid: 0, unknown: [], rejected: [] };
|
|
269
|
+
for (const [id, value] of Object.entries(pack.strings)) {
|
|
270
|
+
report.total += 1;
|
|
271
|
+
const shape = lookup(id);
|
|
272
|
+
if (!shape) {
|
|
273
|
+
report.unknown.push(id);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const result = validateString(shape, value);
|
|
277
|
+
if ('reason' in result) {
|
|
278
|
+
report.rejected.push({ id, reason: result.reason });
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
values.set(id, shape.kind === 'text' ? { text: literal(result.parts) } : { parts: result.parts });
|
|
282
|
+
report.valid += 1;
|
|
283
|
+
}
|
|
284
|
+
return { name: pack.name, language: pack.language, base: pack.base, site: pack.site, catalog: pack.catalog, values, report };
|
|
285
|
+
}
|
|
286
|
+
// -------------------------------------------------------------------------------------------
|
|
287
|
+
// Interpreting a message (closures over the parsed parts; nothing is evaluated)
|
|
288
|
+
const rulesCache = new Map();
|
|
289
|
+
/** Plural rules of the first language the runtime knows; `null` = every number is `other`. */
|
|
290
|
+
function rulesFor(languages) {
|
|
291
|
+
const key = languages.join('|');
|
|
292
|
+
let rules = rulesCache.get(key);
|
|
293
|
+
if (rules !== undefined)
|
|
294
|
+
return rules;
|
|
295
|
+
rules = null;
|
|
296
|
+
for (const language of languages) {
|
|
297
|
+
if (!language)
|
|
298
|
+
continue;
|
|
299
|
+
const tag = language.split(/-x-/i)[0];
|
|
300
|
+
try {
|
|
301
|
+
if (Intl.PluralRules.supportedLocalesOf([tag]).length) {
|
|
302
|
+
rules = new Intl.PluralRules(tag);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// Not a tag this runtime accepts: try the next.
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
rulesCache.set(key, rules);
|
|
311
|
+
return rules;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Renders a validated message. A parameter is inserted the way string concatenation would
|
|
315
|
+
* (`String(value)`, nothing for a missing one); `#` is the plural's own value.
|
|
316
|
+
*/
|
|
317
|
+
export function renderMessage(parts, params, languages, pound = undefined) {
|
|
318
|
+
let out = '';
|
|
319
|
+
for (const part of parts) {
|
|
320
|
+
if (typeof part === 'string') {
|
|
321
|
+
out += part;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
// The parser only yields `#` inside a plural, so there is always a value it stands for.
|
|
325
|
+
if (part.kind === 'pound') {
|
|
326
|
+
out += pound === undefined || pound === null ? '' : String(pound);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const value = params[part.name];
|
|
330
|
+
if (part.kind === 'arg') {
|
|
331
|
+
out += value === undefined || value === null ? '' : String(value);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (part.kind === 'select') {
|
|
335
|
+
out += renderMessage(part.options[String(value)] ?? part.options.other, params, languages, pound);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
339
|
+
const option = Number.isFinite(n)
|
|
340
|
+
? part.options[`=${n}`] ?? part.options[rulesFor(languages)?.select(n) ?? 'other'] ?? part.options.other
|
|
341
|
+
: part.options.other;
|
|
342
|
+
out += renderMessage(option, params, languages, value);
|
|
343
|
+
}
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
346
|
+
const MAX_LISTED = 200;
|
|
347
|
+
/** `packs` in application order: a later pack wins over an earlier one for the same key. */
|
|
348
|
+
export function createStringEngine(inventory, packs) {
|
|
349
|
+
const lookup = typeof inventory === 'function' ? inventory : inventoryLookup(inventory);
|
|
350
|
+
// A slice only knows the packs' keys: what it lacks is not a new key of the site.
|
|
351
|
+
const whole = typeof inventory === 'function' || !('partial' in inventory && inventory.partial);
|
|
352
|
+
const originals = new Map();
|
|
353
|
+
const applied = new Map();
|
|
354
|
+
const produced = new WeakMap();
|
|
355
|
+
const seen = new Set();
|
|
356
|
+
const unlisted = new Set();
|
|
357
|
+
const mismatched = new Set();
|
|
358
|
+
const winner = (id) => {
|
|
359
|
+
for (let index = packs.length - 1; index >= 0; index -= 1) {
|
|
360
|
+
const value = packs[index].values.get(id);
|
|
361
|
+
if (value)
|
|
362
|
+
return { pack: packs[index], value };
|
|
363
|
+
}
|
|
364
|
+
return null;
|
|
365
|
+
};
|
|
366
|
+
const resolve = (id, incoming) => {
|
|
367
|
+
let original = incoming;
|
|
368
|
+
// A value of ours coming back in (a second application): work from the site's own value again.
|
|
369
|
+
if (typeof incoming === 'function' && produced.has(incoming))
|
|
370
|
+
original = produced.get(incoming);
|
|
371
|
+
else if (typeof incoming === 'string' && applied.has(id) && applied.get(id) === incoming)
|
|
372
|
+
original = originals.get(id);
|
|
373
|
+
seen.add(id);
|
|
374
|
+
const found = winner(id);
|
|
375
|
+
if (!found) {
|
|
376
|
+
if (whole && unlisted.size < MAX_LISTED && !lookup(id))
|
|
377
|
+
unlisted.add(id);
|
|
378
|
+
return original;
|
|
379
|
+
}
|
|
380
|
+
const shape = lookup(id);
|
|
381
|
+
if (!shape || !translatable(shape.kind))
|
|
382
|
+
return original;
|
|
383
|
+
if (shape.kind === 'text' ? typeof original !== 'string' : typeof original !== 'function') {
|
|
384
|
+
if (mismatched.size < MAX_LISTED)
|
|
385
|
+
mismatched.add(id);
|
|
386
|
+
return original;
|
|
387
|
+
}
|
|
388
|
+
if (originals.has(id) && originals.get(id) === original)
|
|
389
|
+
return applied.get(id);
|
|
390
|
+
let next;
|
|
391
|
+
if ('text' in found.value) {
|
|
392
|
+
next = found.value.text;
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
const parts = found.value.parts;
|
|
396
|
+
const languages = [found.pack.language, found.pack.base];
|
|
397
|
+
const message = (params) => renderMessage(parts, isRecord(params) ? params : {}, languages);
|
|
398
|
+
produced.set(message, original);
|
|
399
|
+
next = message;
|
|
400
|
+
}
|
|
401
|
+
originals.set(id, original);
|
|
402
|
+
applied.set(id, next);
|
|
403
|
+
return next;
|
|
404
|
+
};
|
|
405
|
+
return {
|
|
406
|
+
resolve,
|
|
407
|
+
originals: () => originals,
|
|
408
|
+
stats: () => ({ seen: seen.size, applied: applied.size, unlisted: [...unlisted], mismatched: [...mismatched] }),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* The whole catalog at once: a new object with the packs applied; `catalog` itself is not
|
|
413
|
+
* changed. `applyPacks(applyPacks(c, …), …)` with one engine equals `applyPacks(c, …)`.
|
|
414
|
+
*/
|
|
415
|
+
export function applyPacks(catalog, engine) {
|
|
416
|
+
const out = {};
|
|
417
|
+
for (const id of Object.keys(catalog))
|
|
418
|
+
out[id] = engine.resolve(id, catalog[id]);
|
|
419
|
+
return out;
|
|
420
|
+
}
|
|
421
|
+
/** The inverse: every value a pack changed is the site's own again; `catalog` is not changed. */
|
|
422
|
+
export function restoreCatalog(catalog, engine) {
|
|
423
|
+
const originals = engine.originals();
|
|
424
|
+
const out = {};
|
|
425
|
+
for (const id of Object.keys(catalog))
|
|
426
|
+
out[id] = originals.has(id) ? originals.get(id) : catalog[id];
|
|
427
|
+
return out;
|
|
428
|
+
}
|
|
429
|
+
/** Coverage of a compiled pack per group of the inventory, plus `all`. */
|
|
430
|
+
export function coverage(inventory, pack) {
|
|
431
|
+
const out = { all: { translatable: 0, translated: 0, closed: 0 } };
|
|
432
|
+
for (const [id, entry] of Object.entries(inventory.entries)) {
|
|
433
|
+
const group = (out[entry.group ?? MAIN_GROUP] ??= { translatable: 0, translated: 0, closed: 0 });
|
|
434
|
+
for (const bucket of [group, out.all]) {
|
|
435
|
+
if (!translatable(entry.kind)) {
|
|
436
|
+
bucket.closed += 1;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
bucket.translatable += 1;
|
|
440
|
+
if (pack.values.has(id))
|
|
441
|
+
bucket.translated += 1;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return out;
|
|
445
|
+
}
|
|
446
|
+
const sameList = (a = [], b = []) => a.length === b.length && [...a].sort().join('\n') === [...b].sort().join('\n');
|
|
447
|
+
/**
|
|
448
|
+
* Compares an inventory with what was observed now. `loaded` names the groups whose base catalog
|
|
449
|
+
* was actually read: a key can only be called vanished when its group was (a lazily loaded group
|
|
450
|
+
* that never arrived says nothing about its keys).
|
|
451
|
+
*/
|
|
452
|
+
export function driftReport(inventory, observed, loaded) {
|
|
453
|
+
const report = { added: [], reworded: [], reshaped: [], vanished: [] };
|
|
454
|
+
for (const [id, now] of Object.entries(observed)) {
|
|
455
|
+
const before = Object.prototype.hasOwnProperty.call(inventory.entries, id) ? inventory.entries[id] : undefined;
|
|
456
|
+
if (!before) {
|
|
457
|
+
report.added.push(id);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (before.kind !== now.kind || !sameList(before.params, now.params) || !sameList(before.plural, now.plural) || !sameList(before.select, now.select) || !sameList(before.tags, now.tags)) {
|
|
461
|
+
report.reshaped.push(id);
|
|
462
|
+
}
|
|
463
|
+
else if (before.hash !== now.hash) {
|
|
464
|
+
report.reworded.push(id);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const groups = new Set(loaded);
|
|
468
|
+
for (const [id, entry] of Object.entries(inventory.entries)) {
|
|
469
|
+
if (!Object.prototype.hasOwnProperty.call(observed, id) && groups.has(entry.group ?? MAIN_GROUP))
|
|
470
|
+
report.vanished.push(id);
|
|
471
|
+
}
|
|
472
|
+
return report;
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=stringPacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stringPacks.js","sourceRoot":"","sources":["../../../runtime/src/stringPacks.ts"],"names":[],"mappings":"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","sourcesContent":["// String packs: data-only packs that replace the wrapped site's own UI strings (published as\n// `waybetter/runtime/stringPacks`). A pack adds a language the site does not offer, fixes its\n// translation or rewords it. This file is the data side: the pack and inventory formats, the\n// validator, the ordered application engine, coverage and the drift report. It touches no DOM and\n// runs the same in the page, in the shell and in Node (`waybetter strings check`).\n//\n// The rules (they are the owner's, and every function here keeps them):\n// - Idempotent. The result is a pure function of (the site's own values, the ordered pack list):\n// applying twice equals applying once, and a later pack wins over an earlier one per key.\n// - The site's own language is the fallback. A key no pack changes keeps exactly the site's value\n// (the same object, not a copy).\n// - An inventory of the site's known keys. A pack may change only inventory keys, and only keys\n// whose kind can be replaced by data; unknown keys are reported, never applied.\n// - Packs are data: plain text in an ICU MessageFormat subset ({param}, plural, select). Never\n// HTML, never code. A message is interpreted by closures here; nothing is evaluated.\n//\n// One site can have several catalogs (each with its own key space, inventory and hook adapter,\n// see stringPackHook.ts); a pack names the one it is for.\nimport { parseMessage } from './i18n.ts';\n\ntype Parts = ReturnType<typeof parseMessage>;\n\n// -------------------------------------------------------------------------------------------\n// Formats\n\n/**\n * What the site's value for a key is:\n * - `text`: a plain string;\n * - `message`: a function of a parameter object that returns a string;\n * - `rich`: text with element slots (not replaceable by data yet);\n * - `format`: a compiled number/date/currency formatter (not text).\n * Only `text` and `message` can be replaced by a pack.\n */\nexport type StringKind = 'text' | 'message' | 'rich' | 'format';\n\nexport type InventoryEntry = {\n kind: StringKind;\n /** Parameter names the site's value takes (`message`). */\n params?: string[];\n /** Parameters the site's value pluralizes: known to be numbers, so a pack may pluralize them. */\n plural?: string[];\n /** Parameters the site's value selects on. */\n select?: string[];\n /** Tag slot names, for catalogs whose values carry `<tag>…</tag>` slots. */\n tags?: string[];\n /** Hash of the base-language value (`hashText`): drift detection; never the text itself. */\n hash: string;\n /** Part of the catalog the key arrives with (default `main`), e.g. a lazily loaded `emoji` group. */\n group?: string;\n};\n\n/** The known keys of one catalog of one site. It carries ids and shapes, never the site's text. */\nexport type StringInventory = {\n format: 1;\n site: string;\n catalog: string;\n /** Language the hashes were taken from. */\n base: string;\n /** The site build the inventory was read from. */\n build?: string;\n entries: Record<string, InventoryEntry>;\n};\n\n/** A string pack document: metadata and `key id -> text`. */\nexport type StringPack = {\n format: 1;\n site: string;\n catalog: string;\n /** BCP 47 tag of the pack's language (private-use tags are fine: `en-x-pirate`). */\n language: string;\n name: string;\n authors: string[];\n /** A language whose plural rules the pack follows when the runtime does not know `language`. */\n base?: string;\n version?: string;\n description?: string;\n strings: Record<string, string>;\n};\n\nexport const STRING_PACK_LIMITS = Object.freeze({ strings: 40000, value: 2000, name: 80, authors: 16 });\nexport const MAIN_GROUP = 'main';\n\nconst TAG = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/;\nconst IDENT = /^[a-z][a-z0-9-]{0,31}$/;\nconst CONTROL = /[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f--]/;\nconst CATEGORIES = new Set(['zero', 'one', 'two', 'few', 'many', 'other']);\n\n/** Whether a pack can replace a value of this kind. */\nexport function translatable(kind: StringKind): boolean {\n return kind === 'text' || kind === 'message';\n}\n\n/** 53-bit hash of a text as 11 base-36 characters. Same result in every runtime. */\nexport function hashText(text: string): string {\n let h1 = 0xdeadbeef;\n let h2 = 0x41c6ce57;\n for (let i = 0; i < text.length; i += 1) {\n const ch = text.charCodeAt(i);\n h1 = Math.imul(h1 ^ ch, 2654435761);\n h2 = Math.imul(h2 ^ ch, 1597334677);\n }\n h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);\n h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);\n return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).padStart(11, '0');\n}\n\n// -------------------------------------------------------------------------------------------\n// Inventory lookup (the full inventory, or its compact page form)\n\n/** The facts the validator and the engine need about one key. */\nexport type EntryShape = Pick<InventoryEntry, 'kind' | 'params' | 'plural' | 'select' | 'tags'>;\nexport type InventoryLookup = (id: string) => EntryShape | undefined;\n\n/**\n * The inventory as it ships inside a page runtime: ids and shapes only, no hashes, a fraction of\n * the size. `text` and `closed` are space-separated ids; a message is `name name*` (`*` = plural,\n * `?` = select).\n */\nexport type PageInventory = {\n site: string;\n catalog: string;\n text: string;\n message: Record<string, string>;\n closed: string;\n /** A slice: only the keys some pack names. A key missing from a slice is not a new key of the site. */\n partial?: boolean;\n};\n\nfunction compact(site: string, catalog: string, shapes: Iterable<[string, EntryShape]>): PageInventory {\n const text: string[] = [];\n const closed: string[] = [];\n const message: Record<string, string> = {};\n for (const [id, entry] of shapes) {\n if (entry.kind === 'text' && !entry.tags?.length) text.push(id);\n else if (entry.kind === 'message' && !entry.tags?.length) {\n message[id] = (entry.params ?? []).map(name => `${name}${entry.plural?.includes(name) ? '*' : ''}${entry.select?.includes(name) ? '?' : ''}`).join(' ');\n } else closed.push(id);\n }\n return { site, catalog, text: text.join(' '), message, closed: closed.join(' ') };\n}\n\nexport function packInventory(inventory: StringInventory): PageInventory {\n return compact(inventory.site, inventory.catalog, Object.entries(inventory.entries));\n}\n\n/**\n * The part of an inventory that the given keys need: what travels to a page together with the\n * packs it should apply (a page never needs the rest). Keys the inventory does not have are left\n * out, so they stay unknown there too.\n */\nexport function sliceInventory(inventory: StringInventory | PageInventory, ids: Iterable<string>): PageInventory {\n const lookup = inventoryLookup(inventory);\n const shapes: [string, EntryShape][] = [];\n for (const id of new Set(ids)) {\n const shape = lookup(id);\n if (shape) shapes.push([id, shape]);\n }\n return { ...compact(inventory.site, inventory.catalog, shapes), partial: true };\n}\n\n/** Whether untrusted data has the shape of a page inventory (ids and parameter names only). */\nexport function isPageInventory(input: unknown): input is PageInventory {\n if (!isRecord(input)) return false;\n const ids = (value: unknown) => typeof value === 'string' && value.length <= 2_000_000 && /^[\\w$. -]*$/.test(value);\n return typeof input.site === 'string' && IDENT.test(input.site) && typeof input.catalog === 'string' && IDENT.test(input.catalog)\n && ids(input.text) && ids(input.closed) && isRecord(input.message)\n && Object.entries(input.message).every(([id, names]) => /^[\\w$.-]+$/.test(id) && typeof names === 'string' && /^[\\w$*? ]*$/.test(names))\n && (input.partial === undefined || typeof input.partial === 'boolean');\n}\n\nconst TEXT_SHAPE: EntryShape = Object.freeze({ kind: 'text' });\nconst CLOSED_SHAPE: EntryShape = Object.freeze({ kind: 'rich' });\n\n/** A lookup over either form. The page form is decoded on first use. */\nexport function inventoryLookup(inventory: StringInventory | PageInventory): InventoryLookup {\n if ('entries' in inventory) {\n const entries = inventory.entries;\n return id => (Object.prototype.hasOwnProperty.call(entries, id) ? entries[id] : undefined);\n }\n let table: Map<string, EntryShape> | null = null;\n const decode = () => {\n const map = new Map<string, EntryShape>();\n if (inventory.text) for (const id of inventory.text.split(' ')) map.set(id, TEXT_SHAPE);\n if (inventory.closed) for (const id of inventory.closed.split(' ')) map.set(id, CLOSED_SHAPE);\n for (const [id, spec] of Object.entries(inventory.message)) {\n const names = spec ? spec.split(' ') : [];\n map.set(id, {\n kind: 'message',\n params: names.map(name => name.replace(/[*?]+$/, '')),\n plural: names.filter(name => name.includes('*')).map(name => name.replace(/[*?]+$/, '')),\n select: names.filter(name => name.includes('?')).map(name => name.replace(/[*?]+$/, '')),\n });\n }\n return map;\n };\n return id => (table ??= decode()).get(id);\n}\n\n// -------------------------------------------------------------------------------------------\n// Validator\n\nexport type PackIssue = { level: 'error' | 'warning'; key?: string; message: string };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** Checks the document envelope. Errors mean the pack is not applied at all. */\nexport function checkPackShape(input: unknown): PackIssue[] {\n const issues: PackIssue[] = [];\n const error = (message: string) => { issues.push({ level: 'error', message }); };\n if (!isRecord(input)) return [{ level: 'error', message: 'a string pack is a JSON object' }];\n if (input.format !== 1) error('\"format\" must be 1');\n for (const field of ['site', 'catalog'] as const) {\n if (typeof input[field] !== 'string' || !IDENT.test(input[field])) error(`\"${field}\" must be a short lowercase id`);\n }\n if (typeof input.language !== 'string' || !TAG.test(input.language)) error('\"language\" must be a language tag (en, pt-BR, en-x-pirate)');\n if (input.base !== undefined && (typeof input.base !== 'string' || !TAG.test(input.base))) error('\"base\" must be a language tag');\n if (typeof input.name !== 'string' || !input.name.trim() || input.name.length > STRING_PACK_LIMITS.name || CONTROL.test(input.name) || /[<>]/.test(input.name)) {\n error(`\"name\" must be 1 to ${STRING_PACK_LIMITS.name} characters of plain text`);\n }\n if (!Array.isArray(input.authors) || input.authors.length > STRING_PACK_LIMITS.authors\n || input.authors.some(author => typeof author !== 'string' || !author.trim() || author.length > STRING_PACK_LIMITS.name || CONTROL.test(author) || /[<>]/.test(author))) {\n error(`\"authors\" must be a list of at most ${STRING_PACK_LIMITS.authors} plain-text names`);\n }\n for (const field of ['version', 'description'] as const) {\n const value = input[field];\n if (value !== undefined && (typeof value !== 'string' || value.length > 400 || CONTROL.test(value) || /[<>]/.test(value))) error(`\"${field}\" must be plain text`);\n }\n if (!isRecord(input.strings)) error('\"strings\" must be an object of key id -> text');\n else if (Object.keys(input.strings).length > STRING_PACK_LIMITS.strings) error(`\"strings\" has more than ${STRING_PACK_LIMITS.strings} entries`);\n const known = new Set(['$schema', 'format', 'site', 'catalog', 'language', 'base', 'name', 'authors', 'version', 'description', 'strings']);\n for (const key of Object.keys(input)) if (!known.has(key)) issues.push({ level: 'warning', message: `unknown field \"${key}\" is ignored` });\n return issues;\n}\n\nconst TAG_TOKEN = /<\\/?([A-Za-z][A-Za-z0-9-]*)>/g;\n\n/** Tag rule: only the original's tag slots, each opened and closed; any other `<` or `>` is refused. */\nfunction checkTags(value: string, tags: readonly string[]): string | null {\n if (!value.includes('<') && !value.includes('>')) {\n return tags.length ? `the original's tags are missing: ${tags.map(tag => `<${tag}>`).join(' ')}` : null;\n }\n if (!tags.length) return 'markup is not allowed: a pack is text, < and > cannot be used';\n const open: string[] = [];\n const seen = new Set<string>();\n for (const match of value.matchAll(TAG_TOKEN)) {\n const name = match[1]!;\n if (!tags.includes(name)) return `<${name}> is not a tag of the original`;\n if (match[0].startsWith('</')) {\n if (open.pop() !== name) return `</${name}> does not close an open tag`;\n } else {\n open.push(name);\n seen.add(name);\n }\n }\n if (open.length) return `<${open[open.length - 1]}> is not closed`;\n if (/[<>]/.test(value.replace(TAG_TOKEN, ''))) return '< and > may only be used for the original\\'s tags';\n const missing = tags.filter(tag => !seen.has(tag));\n return missing.length ? `the original's tags are missing: ${missing.map(tag => `<${tag}>`).join(' ')}` : null;\n}\n\n/** Walks a parsed message against the key's shape; returns the first problem. */\nfunction checkParts(parts: Parts, shape: EntryShape): string | null {\n const params = shape.params ?? [];\n for (const part of parts) {\n if (typeof part === 'string' || part.kind === 'pound') continue;\n if (!params.includes(part.name)) {\n return params.length ? `{${part.name}} is not a parameter of the original (it has: ${params.join(', ')})` : `{${part.name}}: the original takes no parameters`;\n }\n if (part.kind === 'arg') {\n if (part.format) return `{${part.name}, ${part.format}} is not supported: only {name}, plural and select`;\n continue;\n }\n if (part.kind === 'plural') {\n if (part.ordinal) return `{${part.name}, selectordinal} is not supported: only {name}, plural and select`;\n if (part.offset) return `{${part.name}, plural, offset:…} is not supported`;\n if (!shape.plural?.includes(part.name)) return `{${part.name}} is not a plural of the original`;\n for (const selector of Object.keys(part.options)) {\n if (!CATEGORIES.has(selector) && !/^=\\d+$/.test(selector)) return `\"${selector}\" is not a plural form (zero, one, two, few, many, other, =N)`;\n }\n } else if (!shape.select?.includes(part.name)) {\n return `{${part.name}} is not a select of the original`;\n }\n for (const option of Object.values(part.options)) {\n const nested = checkParts(option, shape);\n if (nested) return nested;\n }\n }\n return null;\n}\n\n/**\n * Validates one pack value against the key's inventory entry. Returns the parsed message, or the\n * reason it is refused (the original then stays).\n */\nexport function validateString(shape: EntryShape, value: unknown): { parts: Parts } | { reason: string } {\n if (!translatable(shape.kind)) return { reason: `not translatable yet (${shape.kind})` };\n if (typeof value !== 'string') return { reason: 'the value must be text' };\n if (!value) return { reason: 'the value is empty' };\n if (value.length > STRING_PACK_LIMITS.value) return { reason: `longer than ${STRING_PACK_LIMITS.value} characters` };\n if (CONTROL.test(value)) return { reason: 'control or bidi-override characters are not allowed' };\n const tags = checkTags(value, shape.tags ?? []);\n if (tags) return { reason: tags };\n let parts: Parts;\n try {\n parts = parseMessage(value);\n } catch (error) {\n return { reason: `invalid message syntax: ${error instanceof Error ? error.message : String(error)}` };\n }\n const problem = checkParts(parts, shape.kind === 'message' ? shape : { kind: 'text' });\n return problem ? { reason: problem } : { parts };\n}\n\n// -------------------------------------------------------------------------------------------\n// Compiling a pack (validation against an inventory)\n\nexport type CompiledValue = { text: string } | { parts: Parts };\n\nexport type PackReport = {\n /** Entries in the pack. */\n total: number;\n /** Entries that are valid for an inventory key. */\n valid: number;\n /** Ids the inventory does not have: never applied. */\n unknown: string[];\n /** Values that failed validation: not applied, the original stays. */\n rejected: { id: string; reason: string }[];\n};\n\nexport type CompiledPack = {\n name: string;\n language: string;\n base?: string;\n site: string;\n catalog: string;\n values: ReadonlyMap<string, CompiledValue>;\n report: PackReport;\n};\n\n/** A message without arguments as its literal text (quotes resolved). */\nfunction literal(parts: Parts): string {\n return parts.map(part => (typeof part === 'string' ? part : '')).join('');\n}\n\n/**\n * Validates a pack against an inventory. Throws on a malformed envelope or a pack for another\n * site/catalog; every other problem is per key and lands in `report`.\n */\nexport function compilePack(input: unknown, inventory: StringInventory | PageInventory): CompiledPack {\n const shapeErrors = checkPackShape(input).filter(issue => issue.level === 'error');\n if (shapeErrors.length) throw new Error(`invalid string pack: ${shapeErrors.map(issue => issue.message).join('; ')}`);\n const pack = input as StringPack;\n if (pack.site !== inventory.site || pack.catalog !== inventory.catalog) {\n throw new Error(`the pack is for ${pack.site}/${pack.catalog}, the inventory is ${inventory.site}/${inventory.catalog}`);\n }\n const lookup = inventoryLookup(inventory);\n const values = new Map<string, CompiledValue>();\n const report: PackReport = { total: 0, valid: 0, unknown: [], rejected: [] };\n for (const [id, value] of Object.entries(pack.strings)) {\n report.total += 1;\n const shape = lookup(id);\n if (!shape) { report.unknown.push(id); continue; }\n const result = validateString(shape, value);\n if ('reason' in result) { report.rejected.push({ id, reason: result.reason }); continue; }\n values.set(id, shape.kind === 'text' ? { text: literal(result.parts) } : { parts: result.parts });\n report.valid += 1;\n }\n return { name: pack.name, language: pack.language, base: pack.base, site: pack.site, catalog: pack.catalog, values, report };\n}\n\n// -------------------------------------------------------------------------------------------\n// Interpreting a message (closures over the parsed parts; nothing is evaluated)\n\nconst rulesCache = new Map<string, Intl.PluralRules | null>();\n\n/** Plural rules of the first language the runtime knows; `null` = every number is `other`. */\nfunction rulesFor(languages: readonly (string | undefined)[]): Intl.PluralRules | null {\n const key = languages.join('|');\n let rules = rulesCache.get(key);\n if (rules !== undefined) return rules;\n rules = null;\n for (const language of languages) {\n if (!language) continue;\n const tag = language.split(/-x-/i)[0]!;\n try {\n if (Intl.PluralRules.supportedLocalesOf([tag]).length) { rules = new Intl.PluralRules(tag); break; }\n } catch {\n // Not a tag this runtime accepts: try the next.\n }\n }\n rulesCache.set(key, rules);\n return rules;\n}\n\n/**\n * Renders a validated message. A parameter is inserted the way string concatenation would\n * (`String(value)`, nothing for a missing one); `#` is the plural's own value.\n */\nexport function renderMessage(parts: Parts, params: Readonly<Record<string, unknown>>, languages: readonly (string | undefined)[], pound: unknown = undefined): string {\n let out = '';\n for (const part of parts) {\n if (typeof part === 'string') { out += part; continue; }\n // The parser only yields `#` inside a plural, so there is always a value it stands for.\n if (part.kind === 'pound') { out += pound === undefined || pound === null ? '' : String(pound); continue; }\n const value = params[part.name];\n if (part.kind === 'arg') { out += value === undefined || value === null ? '' : String(value); continue; }\n if (part.kind === 'select') {\n out += renderMessage(part.options[String(value)] ?? part.options.other!, params, languages, pound);\n continue;\n }\n const n = typeof value === 'number' ? value : Number(value);\n const option = Number.isFinite(n)\n ? part.options[`=${n}`] ?? part.options[rulesFor(languages)?.select(n) ?? 'other'] ?? part.options.other!\n : part.options.other!;\n out += renderMessage(option, params, languages, value);\n }\n return out;\n}\n\n// -------------------------------------------------------------------------------------------\n// The ordered application engine\n\nexport type EngineStats = {\n /** Keys the site registered. */\n seen: number;\n /** Keys a pack value was used for. */\n applied: number;\n /** Keys the site registered that the inventory does not have (new keys: drift). */\n unlisted: string[];\n /** Keys whose live value does not have the inventory's kind: left untouched. */\n mismatched: string[];\n};\n\nexport type StringEngine = {\n /**\n * The value to use for `id`, given the site's own value: a pack's, or exactly `original`.\n * Feeding a value this engine produced back in returns the same value (idempotent).\n */\n resolve(id: string, original: unknown): unknown;\n /** The site's own values of the keys a pack changed: what restoring needs. */\n originals(): ReadonlyMap<string, unknown>;\n stats(): EngineStats;\n};\n\nconst MAX_LISTED = 200;\n\n/** `packs` in application order: a later pack wins over an earlier one for the same key. */\nexport function createStringEngine(inventory: StringInventory | PageInventory | InventoryLookup, packs: readonly CompiledPack[]): StringEngine {\n const lookup = typeof inventory === 'function' ? inventory : inventoryLookup(inventory);\n // A slice only knows the packs' keys: what it lacks is not a new key of the site.\n const whole = typeof inventory === 'function' || !('partial' in inventory && inventory.partial);\n const originals = new Map<string, unknown>();\n const applied = new Map<string, unknown>();\n const produced = new WeakMap<object, unknown>();\n const seen = new Set<string>();\n const unlisted = new Set<string>();\n const mismatched = new Set<string>();\n\n const winner = (id: string): { pack: CompiledPack; value: CompiledValue } | null => {\n for (let index = packs.length - 1; index >= 0; index -= 1) {\n const value = packs[index]!.values.get(id);\n if (value) return { pack: packs[index]!, value };\n }\n return null;\n };\n\n const resolve = (id: string, incoming: unknown): unknown => {\n let original = incoming;\n // A value of ours coming back in (a second application): work from the site's own value again.\n if (typeof incoming === 'function' && produced.has(incoming)) original = produced.get(incoming);\n else if (typeof incoming === 'string' && applied.has(id) && applied.get(id) === incoming) original = originals.get(id);\n seen.add(id);\n const found = winner(id);\n if (!found) {\n if (whole && unlisted.size < MAX_LISTED && !lookup(id)) unlisted.add(id);\n return original;\n }\n const shape = lookup(id);\n if (!shape || !translatable(shape.kind)) return original;\n if (shape.kind === 'text' ? typeof original !== 'string' : typeof original !== 'function') {\n if (mismatched.size < MAX_LISTED) mismatched.add(id);\n return original;\n }\n if (originals.has(id) && originals.get(id) === original) return applied.get(id);\n let next: unknown;\n if ('text' in found.value) {\n next = found.value.text;\n } else {\n const parts = found.value.parts;\n const languages = [found.pack.language, found.pack.base];\n const message = (params?: unknown): string => renderMessage(parts, isRecord(params) ? params : {}, languages);\n produced.set(message, original);\n next = message;\n }\n originals.set(id, original);\n applied.set(id, next);\n return next;\n };\n\n return {\n resolve,\n originals: () => originals,\n stats: () => ({ seen: seen.size, applied: applied.size, unlisted: [...unlisted], mismatched: [...mismatched] }),\n };\n}\n\n/**\n * The whole catalog at once: a new object with the packs applied; `catalog` itself is not\n * changed. `applyPacks(applyPacks(c, …), …)` with one engine equals `applyPacks(c, …)`.\n */\nexport function applyPacks(catalog: Readonly<Record<string, unknown>>, engine: StringEngine): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const id of Object.keys(catalog)) out[id] = engine.resolve(id, catalog[id]);\n return out;\n}\n\n/** The inverse: every value a pack changed is the site's own again; `catalog` is not changed. */\nexport function restoreCatalog(catalog: Readonly<Record<string, unknown>>, engine: StringEngine): Record<string, unknown> {\n const originals = engine.originals();\n const out: Record<string, unknown> = {};\n for (const id of Object.keys(catalog)) out[id] = originals.has(id) ? originals.get(id) : catalog[id];\n return out;\n}\n\n// -------------------------------------------------------------------------------------------\n// Coverage\n\nexport type Coverage = {\n /** Keys a pack can replace (text and messages). */\n translatable: number;\n /** Of those, keys the pack has a valid value for. */\n translated: number;\n /** Keys that exist but cannot be replaced by data yet (rich text, formatters). */\n closed: number;\n};\n\n/** Coverage of a compiled pack per group of the inventory, plus `all`. */\nexport function coverage(inventory: StringInventory, pack: Pick<CompiledPack, 'values'>): Record<string, Coverage> {\n const out: Record<string, Coverage> = { all: { translatable: 0, translated: 0, closed: 0 } };\n for (const [id, entry] of Object.entries(inventory.entries)) {\n const group = (out[entry.group ?? MAIN_GROUP] ??= { translatable: 0, translated: 0, closed: 0 });\n for (const bucket of [group, out.all!]) {\n if (!translatable(entry.kind)) { bucket.closed += 1; continue; }\n bucket.translatable += 1;\n if (pack.values.has(id)) bucket.translated += 1;\n }\n }\n return out;\n}\n\n// -------------------------------------------------------------------------------------------\n// Drift (the site shipped a new build)\n\nexport type DriftReport = {\n /** Keys the site has that the inventory lacks. */\n added: string[];\n /** Keys whose base-language value changed (same shape). */\n reworded: string[];\n /** Keys whose kind or parameters changed: pack values for them have to be checked again. */\n reshaped: string[];\n /** Inventory keys the site no longer has. Only for groups whose base catalog was loaded. */\n vanished: string[];\n};\n\nconst sameList = (a: readonly string[] = [], b: readonly string[] = []) => a.length === b.length && [...a].sort().join('\\n') === [...b].sort().join('\\n');\n\n/**\n * Compares an inventory with what was observed now. `loaded` names the groups whose base catalog\n * was actually read: a key can only be called vanished when its group was (a lazily loaded group\n * that never arrived says nothing about its keys).\n */\nexport function driftReport(inventory: StringInventory, observed: Readonly<Record<string, InventoryEntry>>, loaded: readonly string[]): DriftReport {\n const report: DriftReport = { added: [], reworded: [], reshaped: [], vanished: [] };\n for (const [id, now] of Object.entries(observed)) {\n const before = Object.prototype.hasOwnProperty.call(inventory.entries, id) ? inventory.entries[id] : undefined;\n if (!before) { report.added.push(id); continue; }\n if (before.kind !== now.kind || !sameList(before.params, now.params) || !sameList(before.plural, now.plural) || !sameList(before.select, now.select) || !sameList(before.tags, now.tags)) {\n report.reshaped.push(id);\n } else if (before.hash !== now.hash) {\n report.reworded.push(id);\n }\n }\n const groups = new Set(loaded);\n for (const [id, entry] of Object.entries(inventory.entries)) {\n if (!Object.prototype.hasOwnProperty.call(observed, id) && groups.has(entry.group ?? MAIN_GROUP)) report.vanished.push(id);\n }\n return report;\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type RootStyles = {
|
|
2
|
+
/** The live sheet (insertRule/deleteRule work on it), or null when nothing could be parsed. */
|
|
3
|
+
readonly sheet: CSSStyleSheet | null;
|
|
4
|
+
/** True when the sheet is adopted (CSP-proof); false for the `<style>` fallback. */
|
|
5
|
+
readonly adopted: boolean;
|
|
6
|
+
/** Replaces the rules. */
|
|
7
|
+
update(css: string): void;
|
|
8
|
+
/** Takes the rules off the root (idempotent). */
|
|
9
|
+
remove(): void;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Styles `root` with `css`. Adopted sheets come after the root's `<style>` elements in the cascade
|
|
13
|
+
* (like a `<style>` appended last), and several calls stack in call order.
|
|
14
|
+
*/
|
|
15
|
+
export declare function adoptStyles(root: ShadowRoot | Document, css: string): RootStyles;
|