textbrowser 0.50.0 → 0.52.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.
Files changed (106) hide show
  1. package/.ncurc.cjs +3 -0
  2. package/CHANGES.md +13 -0
  3. package/dist/WorkInfo-es.js +655 -165
  4. package/dist/activateCallback-es.js +81 -10
  5. package/dist/dist/WorkInfo-es.d.ts +331 -0
  6. package/dist/dist/WorkInfo-es.d.ts.map +1 -0
  7. package/dist/dist/activateCallback-es.d.ts +17 -0
  8. package/dist/dist/activateCallback-es.d.ts.map +1 -0
  9. package/dist/dist/index-es.d.ts +619 -0
  10. package/dist/dist/index-es.d.ts.map +1 -0
  11. package/dist/dist/sw-helper.d.ts +3 -0
  12. package/dist/dist/sw-helper.d.ts.map +1 -0
  13. package/dist/eslint.config.d.ts +126 -0
  14. package/dist/eslint.config.d.ts.map +1 -0
  15. package/dist/index-es.js +3449 -1848
  16. package/dist/index-es.min.js +4 -4
  17. package/dist/resources/activateCallback.d.ts +23 -0
  18. package/dist/resources/activateCallback.d.ts.map +1 -0
  19. package/dist/resources/index.d.ts +128 -0
  20. package/dist/resources/index.d.ts.map +1 -0
  21. package/dist/resources/resultsDisplay.d.ts +110 -0
  22. package/dist/resources/resultsDisplay.d.ts.map +1 -0
  23. package/dist/resources/templates/index.d.ts +59 -0
  24. package/dist/resources/templates/index.d.ts.map +1 -0
  25. package/dist/resources/templates/languageSelect.d.ts +18 -0
  26. package/dist/resources/templates/languageSelect.d.ts.map +1 -0
  27. package/dist/resources/templates/resultsDisplayClient.d.ts +26 -0
  28. package/dist/resources/templates/resultsDisplayClient.d.ts.map +1 -0
  29. package/dist/resources/templates/resultsDisplayServerOrClient.d.ts +131 -0
  30. package/dist/resources/templates/resultsDisplayServerOrClient.d.ts.map +1 -0
  31. package/dist/resources/templates/utils/dom.d.ts +15 -0
  32. package/dist/resources/templates/utils/dom.d.ts.map +1 -0
  33. package/dist/resources/templates/utils/html.d.ts +3 -0
  34. package/dist/resources/templates/utils/html.d.ts.map +1 -0
  35. package/dist/resources/templates/workDisplay.d.ts +183 -0
  36. package/dist/resources/templates/workDisplay.d.ts.map +1 -0
  37. package/dist/resources/templates/workSelect.d.ts +18 -0
  38. package/dist/resources/templates/workSelect.d.ts.map +1 -0
  39. package/dist/resources/user-sample.d.ts +2 -0
  40. package/dist/resources/user-sample.d.ts.map +1 -0
  41. package/dist/resources/user.d.ts +2 -0
  42. package/dist/resources/user.d.ts.map +1 -0
  43. package/dist/resources/utils/IntlURLSearchParams.d.ts +56 -0
  44. package/dist/resources/utils/IntlURLSearchParams.d.ts.map +1 -0
  45. package/dist/resources/utils/Languages.d.ts +81 -0
  46. package/dist/resources/utils/Languages.d.ts.map +1 -0
  47. package/dist/resources/utils/Metadata.d.ts +133 -0
  48. package/dist/resources/utils/Metadata.d.ts.map +1 -0
  49. package/dist/resources/utils/Params.d.ts +30 -0
  50. package/dist/resources/utils/Params.d.ts.map +1 -0
  51. package/dist/resources/utils/Plugin.d.ts +278 -0
  52. package/dist/resources/utils/Plugin.d.ts.map +1 -0
  53. package/dist/resources/utils/ServiceWorker.d.ts +26 -0
  54. package/dist/resources/utils/ServiceWorker.d.ts.map +1 -0
  55. package/dist/resources/utils/WorkInfo.d.ts +104 -0
  56. package/dist/resources/utils/WorkInfo.d.ts.map +1 -0
  57. package/dist/resources/utils/dialogs.d.ts +155 -0
  58. package/dist/resources/utils/dialogs.d.ts.map +1 -0
  59. package/dist/resources/utils/getLocaleFallbackResults.d.ts +19 -0
  60. package/dist/resources/utils/getLocaleFallbackResults.d.ts.map +1 -0
  61. package/dist/resources/utils/sanitize.d.ts +6 -0
  62. package/dist/resources/utils/sanitize.d.ts.map +1 -0
  63. package/dist/resources/vendor/json-refs-min.d.ts +3 -0
  64. package/dist/resources/vendor/json-refs-min.d.ts.map +1 -0
  65. package/dist/resources/workDisplay.d.ts +81 -0
  66. package/dist/resources/workDisplay.d.ts.map +1 -0
  67. package/dist/resources/workSelect.d.ts +17 -0
  68. package/dist/resources/workSelect.d.ts.map +1 -0
  69. package/dist/rollup.config.d.ts +19 -0
  70. package/dist/rollup.config.d.ts.map +1 -0
  71. package/dist/server/main.d.ts +47 -0
  72. package/dist/server/main.d.ts.map +1 -0
  73. package/dist/sw-helper.d.ts +3 -0
  74. package/dist/sw-helper.d.ts.map +1 -0
  75. package/dist/sw-helper.js +52 -12
  76. package/dist/sw-sample.d.ts +2 -0
  77. package/dist/sw-sample.d.ts.map +1 -0
  78. package/general-schemas/files.jsonschema +6 -0
  79. package/package.json +32 -19
  80. package/resources/activateCallback.js +81 -10
  81. package/resources/index.js +169 -58
  82. package/resources/resultsDisplay.js +520 -153
  83. package/resources/templates/index.js +39 -17
  84. package/resources/templates/languageSelect.js +14 -1
  85. package/resources/templates/resultsDisplayClient.js +22 -3
  86. package/resources/templates/resultsDisplayServerOrClient.js +188 -49
  87. package/resources/templates/utils/dom.js +13 -2
  88. package/resources/templates/workDisplay.js +299 -76
  89. package/resources/templates/workSelect.js +16 -3
  90. package/resources/user-sample.js +1 -0
  91. package/resources/utils/IntlURLSearchParams.js +46 -4
  92. package/resources/utils/Languages.js +71 -4
  93. package/resources/utils/Metadata.js +222 -23
  94. package/resources/utils/Params.js +70 -23
  95. package/resources/utils/Plugin.js +175 -1
  96. package/resources/utils/ServiceWorker.js +49 -21
  97. package/resources/utils/WorkInfo.js +256 -44
  98. package/resources/utils/dialogs.js +105 -7
  99. package/resources/utils/getLocaleFallbackResults.js +15 -3
  100. package/resources/utils/sanitize.js +4 -0
  101. package/resources/workDisplay.js +151 -25
  102. package/resources/workSelect.js +37 -17
  103. package/server/main.js +118 -20
  104. package/sw-sample.js +2 -1
  105. package/tsconfig.json +30 -0
  106. package/resources/user.js +0 -20
