textbrowser 0.54.5 → 0.54.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGES.md +8 -0
  2. package/dist/eslint.config.d.ts +3 -0
  3. package/dist/eslint.config.d.ts.map +1 -0
  4. package/dist/index-es.js +10 -4
  5. package/dist/index-es.min.js +1 -1
  6. package/dist/resources/activateCallback.d.ts +23 -0
  7. package/dist/resources/activateCallback.d.ts.map +1 -0
  8. package/dist/resources/index.d.ts +128 -0
  9. package/dist/resources/index.d.ts.map +1 -0
  10. package/dist/resources/resultsDisplay.d.ts +110 -0
  11. package/dist/resources/resultsDisplay.d.ts.map +1 -0
  12. package/dist/resources/templates/index.d.ts +59 -0
  13. package/dist/resources/templates/index.d.ts.map +1 -0
  14. package/dist/resources/templates/languageSelect.d.ts +18 -0
  15. package/dist/resources/templates/languageSelect.d.ts.map +1 -0
  16. package/dist/resources/templates/resultsDisplayClient.d.ts +26 -0
  17. package/dist/resources/templates/resultsDisplayClient.d.ts.map +1 -0
  18. package/dist/resources/templates/resultsDisplayServerOrClient.d.ts +131 -0
  19. package/dist/resources/templates/resultsDisplayServerOrClient.d.ts.map +1 -0
  20. package/dist/resources/templates/utils/dom.d.ts +15 -0
  21. package/dist/resources/templates/utils/dom.d.ts.map +1 -0
  22. package/dist/resources/templates/utils/html.d.ts +3 -0
  23. package/dist/resources/templates/utils/html.d.ts.map +1 -0
  24. package/dist/resources/templates/workDisplay.d.ts +183 -0
  25. package/dist/resources/templates/workDisplay.d.ts.map +1 -0
  26. package/dist/resources/templates/workSelect.d.ts +18 -0
  27. package/dist/resources/templates/workSelect.d.ts.map +1 -0
  28. package/dist/resources/utils/IntlURLSearchParams.d.ts +56 -0
  29. package/dist/resources/utils/IntlURLSearchParams.d.ts.map +1 -0
  30. package/dist/resources/utils/Languages.d.ts +81 -0
  31. package/dist/resources/utils/Languages.d.ts.map +1 -0
  32. package/dist/resources/utils/Metadata.d.ts +133 -0
  33. package/dist/resources/utils/Metadata.d.ts.map +1 -0
  34. package/dist/resources/utils/Params.d.ts +30 -0
  35. package/dist/resources/utils/Params.d.ts.map +1 -0
  36. package/dist/resources/utils/Plugin.d.ts +273 -0
  37. package/dist/resources/utils/Plugin.d.ts.map +1 -0
  38. package/dist/resources/utils/ServiceWorker.d.ts +26 -0
  39. package/dist/resources/utils/ServiceWorker.d.ts.map +1 -0
  40. package/dist/resources/utils/WorkInfo.d.ts +106 -0
  41. package/dist/resources/utils/WorkInfo.d.ts.map +1 -0
  42. package/dist/resources/utils/dialogs.d.ts +155 -0
  43. package/dist/resources/utils/dialogs.d.ts.map +1 -0
  44. package/dist/resources/utils/getLocaleFallbackResults.d.ts +19 -0
  45. package/dist/resources/utils/getLocaleFallbackResults.d.ts.map +1 -0
  46. package/dist/resources/utils/sanitize.d.ts +6 -0
  47. package/dist/resources/utils/sanitize.d.ts.map +1 -0
  48. package/dist/resources/workDisplay.d.ts +81 -0
  49. package/dist/resources/workDisplay.d.ts.map +1 -0
  50. package/dist/resources/workSelect.d.ts +17 -0
  51. package/dist/resources/workSelect.d.ts.map +1 -0
  52. package/dist/rollup.config.d.ts +19 -0
  53. package/dist/rollup.config.d.ts.map +1 -0
  54. package/dist/server/main.d.ts +47 -0
  55. package/dist/server/main.d.ts.map +1 -0
  56. package/dist/sw-helper.js +355 -0
  57. package/package.json +1 -1
  58. package/resources/index.js +8 -4
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @typedef {() => Promise<{
3
+ * groupName: string | Text | DocumentFragment,
4
+ * worksToFields: {
5
+ * workName: string | Text | DocumentFragment,
6
+ * shortcut: string,
7
+ * fieldAliasOrNames: (
8
+ * string|string[]|import('../server/main.js').LocalizationStrings
9
+ * )[]|undefined
10
+ * }[]
11
+ * }[]>} GetFieldAliasOrNames
12
+ */
13
+ /**
14
+ * @typedef {(innerArg: {
15
+ * form: HTMLFormElement,
16
+ * random: {
17
+ * checked: boolean
18
+ * },
19
+ * checkboxes: HTMLInputElement[],
20
+ * type: string,
21
+ * fieldAliasOrNames?: string[],
22
+ * workName?: string,
23
+ * }) => string} SerializeParamsAsURL
24
+ */
25
+ /**
26
+ * @typedef {(
27
+ * key: string|string[],
28
+ * substitutions?: Record<string, string>
29
+ * ) => string|Text|DocumentFragment} LDirectional
30
+ */
31
+ /**
32
+ * @typedef {(
33
+ * key: string,
34
+ * tag: string,
35
+ * attr: string,
36
+ * atts: import('jamilih').JamilihAttributes,
37
+ * children?: import('jamilih').JamilihChildren
38
+ * ) => import('jamilih').JamilihArray} LElement
39
+ */
40
+ /**
41
+ * @this {import('./index.js').default}
42
+ * @param {{
43
+ * l: import('intl-dom').I18NCallback,
44
+ * languageParam: string,
45
+ * lang: string[],
46
+ * preferredLocale: string,
47
+ * languages: import('./utils/Languages.js').Languages,
48
+ * fallbackLanguages: string[],
49
+ * $p: import('./utils/IntlURLSearchParams.js').default
50
+ * }} cfg
51
+ */
52
+ export default function workDisplay(this: import("./index.js").default, { l, languageParam, lang, preferredLocale, languages, fallbackLanguages, $p }: {
53
+ l: import("intl-dom").I18NCallback;
54
+ languageParam: string;
55
+ lang: string[];
56
+ preferredLocale: string;
57
+ languages: import("./utils/Languages.js").Languages;
58
+ fallbackLanguages: string[];
59
+ $p: import("./utils/IntlURLSearchParams.js").default;
60
+ }): Promise<void>;
61
+ export type GetFieldAliasOrNames = () => Promise<{
62
+ groupName: string | Text | DocumentFragment;
63
+ worksToFields: {
64
+ workName: string | Text | DocumentFragment;
65
+ shortcut: string;
66
+ fieldAliasOrNames: (string | string[] | import("../server/main.js").LocalizationStrings)[] | undefined;
67
+ }[];
68
+ }[]>;
69
+ export type SerializeParamsAsURL = (innerArg: {
70
+ form: HTMLFormElement;
71
+ random: {
72
+ checked: boolean;
73
+ };
74
+ checkboxes: HTMLInputElement[];
75
+ type: string;
76
+ fieldAliasOrNames?: string[];
77
+ workName?: string;
78
+ }) => string;
79
+ export type LDirectional = (key: string | string[], substitutions?: Record<string, string>) => string | Text | DocumentFragment;
80
+ export type LElement = (key: string, tag: string, attr: string, atts: import("jamilih").JamilihAttributes, children?: import("jamilih").JamilihChildren) => import("jamilih").JamilihArray;
81
+ //# sourceMappingURL=workDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workDisplay.d.ts","sourceRoot":"","sources":["../../resources/workDisplay.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AAEH;;;;;;;;GAQG;AAEH;;;;;;;;;;;GAWG;AACH,uJAVW;IACN,CAAC,EAAE,OAAO,UAAU,EAAE,YAAY,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,sBAAsB,EAAE,SAAS,CAAC;IACpD,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,EAAE,EAAE,OAAO,gCAAgC,EAAE,OAAO,CAAA;CACrD,iBAwOH;mCA5RY,MAAM,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,gBAAgB,CAAC;IAC5C,aAAa,EAAE;QACb,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,gBAAgB,CAAC;QAC3C,QAAQ,EAAE,MAAM,CAAC;QACjB,iBAAiB,EAAE,CACzB,MAAc,GAAC,MAAM,EAAE,GAAC,OAAO,mBAAmB,EAAE,mBAAmB,CAChE,EAAE,GAAC,SAAS,CAAA;KACd,EAAE,CAAA;CACJ,EAAE,CAAC;mCAIM,CAAC,QAAQ,EAAE;IACnB,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE;QACN,OAAO,EAAE,OAAO,CAAA;KACjB,CAAC;IACF,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,KAAK,MAAM;2BAIF,CACR,GAAG,EAAE,MAAM,GAAC,MAAM,EAAE,EACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACnC,MAAM,GAAC,IAAI,GAAC,gBAAgB;uBAIvB,CACR,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,SAAS,EAAE,iBAAiB,EACzC,QAAQ,CAAC,EAAE,OAAO,SAAS,EAAE,eAAe,KACzC,OAAO,SAAS,EAAE,YAAY"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @param {{
3
+ * files: string,
4
+ * lang: string[],
5
+ * fallbackLanguages: string[],
6
+ * $p: import('./utils/IntlURLSearchParams.js').default,
7
+ * followParams: (formSelector: string, cb: () => void) => void
8
+ * }} cfg
9
+ */
10
+ export default function workSelect({ files, lang, fallbackLanguages, $p, followParams }: {
11
+ files: string;
12
+ lang: string[];
13
+ fallbackLanguages: string[];
14
+ $p: import("./utils/IntlURLSearchParams.js").default;
15
+ followParams: (formSelector: string, cb: () => void) => void;
16
+ }): Promise<void>;
17
+ //# sourceMappingURL=workSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workSelect.d.ts","sourceRoot":"","sources":["../../resources/workSelect.js"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AACH,yFARW;IACN,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,EAAE,EAAE,OAAO,gCAAgC,EAAE,OAAO,CAAC;IACrD,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAA;CAC7D,iBAiFH"}
@@ -0,0 +1,19 @@
1
+ declare const _default: (import("rollup").RollupOptions | {
2
+ input: string;
3
+ output: {
4
+ format: string;
5
+ file: string;
6
+ name: string;
7
+ };
8
+ plugins: any[];
9
+ } | {
10
+ input: string;
11
+ output: {
12
+ format: string;
13
+ file: string;
14
+ name: string;
15
+ };
16
+ plugins?: undefined;
17
+ })[];
18
+ export default _default;
19
+ //# sourceMappingURL=rollup.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollup.config.d.ts","sourceRoot":"","sources":["../rollup.config.js"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ export type UserOptions = {
3
+ nodeActivate: boolean;
4
+ port: number;
5
+ domain: string;
6
+ basePath: string;
7
+ interlinearSeparator: string;
8
+ localizeParamNames: boolean;
9
+ trustFormatHTML: boolean;
10
+ allowPlugins: boolean;
11
+ showEmptyInterlinear: boolean;
12
+ showTitleOnSingleInterlinear: boolean;
13
+ serviceWorkerPath: string;
14
+ userJSON: string;
15
+ languages: string;
16
+ files: string;
17
+ namespace: string;
18
+ httpServer: string;
19
+ expressServer: string;
20
+ };
21
+ export type AnyValue = any;
22
+ export type ResultsDisplayServerContext = import("../resources/utils/ServiceWorker.js").ServiceWorkerConfig & UserOptions & {
23
+ lang: string[];
24
+ langs: LanguageInfo[];
25
+ fallbackLanguages: string[];
26
+ log: (...args: AnyValue[]) => void;
27
+ nodeActivate?: boolean;
28
+ port?: number;
29
+ skipIndexedDB: false;
30
+ noDynamic: false;
31
+ };
32
+ export type LanguageInfo = {
33
+ code: string;
34
+ direction: "ltr" | "rtl";
35
+ locale: {
36
+ $ref: string;
37
+ };
38
+ };
39
+ export type LocalizationStrings = {
40
+ [key: string]: string | string[] | LocalizationStrings;
41
+ };
42
+ export type LanguagesData = {
43
+ languages: LanguageInfo[];
44
+ localeFileBasePath?: string;
45
+ "localization-strings": LocalizationStrings;
46
+ };
47
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../server/main.js"],"names":[],"mappings":";0BAqCa;IACR,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,4BAA4B,EAAE,OAAO,CAAC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAA;CACtB;uBA2CS,GAAG;0CAKH,OAAO,qCAAqC,EAAE,mBAAmB,GACzE,WAAW,GAAG;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,KAAK,CAAC;IACrB,SAAS,EAAE,KAAK,CAAC;CAClB;2BAwDO;IACR,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,KAAK,GAAC,KAAK,CAAC;IACvB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF;kCAIS;IACZ,CAAK,GAAG,EAAE,MAAM,GAAG,MAAM,GAAC,MAAM,EAAE,GAAC,mBAAmB,CAAA;CACnD;4BAIS;IACR,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAChC,sBAA0B,EAAE,mBAAmB,CAAA;CAC5C"}
@@ -0,0 +1,355 @@
1
+ import activateCallback from './activateCallback-es.js';
2
+ import {getWorkFiles} from './WorkInfo-es.js';
3
+
4
+ // VERSION 0.1.0
5
+
6
+ const CURRENT_CACHES = {
7
+ prefetch: 'prefetch-cache-v'
8
+ };
9
+ const minutes = 60 * 1000;
10
+
11
+ // UTILITIES
12
+
13
+ /**
14
+ * @typedef {number} PositiveInteger
15
+ */
16
+
17
+ /**
18
+ * @typedef {number} Float
19
+ */
20
+
21
+ /**
22
+ * @param {string} url
23
+ */
24
+ async function getJSON (url) {
25
+ const resp = await fetch(url);
26
+ return await resp.json();
27
+ }
28
+
29
+ /**
30
+ * @param {ServiceWorkerGlobalScope} self
31
+ */
32
+ function swHelper (self) {
33
+ // These could be made configurable by argument too
34
+ const defaultUserStaticFiles = [
35
+ '/', // Needs a separate entry from `index.html` (at least in Chrome)
36
+ 'index.html',
37
+ 'files.json',
38
+ 'site.json',
39
+ 'resources/user.js'
40
+ // We do not put the user.json here as that is obtained live with this
41
+ // service worker via `pathToUserJSON`
42
+ ];
43
+ // Todo: We could supply `new URL(fileName, moduleURL).href` to
44
+ // get these as reliable full paths without hard-coding or needing to
45
+ // actually be in `node_modules/textbrowser`; see `resources/index.js`
46
+ const textbrowserStaticResourceFiles = [
47
+ 'node_modules/textbrowser/appdata/languages.json',
48
+
49
+ /*
50
+ // Only needed atm for browser validation
51
+ 'node_modules/textbrowser/general-schemas/files.jsonschema',
52
+ 'node_modules/textbrowser/general-schemas/languages.jsonschema',
53
+ 'node_modules/textbrowser-data-schemas/schemas/locale.jsonschema',
54
+ 'node_modules/textbrowser-data-schemas/schemas/metadata.jsonschema',
55
+ 'node_modules/textbrowser-data-schemas/schemas/table.jsonschema', // Not currently using for validation or meta-data
56
+ */
57
+
58
+ 'node_modules/intl-locale-textinfo-polyfill/lib/Locale.js',
59
+ 'node_modules/textbrowser/dist/assets/languages-1sAACTKG.json',
60
+ 'node_modules/textbrowser/dist/assets/index-_11XnUty.css',
61
+
62
+ 'node_modules/textbrowser/dist/index-es.js'
63
+ ];
64
+
65
+ /**
66
+ * @typedef {object} ConfigObject
67
+ * @property {string} namespace
68
+ * @property {string} basePath
69
+ * @property {string} languages
70
+ * @property {string} files
71
+ * @property {string[]} userStaticFiles
72
+ */
73
+
74
+ /**
75
+ *
76
+ * @param {Partial<ConfigObject>} args
77
+ * @returns {Required<ConfigObject>}
78
+ * @todo Since some of these reused, move to external file (or
79
+ * use `setServiceWorkerDefaults`?)
80
+ */
81
+ function getConfigDefaults (args) {
82
+ return {
83
+ namespace: 'textbrowser',
84
+ basePath: '',
85
+ languages: new URL('../appdata/languages.json', import.meta.url).href,
86
+ files: 'files.json',
87
+ userStaticFiles: defaultUserStaticFiles,
88
+ // Opportunity to override
89
+ ...args
90
+ };
91
+ }
92
+
93
+ /**
94
+ *
95
+ * @param {string[]} messages
96
+ * @returns {Promise<void>}
97
+ */
98
+ function log (...messages) {
99
+ const message = messages.join(' ');
100
+ console.log(message);
101
+ return post({message, type: 'log'});
102
+ }
103
+
104
+ /**
105
+ *
106
+ * @param {Error} error
107
+ * @param {string[]} messages
108
+ * @returns {Promise<void>} Resolves to `undefined`
109
+ */
110
+ function logError (error, ...messages) {
111
+ const message = messages.join(' ');
112
+ console.error(error, message);
113
+ return post({
114
+ message,
115
+ // errorType: error.type,
116
+ // name: error.name,
117
+ type: 'error'
118
+ });
119
+ }
120
+
121
+ /**
122
+ * @callback DelayCallback
123
+ * @param {Float} time
124
+ * @returns {void}
125
+ */
126
+
127
+ /**
128
+ *
129
+ * @param {DelayCallback} cb
130
+ * @param {PositiveInteger} timeout
131
+ * @param {string} errMessage
132
+ * @param {PositiveInteger} [time]
133
+ * @returns {Promise<void>}
134
+ */
135
+ async function tryAndRetry (cb, timeout, errMessage, time = 0) {
136
+ time++;
137
+ try {
138
+ await cb(time); // eslint-disable-line n/callback-return -- Needed for retries
139
+ return undefined;
140
+ } catch (err) {
141
+ console.log('errrr', err);
142
+ logError(
143
+ /** @type {Error} */
144
+ (err),
145
+ /** @type {Error} */
146
+ (err).message || errMessage
147
+ );
148
+ return new Promise((resolve) => {
149
+ setTimeout(() => {
150
+ resolve(tryAndRetry(cb, timeout, errMessage, time));
151
+ }, timeout);
152
+ });
153
+ }
154
+ }
155
+
156
+ /**
157
+ *
158
+ * @param {object} args
159
+ * @param {"log"|"error"|"beginInstall"|"finishedInstall"|"beginActivate"|"finishedActivate"} args.type
160
+ * @param {string} [args.message]
161
+ * @returns {Promise<void>}
162
+ */
163
+ async function post ({type, message = type}) {
164
+ const clients = await self.clients.matchAll({
165
+ // Are there any uncontrolled within activate anyways?
166
+ includeUncontrolled: true,
167
+ type: 'window'
168
+ }) || [];
169
+ if (message.includes('Posting finished')) {
170
+ message += ` (count: ${clients.length})`;
171
+ }
172
+ clients.forEach((client) => {
173
+ // Although we only need one client to which to send
174
+ // arguments, we want to signal phase complete to all
175
+ // eslint-disable-next-line unicorn/require-post-message-target-origin -- In worker
176
+ client.postMessage({message, type});
177
+ });
178
+ }
179
+
180
+ /**
181
+ *
182
+ * @param {PositiveInteger} time
183
+ * @throws {Error}
184
+ * @returns {Promise<void>}
185
+ */
186
+ async function install (time) {
187
+ post({type: 'beginInstall'});
188
+ log(`Install: Trying, attempt ${time}`);
189
+ const now = Date.now();
190
+ const json = await getJSON(/** @type {string} */ (pathToUserJSON));
191
+
192
+ const {
193
+ namespace, languages, files, userStaticFiles
194
+ } = getConfigDefaults(json);
195
+
196
+ const {version} = await getJSON('./package.json');
197
+
198
+ console.log('opening cache', namespace + CURRENT_CACHES.prefetch + version);
199
+ const [
200
+ cache,
201
+ userDataFiles,
202
+ {languages: langs}
203
+ ] = await Promise.all([
204
+ caches.open(namespace + CURRENT_CACHES.prefetch + version),
205
+ getWorkFiles(files),
206
+ getJSON(languages)
207
+ ]);
208
+ log('Install: Retrieved dependency values');
209
+
210
+ const langPathParts = languages.split('/');
211
+
212
+ // Todo: Ought to make these locales only conditionally required and
213
+ // then only show those specified in languages menu or go directly
214
+ // to work selection
215
+ // Todo: We might give option to only download
216
+ // one locale and avoid language splash page
217
+ const localeFiles =
218
+ /**
219
+ * @type {{
220
+ * code: string,
221
+ * direction: "rtl"|"ltr",
222
+ * locale: {$ref: string}
223
+ * }[]}
224
+ */ (langs).map(
225
+ ({locale: {$ref}}) => {
226
+ return (langPathParts.length > 1
227
+ ? langPathParts.slice(0, -1).join('/') + '/'
228
+ : ''
229
+ ) + $ref;
230
+ }
231
+ );
232
+
233
+ const urlsToPrefetch = [
234
+ ...textbrowserStaticResourceFiles,
235
+ ...localeFiles,
236
+ ...userStaticFiles,
237
+ ...userDataFiles,
238
+ ...stylesheets
239
+ ];
240
+ // .map((url) => url === 'index.html' ? new Request(url, {cache: 'reload'}) : url)
241
+ try {
242
+ const cachePromises = urlsToPrefetch.map(async (urlToPrefetch) => {
243
+ // This constructs a new URL object using the service worker's script
244
+ // location as the base for relative URLs.
245
+ const url = new URL(urlToPrefetch, location.href);
246
+ url.search += (url.search ? '&' : '?') + 'cache-bust=' + now;
247
+ const request = new Request(url, {mode: 'no-cors'});
248
+ try {
249
+ const response = await fetch(request);
250
+ if (response.status >= 400) {
251
+ throw new Error('request for ' + urlToPrefetch +
252
+ ' failed with status ' + response.statusText);
253
+ }
254
+ return cache.put(urlToPrefetch, response);
255
+ } catch (error) {
256
+ logError(
257
+ /** @type {Error} */
258
+ (error),
259
+ `Not caching ${urlToPrefetch} due to ${error}`
260
+ );
261
+ throw error;
262
+ }
263
+ });
264
+ await Promise.all(cachePromises);
265
+ log('Install: Pre-fetching complete.');
266
+ } catch (error) {
267
+ logError(
268
+ /** @type {Error} */
269
+ (error),
270
+ `Install: Pre-fetching failed: ${error}`
271
+ );
272
+ // Failing gives chance for a new client to re-trigger install?
273
+ throw error;
274
+ }
275
+
276
+ // An install update event will not be reported until controlled,
277
+ // so we need to inform the clients
278
+ log(`Install: Posting finished message to clients`);
279
+
280
+ // Although we only need one client to which to send
281
+ // arguments, we want to signal phase complete to all
282
+ post({type: 'finishedInstall'});
283
+ }
284
+
285
+ /**
286
+ *
287
+ * @param {PositiveInteger} time
288
+ * @returns {Promise<void>}
289
+ */
290
+ async function activate (time) {
291
+ post({type: 'beginActivate'});
292
+ log(`Activate: Trying, attempt ${time}`);
293
+ const [json, cacheNames] = await Promise.all([
294
+ getJSON(/** @type {string} */ (pathToUserJSON)),
295
+ caches.keys()
296
+ ]);
297
+ const {namespace, files, basePath} = getConfigDefaults(json);
298
+ const {version} = await getJSON('./package.json');
299
+
300
+ const expectedCacheNames = Object.values(CURRENT_CACHES).map((n) => namespace + n + version);
301
+ cacheNames.map(async (cacheName) => {
302
+ if (!expectedCacheNames.includes(cacheName)) {
303
+ log('Activate: Deleting out of date cache:', cacheName);
304
+ await caches.delete(cacheName);
305
+ }
306
+ });
307
+
308
+ await activateCallback({namespace, files, basePath, log});
309
+ log('Activate: Database changes completed');
310
+
311
+ log(`Activate: Posting finished message to clients`);
312
+ // Signal phase complete to all clients
313
+ post({type: 'finishedActivate'});
314
+ }
315
+
316
+ // @ts-expect-error Ok
317
+ const params = new URL(location).searchParams;
318
+ const pathToUserJSON = params.get('pathToUserJSON');
319
+ const stylesheets = JSON.parse(params.get('stylesheets') || '[]');
320
+
321
+ console.log('sw info', pathToUserJSON);
322
+ console.log('sw stylesheets', stylesheets);
323
+
324
+ self.addEventListener('install', (e) => {
325
+ self.skipWaiting();
326
+ e.waitUntil(tryAndRetry(install, 5 * minutes, 'Error installing'));
327
+ });
328
+
329
+ self.addEventListener('activate', (e) => {
330
+ // Erring is of no present use here:
331
+ // https://github.com/w3c/ServiceWorker/issues/659#issuecomment-384919053
332
+ e.waitUntil(tryAndRetry(activate, 5 * minutes, 'Error activating'));
333
+ });
334
+
335
+ // We cannot make this async as `e.respondWith` must be called synchronously
336
+ self.addEventListener('fetch', (e) => {
337
+ // DevTools opening will trigger these o-i-c requests
338
+ const {request} = e;
339
+ const {cache, mode, url} = request;
340
+ if (cache === 'only-if-cached' &&
341
+ mode !== 'same-origin') {
342
+ return;
343
+ }
344
+ console.log('fetching', url);
345
+ e.respondWith((async () => {
346
+ const cached = await caches.match(request);
347
+ if (!cached) {
348
+ console.log('no cached found', url);
349
+ }
350
+ return cached || fetch(request);
351
+ })());
352
+ });
353
+ }
354
+
355
+ export default swHelper;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "textbrowser",
3
- "version": "0.54.5",
3
+ "version": "0.54.7",
4
4
  "description": "Multilinear text browser",
5
5
  "type": "module",
6
6
  "main": "dist/index-es.min.js",
@@ -477,10 +477,14 @@ class TextBrowser {
477
477
  } else {
478
478
  const worker = r.installing || r.waiting || r.active;
479
479
  if (!worker) {
480
- // Todo: Why wouldn't there be a worker here?
481
- console.error('Unexpected error: worker registration received without a worker.');
482
- // If anything, would probably need to register though
483
- await register();
480
+ console.warn('Worker registration returned with no worker; retrying registration.');
481
+ try {
482
+ await r.unregister();
483
+ } catch (err) {
484
+ console.warn('Unregister failed during retry', err);
485
+ }
486
+ Templates.permissions.main({siteI18n});
487
+ await prepareForServiceWorker.call(this);
484
488
  return;
485
489
  }
486
490