textbrowser 0.50.0 → 0.51.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 (104) hide show
  1. package/.ncurc.cjs +3 -0
  2. package/CHANGES.md +8 -0
  3. package/dist/WorkInfo-es.js +361 -111
  4. package/dist/activateCallback-es.js +72 -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 +959 -195
  16. package/dist/index-es.min.js +2 -2
  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 +274 -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 +46 -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 +21 -8
  80. package/resources/activateCallback.js +75 -10
  81. package/resources/index.js +161 -49
  82. package/resources/resultsDisplay.js +517 -152
  83. package/resources/templates/index.js +39 -16
  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 -75
  89. package/resources/templates/workSelect.js +16 -3
  90. package/resources/utils/IntlURLSearchParams.js +46 -4
  91. package/resources/utils/Languages.js +71 -4
  92. package/resources/utils/Metadata.js +219 -22
  93. package/resources/utils/Params.js +70 -23
  94. package/resources/utils/Plugin.js +169 -1
  95. package/resources/utils/ServiceWorker.js +48 -19
  96. package/resources/utils/WorkInfo.js +255 -43
  97. package/resources/utils/dialogs.js +105 -7
  98. package/resources/utils/getLocaleFallbackResults.js +12 -2
  99. package/resources/utils/sanitize.js +4 -0
  100. package/resources/workDisplay.js +151 -25
  101. package/resources/workSelect.js +37 -17
  102. package/server/main.js +111 -19
  103. package/tsconfig.json +30 -0
  104. 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,21 @@ 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