@@ -6,6 +6,61 @@ import {dialogs} from './utils/dialogs.js';
6
6
 
7
7
  import Templates from './templates/index.js';
8
8
 
9
+ /**
10
+ * @typedef {() => Promise<{
11
+ * groupName: string | Text | DocumentFragment,
12
+ * worksToFields: {
13
+ * workName: string | Text | DocumentFragment,
14
+ * shortcut: string,
15
+ * fieldAliasOrNames: (
16
+ * string|string[]|import('../server/main.js').LocalizationStrings
17
+ * )[]|undefined
18
+ * }[]
19
+ * }[]>} GetFieldAliasOrNames
20
+ */
21
+
22
+ /**
23
+ * @typedef {(innerArg: {
24
+ * form: HTMLFormElement,
25
+ * random: {
26
+ * checked: boolean
27
+ * },
28
+ * checkboxes: HTMLInputElement[],
29
+ * type: string,
30
+ * fieldAliasOrNames?: string[],
31
+ * workName?: string,
32
+ * }) => string} SerializeParamsAsURL
33
+ */
34
+
35
+ /**
36
+ * @typedef {(
37
+ * key: string|string[],
38
+ * substitutions?: Record<string, string>
39
+ * ) => string|Text|DocumentFragment} LDirectional
40
+ */
41
+
42
+ /**
43
+ * @typedef {(
44
+ * key: string,
45
+ * tag: string,
46
+ * attr: string,
47
+ * atts: import('jamilih').JamilihAttributes,
48
+ * children?: import('jamilih').JamilihChildren
49
+ * ) => import('jamilih').JamilihArray} LElement
50
+ */
51
+
52
+ /**
53
+ * @this {import('./index.js').default}
54
+ * @param {{
55
+ * l: import('intl-dom').I18NCallback,
56
+ * languageParam: string,
57
+ * lang: string[],
58
+ * preferredLocale: string,
59
+ * languages: import('./utils/Languages.js').Languages,
60
+ * fallbackLanguages: string[],
61
+ * $p: import('./utils/IntlURLSearchParams.js').default
62
+ * }} cfg
63
+ */
9
64
  export default async function workDisplay ({
10
65
  l, languageParam,
11
66
  lang, preferredLocale, languages, fallbackLanguages, $p
@@ -13,7 +68,7 @@ export default async function workDisplay ({
13
68
  const {preferencesPlugin} = this;
14
69
  const langs = this.langData.languages;
15
70
 
16
- const fallbackDirection = this.getDirectionForLanguageCode(fallbackLanguages[0]);
71
+ // const fallbackDirection = this.getDirectionForLanguageCode(fallbackLanguages[0]);
17
72
 
18
73
  const prefI18n = localStorage.getItem(this.namespace + '-localizeParamNames');
19
74
  const localizeParamNames = $p.localizeParamNames = $p.has('i18n', true)
@@ -29,19 +84,63 @@ export default async function workDisplay ({
29
84
  prefFormatting !== 'false' && this.hideFormattingSection
30
85
  );
31
86
 
87
+ /**
88
+ * @this {import('./index.js').default}
89
+ * @param {{
90
+ * workI18n: import('intl-dom').I18NCallback,
91
+ * metadataObj: import('./utils/Metadata.js').MetadataObj,
92
+ * getFieldAliasOrName: (field: string) => string|string[]|import('../server/main.js').LocalizationStrings,
93
+ * schemaItems: {title: string, type: string}[],
94
+ * fieldInfo: import('./utils/WorkInfo.js').FieldInfo,
95
+ * metadata: import('./utils/Metadata.js').Metadata,
96
+ * pluginsForWork: import('./utils/Plugin.js').PluginsForWork|null,
97
+ * groupsToWorks: {
98
+ * name: string | Text | DocumentFragment,
99
+ * workNames: (string | Text | DocumentFragment)[],
100
+ * shortcuts: string[]
101
+ * }[]
102
+ * }} cfg
103
+ * @returns {Promise<void>}
104
+ */
32
105
  async function _displayWork ({
33
106
  workI18n, metadataObj, getFieldAliasOrName, schemaItems, // schemaObj,
34
107
  fieldInfo, metadata, pluginsForWork, groupsToWorks
35
108
  }) {
36
109
  const lParam = localizeParamNames
37
- ? (key) => l(['params', key])
38
- : (key) => key;
110
+ // eslint-disable-next-line @stylistic/operator-linebreak -- TS
111
+ ?
112
+ /**
113
+ * @param {string} key
114
+ * @returns {string}
115
+ */ (key) => /** @type {string} */ (l(['params', key]))
116
+ // eslint-disable-next-line @stylistic/operator-linebreak -- TS
117
+ :
118
+ /**
119
+ * @param {string} key
120
+ * @returns {string}
121
+ */
122
+ (key) => key;
39
123
  const lIndexedParam = localizeParamNames
40
- ? (key) => l(['params', 'indexed', key])
41
- : (key) => key;
124
+ // eslint-disable-next-line @stylistic/operator-linebreak -- TS
125
+ ?
126
+ /**
127
+ * @param {string} key
128
+ * @returns {string}
129
+ */
130
+ (key) => /** @type {string} */ (l(['params', 'indexed', key]))
131
+ // eslint-disable-next-line @stylistic/operator-linebreak -- TS
132
+ :
133
+ /**
134
+ * @param {string} key
135
+ * @returns {string}
136
+ */
137
+ (key) => key;
42
138
 
43
139
  // Returns element with localized option text (as Jamilih), with
44
140
  // optional fallback direction
141
+ /**
142
+ * @type {LElement}
143
+ */
45
144
  const lElement = (key, el, attToLocalize, atts, children = []) => {
46
145
  atts[attToLocalize] = l(
47
146
  key
@@ -55,6 +154,10 @@ export default async function workDisplay ({
55
154
  };
56
155
 
57
156
  // Returns plain text node or element (as Jamilih) with fallback direction
157
+
158
+ /**
159
+ * @type {LDirectional}
160
+ */
58
161
  const lDirectional = (key, substitutions) => l(
59
162
  key,
60
163
  substitutions
@@ -67,15 +170,20 @@ export default async function workDisplay ({
67
170
  const fieldMatchesLocale = metadata.getFieldMatchesLocale({
68
171
  namespace: this.namespace,
69
172
  preferredLocale, schemaItems,
70
- pluginsForWork
173
+ // eslint-disable-next-line object-shorthand -- TS
174
+ pluginsForWork: /** @type {import('./utils/Plugin.js').PluginsForWork} */ (
175
+ pluginsForWork
176
+ )
71
177
  });
72
178
 
179
+ /** @type {import('jamilih').JamilihArray[]} */
73
180
  const content = [];
74
181
  this.getBrowseFieldData({
75
182
  metadataObj, schemaItems, getFieldAliasOrName,
76
183
  callback ({browseFields, i}) {
77
184
  Templates.workDisplay.addBrowseFields({
78
- browseFields, fieldInfo,
185
+ browseFields,
186
+ fieldInfo,
79
187
  lDirectional, i, lIndexedParam, $p, content
80
188
  });
81
189
  }
@@ -89,10 +197,14 @@ export default async function workDisplay ({
89
197
  const serializeParamsAsURL = getSerializeParamsAsURL({l, lParam, $p});
90
198
  const paramsSetter = getParamsSetter({l, lParam, $p});
91
199
 
92
- const {groups} = await getJSON(this.files);
200
+ const {groups} = /** @type {import('./utils/WorkInfo.js').FilesObject} */ (
201
+ await getJSON(this.files)
202
+ );
93
203
 
94
204
  // const arabicContent = ['test1', 'test2']; // Todo: Fetch dynamically
95
- const heading = getMetaProp(lang, metadataObj, 'heading');
205
+ const heading = /** @type {string} */ (
206
+ getMetaProp(lang, metadataObj, 'heading')
207
+ );
96
208
 
97
209
  const getFieldAliasOrNames = (() => {
98
210
  // Avoid blocking but start now
@@ -102,10 +214,12 @@ export default async function workDisplay ({
102
214
  const worksToFields = await Promise.all(workNames.map(async (workName, i) => {
103
215
  return {
104
216
  workName, shortcut: shortcuts[i],
105
- fieldAliasOrNames: (await this.getWorkData({
106
- lang, fallbackLanguages, preferredLocale,
107
- languages, work: workName
108
- })).fieldInfo.map(({fieldAliasOrName}) => fieldAliasOrName)
217
+ fieldAliasOrNames: (
218
+ await this.getWorkData({
219
+ lang, fallbackLanguages, preferredLocale,
220
+ languages, work: /** @type {string} */ (workName)
221
+ })
222
+ )?.fieldInfo?.map(({fieldAliasOrName}) => fieldAliasOrName)
109
223
  };
110
224
  }));
111
225
  return {
@@ -128,41 +242,53 @@ export default async function workDisplay ({
128
242
  languageDisplay: 'standard' // 'dialect'
129
243
  }
130
244
  );
245
+
246
+ /**
247
+ * @param {string} code
248
+ */
131
249
  const languageI18n = (code) => {
132
- return displayNames.of(code);
250
+ return /** @type {string} */ (displayNames.of(code));
133
251
  };
134
252
 
135
253
  Templates.workDisplay.main({
136
- languageParam, lang, workI18n,
254
+ languageParam,
255
+ // lang,
256
+ workI18n,
137
257
  l, namespace: this.namespace, groups, heading,
138
- languageI18n, fallbackDirection,
258
+ languageI18n,
259
+ // fallbackDirection,
139
260
  langs, fieldInfo, localizeParamNames,
140
261
  serializeParamsAsURL, paramsSetter, replaceHash,
141
262
  getFieldAliasOrNames,
142
263
  hideFormattingSection, $p,
143
264
  metadataObj, lParam, lElement, lDirectional, lIndexedParam,
144
265
  fieldMatchesLocale,
145
- preferredLocale, schemaItems, content,
266
+ preferredLocale, schemaItems,
267
+ content,
146
268
  preferencesPlugin
147
269
  });
148
270
  }
149
271
 
150
272
  try {
151
- const {workI18n, fileData, metadataObj, ...args} = await this.getWorkData({
152
- lang, fallbackLanguages, preferredLocale,
153
- languages, work: $p.get('work')
154
- });
273
+ const {workI18n, fileData, metadataObj, ...args} =
274
+ /** @type {import('./utils/WorkInfo.js').GetWorkDataReturn} */ (
275
+ await this.getWorkData({
276
+ lang, fallbackLanguages, preferredLocale,
277
+ languages,
278
+ work: /** @type {string} */ ($p.get('work'))
279
+ }));
155
280
 
156
- document.title = workI18n(
281
+ document.title = /** @type {string} */ (workI18n(
157
282
  'browserfile-workdisplay',
158
283
  {
159
284
  work: fileData
160
- ? getMetaProp(lang, metadataObj, 'alias')
285
+ ? /** @type {string} */ (getMetaProp(lang, metadataObj, 'alias'))
161
286
  : ''
162
287
  }
163
- );
288
+ ));
164
289
  await _displayWork.call(this, {workI18n, metadataObj, ...args});
165
- } catch (err) {
290
+ } catch (error) {
291
+ const err = /** @type {Error} */ (error);
166
292
  console.log('err', err);
167
293
  dialogs.alert(err);
168
294
  }
@@ -5,6 +5,15 @@ import {getMetaProp} from './utils/Metadata.js';
5
5
  import {dialogs} from './utils/dialogs.js';
6
6
  import Templates from './templates/index.js';
7
7
 
8
+ /**
9
+ * @param {{
10
+ * files: string,
11
+ * lang: string[],
12
+ * fallbackLanguages: string[],
13
+ * $p: import('./utils/IntlURLSearchParams.js').default,
14
+ * followParams: (formSelector: string, cb: () => void) => void
15
+ * }} cfg
16
+ */
8
17
  export default async function workSelect ({
9
18
  files, lang, fallbackLanguages, $p, followParams
10
19
  /* , l, defineFormatter */
@@ -12,40 +21,48 @@ export default async function workSelect ({
12
21
  // We use getJSON instead of JsonRefs as we do not necessarily need to
13
22
  // resolve the file contents here
14
23
  try {
15
- const works = await getJSON(files);
24
+ const works = /** @type {import('./utils/WorkInfo.js').FilesObject} */ (
25
+ await getJSON(files)
26
+ );
16
27
  const localizationStrings = works['localization-strings'];
17
28
 
18
- const metadataObjs = await getJSON(works.groups.reduce((arr, fileGroup) => {
19
- const metadataBaseDir = (works.metadataBaseDirectory || '') +
20
- (fileGroup.metadataBaseDirectory || '') + '/';
21
- return fileGroup.files.reduce((ar, fileData) => [
22
- ...ar, metadataBaseDir + fileData.metadataFile
23
- ],
24
- arr);
25
- }, []));
29
+ const metadataObjs = /** @type {import('./utils/Metadata.js').MetadataObj[]} */ (
30
+ await getJSON(works.groups.reduce((arr, fileGroup) => {
31
+ const metadataBaseDir = (works.metadataBaseDirectory || '') +
32
+ (fileGroup.metadataBaseDirectory || '') + '/';
33
+ return fileGroup.files.reduce((ar, fileData) => [
34
+ ...ar, metadataBaseDir + fileData.metadataFile
35
+ ],
36
+ arr);
37
+ }, /** @type {string[]} */ ([])))
38
+ );
26
39
 
27
40
  const workI18n = await i18n({
28
41
  messageStyle: 'plainNested',
29
42
  locales: lang,
30
43
  defaultLocales: fallbackLanguages,
31
44
  // Todo: Could at least share this with `index.js`
32
- localeStringFinder ({
45
+ async localeStringFinder ({
33
46
  locales, defaultLocales
34
- }) {
35
- const locale = [...locales, ...defaultLocales].find((language) => {
47
+ } = {}) {
48
+ const locale = [
49
+ ...(/** @type {string[]} */ (locales)),
50
+ ...(/** @type {string[]} */ (defaultLocales))
51
+ ].find((language) => {
36
52
  return language in localizationStrings;
37
53
  });
38
54
  return {
39
- locale,
55
+ // eslint-disable-next-line object-shorthand -- TS
56
+ locale: /** @type {string} */ (locale),
40
57
  strings: {
41
58
  head: {},
42
- body: localizationStrings[locale]
59
+ body: localizationStrings[/** @type {string} */ (locale)]
43
60
  }
44
61
  };
45
62
  }
46
63
  });
47
64
 
48
- document.title = workI18n('browserfile-workselect');
65
+ document.title = /** @type {string} */ (workI18n('browserfile-workselect'));
49
66
  /*
50
67
  // Would need adapting now that not using IMF
51
68
  function lDirectional (key, substitutions, formats) {
@@ -64,13 +81,16 @@ export default async function workSelect ({
64
81
 
65
82
  const metadataObjsIter = metadataObjs[Symbol.iterator]();
66
83
  const getNextAlias = () => {
67
- const metadataObj = metadataObjsIter.next().value;
84
+ const metadataObj = /** @type {import('./utils/Metadata.js').MetadataObj} */ (
85
+ metadataObjsIter.next().value
86
+ );
68
87
  return getMetaProp(lang, metadataObj, 'alias');
69
88
  };
70
89
  Templates.workSelect({
71
90
  groups: works.groups, workI18n, getNextAlias, $p, followParams
72
91
  });
73
- } catch (err) {
92
+ } catch (error) {
93
+ const err = /** @type {Error} */ (error);
74
94
  console.log('Error', err);
75
95
  dialogs.alert(err);
76
96
  }
package/server/main.js CHANGED
@@ -1,12 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /* eslint-env node -- Environment here */
4
- import http from 'http';
4
+ import http from 'node:http';
5
5
 
6
+ // @ts-expect-error Todo: Needs Types
6
7
  import statik from '@brettz9/node-static';
7
8
  import fetch from 'node-fetch';
9
+ // @ts-expect-error Todo: Needs Types
8
10
  import commandLineArgs from 'command-line-args';
9
11
  import DOMParser from 'dom-parser';
12
+
13
+ // Why can't we just import `indexeddbshim` here? Because we need a UMD module?
14
+ // @ts-expect-error Todo: Needs Types
10
15
  import setGlobalVars from 'indexeddbshim/dist/indexeddbshim-UnicodeIdentifiers-node.cjs';
11
16
  import {getJSON} from 'simple-get-json';
12
17
  import {setFetch} from 'intl-dom';
@@ -18,6 +23,8 @@ import {setServiceWorkerDefaults} from '../resources/utils/ServiceWorker.js';
18
23
  import {Languages} from '../resources/utils/Languages.js';
19
24
  import activateCallback from '../resources/activateCallback.js';
20
25
 
26
+ // Todo: Should loosen this to not follow all of `fetch`'s API
27
+ // @ts-expect-error Todo: Needs types
21
28
  setFetch(fetch);
22
29
 
23
30
  // Todo (low): See
@@ -26,6 +33,28 @@ setFetch(fetch);
26
33
  // server-side and apply fetch listener as Node middleware to retrieve a
27
34
  // locally-saved copy of pre-fetched (cached) files during install event
28
35
 
36
+ /**
37
+ * @typedef {{
38
+ * nodeActivate: boolean,
39
+ * port: number,
40
+ * domain: string,
41
+ * basePath: string,
42
+ * interlinearSeparator: string,
43
+ * localizeParamNames: boolean,
44
+ * trustFormatHTML: boolean,
45
+ * allowPlugins: boolean,
46
+ * showEmptyInterlinear: boolean,
47
+ * showTitleOnSingleInterlinear: boolean,
48
+ * serviceWorkerPath: string,
49
+ * userJSON: string,
50
+ * languages: string,
51
+ * files: string,
52
+ * namespace: string,
53
+ * httpServer: string,
54
+ * expressServer: string
55
+ * }} UserOptions
56
+ */
57
+
29
58
  const optionDefinitions = [
30
59
  // Node-server-specific
31
60
  {name: 'nodeActivate', type: Boolean},
@@ -64,6 +93,27 @@ const port = 'port' in userParams ? userParams.port : 8000;
64
93
  const domain = userParams.domain || `localhost`;
65
94
  const basePath = userParams.basePath || `http://${domain}${port ? ':' + port : ''}/`;
66
95
 
96
+ /* eslint-disable jsdoc/reject-any-type -- Arbitrary */
97
+ /**
98
+ * @typedef {any} AnyValue
99
+ */
100
+ /* eslint-enable jsdoc/reject-any-type -- Arbitrary */
101
+
102
+ /**
103
+ * @typedef {import('../resources/utils/ServiceWorker.js').ServiceWorkerConfig &
104
+ * UserOptions & {
105
+ * lang: string[],
106
+ * langs: LanguageInfo[],
107
+ * fallbackLanguages: string[],
108
+ * log: (...args: AnyValue[]) => void,
109
+ * nodeActivate?: boolean,
110
+ * port?: number,
111
+ * skipIndexedDB: false,
112
+ * noDynamic: false,
113
+ * }
114
+ * } ResultsDisplayServerContext
115
+ */
116
+
67
117
  const userParamsWithDefaults = {
68
118
  ...setServiceWorkerDefaults({}, {
69
119
  namespace: 'textbrowser',
@@ -72,6 +122,9 @@ const userParamsWithDefaults = {
72
122
  serviceWorkerPath: `${basePath}sw.js?pathToUserJSON=${encodeURIComponent(userParams.userJSON || '')}`
73
123
  }),
74
124
  ...userParams,
125
+ /**
126
+ * @param {...AnyValue} args
127
+ */
75
128
  log (...args) {
76
129
  console.log(...args);
77
130
  },
@@ -100,8 +153,9 @@ setGlobalVars(null, {
100
153
  }); // Adds `indexedDB` and `IDBKeyRange` to global in Node
101
154
 
102
155
  if (userParams.nodeActivate) {
156
+ // @ts-expect-error Ok
103
157
  // eslint-disable-next-line n/no-unsupported-features/node-builtins -- node-fetch
104
- global.fetch = fetch;
158
+ globalThis.fetch = /** @type {fetch} */ (fetch);
105
159
  setTimeout(async () => {
106
160
  await activateCallback({...userParamsWithDefaults, basePath});
107
161
  });
@@ -109,15 +163,46 @@ if (userParams.nodeActivate) {
109
163
  }
110
164
  console.log('past activate check');
111
165
 
112
- global.DOMParser = DOMParser; // potentially used within resultsDisplay.js
166
+ // @ts-expect-error Ok
167
+ globalThis.DOMParser = DOMParser; // potentially used within resultsDisplay.js
113
168
 
114
169
  const fileServer = new statik.Server(); // Pass path; otherwise uses current directory
115
170
 
116
- let langData, languagesInstance;
171
+ /**
172
+ * @typedef {{
173
+ * code: string,
174
+ * direction: "ltr"|"rtl",
175
+ * locale: {
176
+ * $ref: string
177
+ * }
178
+ * }} LanguageInfo
179
+ */
180
+
181
+ /**
182
+ * @typedef {{
183
+ * [key: string]: string|string[]|LocalizationStrings
184
+ * }} LocalizationStrings
185
+ */
186
+
187
+ /**
188
+ * @typedef {{
189
+ * languages: LanguageInfo[],
190
+ * localeFileBasePath?: string,
191
+ * "localization-strings": LocalizationStrings
192
+ * }} LanguagesData
193
+ */
194
+
195
+ /**
196
+ * @type {LanguagesData}
197
+ */
198
+ let langData;
199
+
200
+ /** @type {Languages} */
201
+ let languagesInstance;
117
202
 
118
203
  const srv = http.createServer(async (req, res) => {
119
204
  // console.log('URL::', new URL(req.url));
120
- const {pathname, search} = new URL(req.url, basePath);
205
+ const {pathname, search} = new URL(/** @type {string} */ (req.url), basePath);
121
206
  if (pathname !== '/textbrowser' || !search) {
122
207
  const staticServer = () => {
123
208
  if (pathname.includes('.git')) {
@@ -148,19 +233,25 @@ const srv = http.createServer(async (req, res) => {
148
233
  );
149
234
  };
150
235
  if (userParams.expressServer) {
236
+ /** @type {import('express').Application} */
151
237
  const app = (await import(userParams.expressServer)).default();
152
238
 
153
- if (userParams.httpServer && (!app._router || !app._router.stack.some(({regexp}) => {
154
- // Hack to ignore middleware like jsonParser (and hopefully
155
- // not get any other)
156
- return regexp.source !== String.raw`^\/?(?=\\/|$)` && regexp.test(req.url);
157
- }))) {
239
+ if (userParams.httpServer && (!app._router || !app._router.stack.some(
240
+ /** @type {(info: {regexp: RegExp}) => boolean} */
241
+ ({regexp}) => {
242
+ // Hack to ignore middleware like jsonParser (and hopefully
243
+ // not get any other)
244
+ return regexp.source !== String.raw`^\/?(?=\\/|$)` &&
245
+ regexp.test(/** @type {string} */ (req.url));
246
+ }
247
+ ))) {
158
248
  await runHttpServer();
159
249
 
160
250
  // Ideally we could use `next` here to serve as a back-up static
161
251
  // server (i.e., for the bahaiwritings app proper), but the indexes
162
252
  // app apparently tries to use it (and fails) after a single
163
253
  // successful HTML page load. So we let the Express app handle
254
+ // @ts-expect-error Ok
164
255
  app(req, res, () => {
165
256
  // Empty
166
257
  });
@@ -168,6 +259,7 @@ const srv = http.createServer(async (req, res) => {
168
259
  }
169
260
 
170
261
  // app.get('*', staticServer);
262
+ // @ts-expect-error Ok
171
263
  app(req, res, next);
172
264
 
173
265
  return;
@@ -184,8 +276,9 @@ const srv = http.createServer(async (req, res) => {
184
276
  return;
185
277
  }
186
278
  const languages = (req.headers['accept-language']?.replace(/;q=.*$/, '') ?? 'en-US').split(',');
279
+ // @ts-expect-error Polyglot reasons
187
280
  // eslint-disable-next-line n/no-unsupported-features/node-builtins -- Polyglot reasons
188
- global.navigator = {
281
+ globalThis.navigator = {
189
282
  language: languages[0],
190
283
  languages
191
284
  };
@@ -194,7 +287,9 @@ const srv = http.createServer(async (req, res) => {
194
287
  });
195
288
 
196
289
  if (!langData || !languagesInstance) {
197
- langData = await getJSON(userParamsWithDefaults.languages);
290
+ langData = /** @type {LanguagesData} */ (
291
+ await getJSON(userParamsWithDefaults.languages)
292
+ );
198
293
  languagesInstance = new Languages({langData});
199
294
  }
200
295
 
@@ -207,22 +302,25 @@ const srv = http.createServer(async (req, res) => {
207
302
  });
208
303
 
209
304
  const resultsDisplay = async function () {
210
- const serverOutput = $p.get('serverOutput', true);
211
- let allowPlugins = $p.get('allowPlugins', true);
212
- if (allowPlugins === '0') {
213
- allowPlugins = false;
214
- }
305
+ const serverOutput = /** @type {"html"|"jamilih"|"json"} */ (
306
+ $p.get('serverOutput', true)
307
+ );
308
+ const allowPluginsParam = $p.get('allowPlugins', true);
309
+ const allowPlugins = Boolean(allowPluginsParam && allowPluginsParam !== '0');
310
+
215
311
  const isHTML = serverOutput === 'html';
216
312
  res.writeHead(200, {'Content-Type': isHTML
217
313
  ? 'text/html;charset=utf8'
218
314
  : 'application/json;charset=utf8'
219
315
  });
316
+
220
317
  // Todo: Move sw-sample.js to bahaiwritings and test
221
318
  const result = await resultsDisplayServer.call(
222
319
  // Context
223
- {
320
+ /** @type {ResultsDisplayServerContext} */
321
+ ({
224
322
  ...userParamsWithDefaults, lang, langs, fallbackLanguages
225
- },
323
+ }),
226
324
  // resultsArgs
227
325
  {
228
326
  l,
@@ -235,7 +333,7 @@ const srv = http.createServer(async (req, res) => {
235
333
  allowPlugins,
236
334
  serverOutput,
237
335
  langData,
238
- prefI18n: $p.get('prefI18n', true)
336
+ prefI18n: /** @type {"true"|"false"|null} */ ($p.get('prefI18n', true))
239
337
  }
240
338
  );
241
339
  res.end(isHTML ? result : JSON.stringify(result));
package/sw-sample.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /* eslint-env serviceworker -- Service worker */
2
2
 
3
+ // eslint-disable-next-line sonarjs/no-internal-api-use -- Demo
3
4
  import swHelper from './node_modules/textbrowser/dist/sw-helper.js';
4
5
 
5
6
  // IMPORTANT: Keep this comment and increment this number to trigger
6
7
  // a worker change: 1
7
8
 
8
- swHelper(self);
9
+ swHelper(globalThis);
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["es2022", "DOM", "DOM.Iterable", "webworker"],
4
+ "moduleResolution": "Bundler",
5
+ "module": "ESNext",
6
+ "allowJs": true,
7
+ "checkJs": true,
8
+ "emitDeclarationOnly": true,
9
+ "declaration": true,
10
+ "declarationMap": true,
11
+ "strict": true,
12
+ "skipLibCheck": true,
13
+ "target": "es2017",
14
+ "outDir": "dist"
15
+ },
16
+ "include": [
17
+ "*.js",
18
+ "resources",
19
+ "server",
20
+ "test",
21
+ "dist/sw-helper.js"
22
+ // "typings/*.d.ts"
23
+ ],
24
+ "exclude": [
25
+ "node_modules", "resources/vendor", "resources/user-sample.js",
26
+ "resources/user.js",
27
+ "sw-sample.js",
28
+ "dist"
29
+ ]
30
+ }
package/resources/user.js DELETED
@@ -1,20 +0,0 @@
1
- import TextBrowser from '../dist/index-es.js';
2
-
3
- const tb = new TextBrowser({
4
- languages: 'appdata/languages.json', // Default
5
- // serviceWorkerPath: 'sw.js', // Default
6
- // site: 'site.json', // Default
7
- // localizeParamNames: true, // Not well-tested
8
- // hideFormattingSection: true,
9
- // requestPersistentStorage: false,
10
- // showEmptyInterlinear: false,
11
- // showTitleOnSingleInterlinear: false,
12
- files: 'files.json', // Change as needed to your `files.json` location
13
- stylesheets: ['@builtin'], // Add other paths for your CSS
14
- namespace: 'myapp', // Used for namespacing localStorage
15
- allowPlugins: true, // Enables `files.json`-specified plugins
16
- // dynamicBasePath: '',
17
- trustFormatHTML: true // Needed if allowing for raw HTML fields (don't use for untrusted schemas)
18
- // , interlinearSeparator: '<hr />' // Defaults to `<br /><br />`
19
- });
20
- await tb.init();