+ /**
97
+ * @typedef {import('../resources/utils/ServiceWorker.js').ServiceWorkerConfig &
98
+ * UserOptions & {
99
+ * lang: string[],
100
+ * langs: LanguageInfo[],
101
+ * fallbackLanguages: string[],
102
+ * log: (...args: any[]) => void,
103
+ * nodeActivate?: boolean,
104
+ * port?: number,
105
+ * skipIndexedDB: false,
106
+ * noDynamic: false,
107
+ * }
108
+ * } ResultsDisplayServerContext
109
+ */
110
+
67
111
  const userParamsWithDefaults = {
68
112
  ...setServiceWorkerDefaults({}, {
69
113
  namespace: 'textbrowser',
@@ -72,6 +116,9 @@ const userParamsWithDefaults = {
72
116
  serviceWorkerPath: `${basePath}sw.js?pathToUserJSON=${encodeURIComponent(userParams.userJSON || '')}`
73
117
  }),
74
118
  ...userParams,
119
+ /**
120
+ * @param {...any} args
121
+ */
75
122
  log (...args) {
76
123
  console.log(...args);
77
124
  },
@@ -100,8 +147,9 @@ setGlobalVars(null, {
100
147
  }); // Adds `indexedDB` and `IDBKeyRange` to global in Node
101
148
 
102
149
  if (userParams.nodeActivate) {
150
+ // @ts-expect-error Ok
103
151
  // eslint-disable-next-line n/no-unsupported-features/node-builtins -- node-fetch
104
- global.fetch = fetch;
152
+ globalThis.fetch = /** @type {fetch} */ (fetch);
105
153
  setTimeout(async () => {
106
154
  await activateCallback({...userParamsWithDefaults, basePath});
107
155
  });
@@ -109,15 +157,46 @@ if (userParams.nodeActivate) {
109
157
  }
110
158
  console.log('past activate check');
111
159
 
112
- global.DOMParser = DOMParser; // potentially used within resultsDisplay.js
160
+ // @ts-expect-error Ok
161
+ globalThis.DOMParser = DOMParser; // potentially used within resultsDisplay.js
113
162
 
114
163
  const fileServer = new statik.Server(); // Pass path; otherwise uses current directory
115
164
 
116
- let langData, languagesInstance;
165
+ /**
166
+ * @typedef {{
167
+ * code: string,
168
+ * direction: "ltr"|"rtl",
169
+ * locale: {
170
+ * $ref: string
171
+ * }
172
+ * }} LanguageInfo
173
+ */
174
+
175
+ /**
176
+ * @typedef {{
177
+ * [key: string]: string|string[]|LocalizationStrings
178
+ * }} LocalizationStrings
179
+ */
180
+
181
+ /**
182
+ * @typedef {{
183
+ * languages: LanguageInfo[],
184
+ * localeFileBasePath?: string,
185
+ * "localization-strings": LocalizationStrings
186
+ * }} LanguagesData
187
+ */
188
+
189
+ /**
190
+ * @type {LanguagesData}
191
+ */
192
+ let langData;
193
+
194
+ /** @type {Languages} */
195
+ let languagesInstance;
117
196
 
118
197
  const srv = http.createServer(async (req, res) => {
119
198
  // console.log('URL::', new URL(req.url));
120
- const {pathname, search} = new URL(req.url, basePath);
199
+ const {pathname, search} = new URL(/** @type {string} */ (req.url), basePath);
121
200
  if (pathname !== '/textbrowser' || !search) {
122
201
  const staticServer = () => {
123
202
  if (pathname.includes('.git')) {
@@ -148,19 +227,25 @@ const srv = http.createServer(async (req, res) => {
148
227
  );
149
228
  };
150
229
  if (userParams.expressServer) {
230
+ /** @type {import('express').Application} */
151
231
  const app = (await import(userParams.expressServer)).default();
152
232
 
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
- }))) {
233
+ if (userParams.httpServer && (!app._router || !app._router.stack.some(
234
+ /** @type {(info: {regexp: RegExp}) => boolean} */
235
+ ({regexp}) => {
236
+ // Hack to ignore middleware like jsonParser (and hopefully
237
+ // not get any other)
238
+ return regexp.source !== String.raw`^\/?(?=\\/|$)` &&
239
+ regexp.test(/** @type {string} */ (req.url));
240
+ }
241
+ ))) {
158
242
  await runHttpServer();
159
243
 
160
244
  // Ideally we could use `next` here to serve as a back-up static
161
245
  // server (i.e., for the bahaiwritings app proper), but the indexes
162
246
  // app apparently tries to use it (and fails) after a single
163
247
  // successful HTML page load. So we let the Express app handle
248
+ // @ts-expect-error Ok
164
249
  app(req, res, () => {
165
250
  // Empty
166
251
  });
@@ -168,6 +253,7 @@ const srv = http.createServer(async (req, res) => {
168
253
  }
169
254
 
170
255
  // app.get('*', staticServer);
256
+ // @ts-expect-error Ok
171
257
  app(req, res, next);
172
258
 
173
259
  return;
@@ -184,6 +270,7 @@ const srv = http.createServer(async (req, res) => {
184
270
  return;
185
271
  }
186
272
  const languages = (req.headers['accept-language']?.replace(/;q=.*$/, '') ?? 'en-US').split(',');
273
+ // @ts-expect-error Polyglot reasons
187
274
  // eslint-disable-next-line n/no-unsupported-features/node-builtins -- Polyglot reasons
188
275
  global.navigator = {
189
276
  language: languages[0],
@@ -194,7 +281,9 @@ const srv = http.createServer(async (req, res) => {
194
281
  });
195
282
 
196
283
  if (!langData || !languagesInstance) {
197
- langData = await getJSON(userParamsWithDefaults.languages);
284
+ langData = /** @type {LanguagesData} */ (
285
+ await getJSON(userParamsWithDefaults.languages)
286
+ );
198
287
  languagesInstance = new Languages({langData});
199
288
  }
200
289
 
@@ -207,22 +296,25 @@ const srv = http.createServer(async (req, res) => {
207
296
  });
208
297
 
209
298
  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
- }
299
+ const serverOutput = /** @type {"html"|"jamilih"|"json"} */ (
300
+ $p.get('serverOutput', true)
301
+ );
302
+ const allowPluginsParam = $p.get('allowPlugins', true);
303
+ const allowPlugins = Boolean(allowPluginsParam && allowPluginsParam !== '0');
304
+
215
305
  const isHTML = serverOutput === 'html';
216
306
  res.writeHead(200, {'Content-Type': isHTML
217
307
  ? 'text/html;charset=utf8'
218
308
  : 'application/json;charset=utf8'
219
309
  });
310
+
220
311
  // Todo: Move sw-sample.js to bahaiwritings and test
221
312
  const result = await resultsDisplayServer.call(
222
313
  // Context
223
- {
314
+ /** @type {ResultsDisplayServerContext} */
315
+ ({
224
316
  ...userParamsWithDefaults, lang, langs, fallbackLanguages
225
- },
317
+ }),
226
318
  // resultsArgs
227
319
  {
228
320
  l,
@@ -235,7 +327,7 @@ const srv = http.createServer(async (req, res) => {
235
327
  allowPlugins,
236
328
  serverOutput,
237
329
  langData,
238
- prefI18n: $p.get('prefI18n', true)
330
+ prefI18n: /** @type {"true"|"false"|null} */ ($p.get('prefI18n', true))
239
331
  }
240
332
  );
241
333
  res.end(isHTML ? result : JSON.stringify(result));
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();