structured-fw 0.8.42 → 0.8.43

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 (89) hide show
  1. package/package.json +10 -3
  2. package/app/models/README.md +0 -9
  3. package/app/routes/README.md +0 -19
  4. package/app/views/README.md +0 -1
  5. package/app/views/layout.html +0 -1
  6. package/build/Config.d.ts +0 -2
  7. package/build/Config.js +0 -30
  8. package/build/app/Types.d.ts +0 -5
  9. package/build/app/Types.js +0 -1
  10. package/build/app/global.d.ts +0 -3
  11. package/build/app/global.js +0 -1
  12. package/build/app/models/Users.d.ts +0 -0
  13. package/build/app/models/Users.js +0 -1
  14. package/build/app/routes/Auth.d.ts +0 -0
  15. package/build/app/routes/Auth.js +0 -1
  16. package/build/app/routes/Test.d.ts +0 -2
  17. package/build/app/routes/Test.js +0 -101
  18. package/build/app/routes/Todo.d.ts +0 -0
  19. package/build/app/routes/Todo.js +0 -1
  20. package/build/app/routes/Upload.d.ts +0 -0
  21. package/build/app/routes/Upload.js +0 -1
  22. package/build/app/routes/Validation.d.ts +0 -2
  23. package/build/app/routes/Validation.js +0 -34
  24. package/build/app/routess/Auth.d.ts +0 -0
  25. package/build/app/routess/Auth.js +0 -1
  26. package/build/app/routess/Test.d.ts +0 -2
  27. package/build/app/routess/Test.js +0 -101
  28. package/build/app/routess/Todo.d.ts +0 -0
  29. package/build/app/routess/Todo.js +0 -1
  30. package/build/app/routess/Upload.d.ts +0 -0
  31. package/build/app/routess/Upload.js +0 -1
  32. package/build/app/routess/Validation.d.ts +0 -2
  33. package/build/app/routess/Validation.js +0 -34
  34. package/build/app/views/components/ClientImport/ClientImport.client.d.ts +0 -2
  35. package/build/app/views/components/ClientImport/ClientImport.client.js +0 -4
  36. package/build/app/views/components/ClientImport/Export.d.ts +0 -1
  37. package/build/app/views/components/ClientImport/Export.js +0 -1
  38. package/build/app/views/components/Conditionals/Conditionals.client.d.ts +0 -2
  39. package/build/app/views/components/Conditionals/Conditionals.client.js +0 -43
  40. package/build/app/views/components/FormTest/FormTestNested/FormTestNested.d.ts +0 -8
  41. package/build/app/views/components/FormTest/FormTestNested/FormTestNested.js +0 -7
  42. package/build/app/views/components/ModelsTest/ModelsTest.client.d.ts +0 -2
  43. package/build/app/views/components/ModelsTest/ModelsTest.client.js +0 -5
  44. package/build/app/views/components/MultipartForm/MultipartForm.client.d.ts +0 -0
  45. package/build/app/views/components/MultipartForm/MultipartForm.client.js +0 -1
  46. package/build/app/views/components/PassObject/PassObject.d.ts +0 -10
  47. package/build/app/views/components/PassObject/PassObject.js +0 -10
  48. package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.d.ts +0 -6
  49. package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.js +0 -6
  50. package/build/app/views/components/RedrawAbort/RedrawAbort.client.d.ts +0 -2
  51. package/build/app/views/components/RedrawAbort/RedrawAbort.client.js +0 -6
  52. package/build/app/views/components/RedrawAbort/RedrawAbort.d.ts +0 -8
  53. package/build/app/views/components/RedrawAbort/RedrawAbort.js +0 -8
  54. package/build/app/views/components/ServerSideContext/ServerSideContext.d.ts +0 -7
  55. package/build/app/views/components/ServerSideContext/ServerSideContext.js +0 -10
  56. package/build/assets/ts/Export.d.ts +0 -1
  57. package/build/assets/ts/Export.js +0 -1
  58. package/build/index.d.ts +0 -1
  59. package/build/index.js +0 -8
  60. package/build/tsconfig.tsbuildinfo +0 -1
  61. package/jsr.json +0 -35
  62. package/system/EventEmitter.ts +0 -38
  63. package/system/Helpers.ts +0 -97
  64. package/system/Symbols.ts +0 -6
  65. package/system/Types.ts +0 -232
  66. package/system/Util.ts +0 -488
  67. package/system/bin/structured.ts +0 -115
  68. package/system/client/App.ts +0 -11
  69. package/system/client/Client.ts +0 -9
  70. package/system/client/ClientComponent.ts +0 -1107
  71. package/system/client/DataStore.ts +0 -101
  72. package/system/client/DataStoreView.ts +0 -82
  73. package/system/client/Net.ts +0 -58
  74. package/system/client/NetRequest.ts +0 -64
  75. package/system/global.d.ts +0 -12
  76. package/system/server/Application.ts +0 -239
  77. package/system/server/Component.ts +0 -409
  78. package/system/server/Components.ts +0 -114
  79. package/system/server/Cookies.ts +0 -29
  80. package/system/server/Document.ts +0 -163
  81. package/system/server/DocumentHead.ts +0 -150
  82. package/system/server/FormValidation.ts +0 -231
  83. package/system/server/Handlebars.ts +0 -51
  84. package/system/server/Request.ts +0 -502
  85. package/system/server/Session.ts +0 -151
  86. package/system/server/dom/DOMFragment.ts +0 -7
  87. package/system/server/dom/DOMNode.ts +0 -140
  88. package/system/server/dom/HTMLParser.ts +0 -238
  89. package/tsconfig.json +0 -31
@@ -1,409 +0,0 @@
1
- import { Document } from './Document.js';
2
- import { attributeValueFromString, attributeValueToString, objectEach, toCamelCase } from '../Util.js';
3
- import { ComponentEntry, LooseObject } from '../Types.js';
4
- import { DOMFragment } from './dom/DOMFragment.js';
5
- import { DOMNode } from './dom/DOMNode.js';
6
- import { EventEmitter } from '../EventEmitter.js';
7
-
8
- export class Component<Events extends Record<string, any> = {'componentCreated' : Component}> extends EventEmitter<Events> {
9
- id: string;
10
- name: string;
11
- document: Document;
12
-
13
- parent: null|Document|Component;
14
- children: Array<Component> = [];
15
-
16
- path: Array<string> = [];
17
-
18
- // all attributes found on component's tag
19
- attributesRaw: Record<string, string | true> = {};
20
-
21
- // extracted from data-attribute on component tag
22
- attributes: Record<string, string|number|boolean|LooseObject|null> = {};
23
-
24
- dom: DOMNode; // jsdom
25
-
26
- data: LooseObject = {};
27
-
28
- entry: null|ComponentEntry; // null for root
29
-
30
- isRoot: boolean;
31
-
32
- constructor(name: string, node?: DOMNode, parent?: Document|Component, autoInit: boolean = true) {
33
- super();
34
- const isDocument = this instanceof Document;
35
- this.name = name;
36
-
37
- if (name === 'root') {
38
- this.dom = new DOMFragment();
39
- this.path.push('');
40
- this.isRoot = true;
41
- } else {
42
- this.dom = node || new DOMFragment();
43
- if (parent) {
44
- this.path = parent.path.concat(this.name);
45
- }
46
- this.isRoot = false;
47
- }
48
-
49
- if (isDocument) {
50
- // this will only happen if an instance of Document, as it extends component
51
- this.document = this;
52
- } else {
53
- // component is always initialized with parent except when it is a document
54
- // since here we know we are not a part of Document, parent has to be a component
55
- if (! (parent instanceof Component)) {
56
- console.error('Component initialized without a parent');
57
- }
58
- this.document = (parent as Component).document;
59
- }
60
-
61
- this.parent = parent || null;
62
-
63
- this.id = '';
64
-
65
- const component = parent === undefined ? false : this.document.application.components.getByName(this.name);
66
- if (component) {
67
- // store ComponentEntry
68
- this.entry = component;
69
-
70
- if (autoInit) {
71
- // fill in with HTML and init children
72
- this.init(component.html);
73
- }
74
- } else {
75
- this.entry = null;
76
- }
77
-
78
- if (! isDocument) {
79
- this.document.emit('componentCreated', this);
80
- }
81
- }
82
-
83
- // load component's data and fill it
84
- // load any nested components recursively
85
- public async init(html: string, data?: LooseObject): Promise<void> {
86
-
87
- // extract data-attributes and encode non-encoded attributes
88
- this.initAttributesData();
89
-
90
- // create component container replacing the original tag name with a div
91
- // (or whatever is set as renderTagName on ComponentEntry)
92
- this.dom.tagName = this.entry?.renderTagName || 'div';
93
-
94
- // fill container with given HTML
95
- this.dom.innerHTML = html;
96
-
97
-
98
- // re-apply attributes the original tag had
99
- // no need to encode values at this point
100
- // any non-encoded attributes got encoded earlier by initAttributesData
101
- this.setAttributes(this.attributesRaw, '', false);
102
-
103
- // store initializer function on owner Document
104
- if (this.entry !== null && this.entry.initializer !== undefined && typeof this.document.initializers[this.name] === 'undefined') {
105
- this.document.initializers[this.name] = this.entry.initializer;
106
- }
107
-
108
- // set data-structured-component="this.name" attribute on tag
109
- this.dom.setAttribute(`data-${this.document.application.config.components.componentNameAttribute}`, this.name);
110
-
111
- // allocate an unique ID for this component
112
- // used client side to uniquely identify the component when it accesses it's storage
113
- if (typeof this.attributes.componentId !== 'string') {
114
- this.id = this.document.allocateId(this);
115
- this.dom.setAttribute('data-component-id', this.id);
116
- } else {
117
- this.id = this.attributes.componentId;
118
- }
119
-
120
- // export RequestContext.data fields specified in Application.exportedRequestContextData
121
- const exportedContextData = this.document.application.exportedRequestContextData.reduce((prev, field) => {
122
- if (! this.document.ctx) {return prev;}
123
- if (field in this.document.ctx.data) {
124
- prev[field] = this.document.ctx.data[field];
125
- }
126
- return prev;
127
- }, {} as Record<keyof RequestContextData, any>)
128
- objectEach(exportedContextData, (key, val) => {
129
- if (this.document.application.exportedRequestContextData.includes(key)) {
130
- this.setAttributes({ [key]: val }, 'data-', true);
131
- }
132
- });
133
- this.data = exportedContextData;
134
-
135
- // if component is marked as deferred (module.deferred returns true), stop here
136
- // ClientComponent will request a redraw as soon as it's initialized
137
- // setting attributes.deferred = false, to avoid looping
138
- if (
139
- this.entry !== null &&
140
- typeof this.entry.module !== 'undefined' &&
141
- typeof this.entry.module.deferred === 'function' &&
142
- this.entry.module.deferred(this.attributes, this.document.ctx, this.document.application) &&
143
- this.attributes.deferred !== false
144
- ) {
145
- this.setAttributes({deferred: true}, 'data-', true);
146
- return;
147
- }
148
-
149
- if (typeof this.attributes.use === 'string' && this.parent !== null) {
150
- // data-use was found on component tag
151
- // if parent Component.data contains it, include it with data
152
- // set data-component-parent when a component uses parent data
153
- // it will be needed when the component is individually rendered
154
- // componentInstances[j].setAttribute('data-component-parent', parentName);
155
- this.attributes = Object.assign(this.importedParentData(this.parent.data) || {}, this.attributes);
156
- }
157
-
158
- // load data
159
- if (data === undefined) {
160
- if (this.entry && this.entry.module) {
161
- // component has a server side part, fetch data using getData
162
- this.data = Object.assign(this.data, await this.entry.module.getData(this.attributes, this.document.ctx, this.document.application, this) || {});
163
- } else {
164
- // if the component has no server side part
165
- // then use attributes as data
166
- this.data = Object.assign(exportedContextData, this.attributes);
167
- }
168
- } else {
169
- this.data = Object.assign(exportedContextData, data, this.attributes);
170
- }
171
-
172
- // fill in before loading the components as user may output new components depending on the data
173
- // eg. if data is an array user may output a ListItem component using Handlebars each
174
- // we want those to be found as children
175
- this.fillData(this.data);
176
-
177
- if (this.entry === null || this.entry.exportData) {
178
- // export all data if component has no server side part
179
- this.setAttributes(this.data, 'data-');
180
- } else if (this.entry) {
181
- // export specified fields if it has a server side part
182
- if (this.entry.exportFields) {
183
- this.setAttributes(this.entry.exportFields.reduce((prev, field) => {
184
- if (this.data[field] !== undefined) {
185
- prev[field] = this.data[field];
186
- }
187
- return prev;
188
- }, {} as Record<string, any>), 'data-');
189
- }
190
-
191
- // if attributes are present on ComponentEntry, add those to the DOM node
192
- if (this.entry.attributes) {
193
- this.setAttributes(this.entry.attributes, '', false);
194
- }
195
- }
196
-
197
- await this.initChildren();
198
-
199
- // add style display = none to all data-if's
200
- // this will prevent twitching client side
201
- // (otherwise elements that should be hidden might appear for a brief second)
202
- if (this.isRoot) {
203
- const dataIf = this.dom.queryByHasAttribute('data-if');
204
-
205
- for (let i = 0; i < dataIf.length; i++) {
206
- dataIf[i].style.display = 'none';
207
- }
208
- }
209
- }
210
-
211
- public setAttributes(attributes: Record<string, any>, prefix: string = '', encode: boolean = true): void {
212
- if (typeof attributes === 'object' && attributes !== null) {
213
- for (const attr in attributes) {
214
- const encoded = typeof attributes[attr] === 'string' && attributes[attr].indexOf('base64:') === 0;
215
- const value = (encode && !encoded) ? attributeValueToString(attr, attributes[attr]) : attributes[attr];
216
- this.dom.setAttribute(prefix + attr, value);
217
- }
218
- }
219
- }
220
-
221
- private async initChildren(passData?: LooseObject): Promise<void> {
222
- const componentTags = this.document.application.components.componentNames;
223
-
224
- const childNodes = this.dom.queryByTagName(...componentTags);
225
- // const promises: Array<Promise<void>> = [];
226
-
227
- for (let i = 0; i < childNodes.length; i++) {
228
- const childNode = childNodes[i];
229
- const component = this.document.application.components.getByName(childNode.tagName);
230
- if (component) {
231
- const child = new Component(component.name, childNode, this, false);
232
- // promises.push(child.init(childNode.outerHTML, passData));
233
- await child.init(childNode.outerHTML, passData);
234
- this.children.push(child);
235
- }
236
- }
237
-
238
- // await Promise.all(promises);
239
- }
240
-
241
- // use string is coming from data-use attribute defined on the component
242
- // use string can include multiple entries separated by a coma
243
- // each entry can be a simple string which is the key in parent data
244
- // but it can also use array item access key[index] and dot notation key.subkey or a combination key[index].subkey
245
- protected importedParentData(parentData: LooseObject): LooseObject {
246
- if (! this.parent) {
247
- return {};
248
- }
249
-
250
- const data: LooseObject = {}
251
-
252
- if (typeof this.attributes.use !== 'string') {
253
- return data;
254
- }
255
-
256
- // split by a coma and convert into array of "data paths"
257
- // data path is an array of strings and numbers, and it's used to navigate the given parentData and extract a value
258
- const usePaths: Array<Array<string|number>> = this.attributes.use.split(',').map((key) => {
259
- return key.split(/\.|\[(\d+)\]/).filter((s) => {return s !== undefined && s.length > 0 }).map((s) => {
260
- return /^\d+$/.test(s) ? parseInt(s) : s;
261
- });
262
- });
263
-
264
- // try to extract data for each path
265
- usePaths.forEach((dataPath) => {
266
- let dataCurrent:any = parentData;
267
- for (let i = 0; i < dataPath.length; i++) {
268
- const segment = dataPath[i];
269
- if (typeof dataCurrent[segment] === 'undefined') {
270
- // not included in parentData, skip
271
- dataCurrent = undefined;
272
- break;
273
- }
274
- dataCurrent = dataCurrent[segment];
275
- }
276
-
277
- // last segment is the key
278
- const dataKey = dataPath[dataPath.length - 1];
279
-
280
- // set the data
281
- data[dataKey] = dataCurrent;
282
- });
283
-
284
- if (usePaths.length == 1 && typeof usePaths[0][usePaths[0].length - 1] === 'number') {
285
- // if only a single import
286
- // and it ends with a number (indexed array) do not return { number : data }
287
- // instead return the data
288
- return data[usePaths[0][usePaths[0].length - 1]];
289
- }
290
-
291
- return data;
292
- }
293
-
294
- // fill this.attributes and this.attributesRaw using attributes found on domNode
295
- // encode all non-encoded attributes using attributeValueToString
296
- protected initAttributesData(domNode?: DOMNode): void {
297
- if (domNode === undefined) {
298
- domNode = this.dom;
299
- }
300
- for (let i = 0; i < domNode.attributes.length; i++) {
301
- const attrNameRaw = domNode.attributes[i].name;
302
-
303
- // attributes can have a data prefix eg. number:data-num="3"
304
- // return unprefixed attribute name
305
- const attrNameUnprefixed = this.attributeUnprefixed(attrNameRaw);
306
-
307
- if (attrNameUnprefixed.indexOf('data-') === 0) {
308
- // only attributes starting with data- are stored to this.attributes
309
- // rest are only kept in attributesRaw
310
- const attrDataType = this.attributeDataType(attrNameRaw);
311
-
312
- // attributes will usually be encoded using attributeValueToString, decode the value
313
- // using attributeValueFromString, if it was encoded dataDecoded is { key: string, value: any }
314
- // otherwise dataDecoded is a string
315
- const dataDecoded = attributeValueFromString(domNode.attributes[i].value.toString());
316
-
317
- // store the fact whether value was encoded, we need it later
318
- const valueEncoded = typeof dataDecoded === 'object';
319
-
320
- // value in it's raw form
321
- // if the value was encoded it has correct type
322
- // if the value was not encoded it may have incorrect type (solved later)
323
- let value = valueEncoded ? dataDecoded.value as string|number|boolean|LooseObject|null : dataDecoded;
324
-
325
- // key of encoded values is preserved as-is
326
- // key of non-encoded values is in-dashed-form, if so, we convert it to camel case
327
- const key = valueEncoded ? dataDecoded.key : toCamelCase(attrNameUnprefixed.substring(5));
328
-
329
- if (! valueEncoded) {
330
- // value was not encoded
331
- if (typeof value === 'string') {
332
- // data type of value is currently string as the value was not encoded
333
- // data-attr may have had a data type prefix, if so, make sure data type is restored
334
- if (attrDataType === 'number') {
335
- value = parseFloat(value);
336
- } else if (attrDataType === 'boolean') {
337
- value = value === 'true' || value === '1';
338
- } else if (attrDataType === 'object') {
339
- if (typeof value === 'string') {
340
- if (value.trim().length > 1) {
341
- value = JSON.parse(value);
342
- } else {
343
- value = null;
344
- }
345
- }
346
- }
347
- }
348
-
349
- // encode attribute value using attributeValueToString
350
- const attrData = attributeValueToString(key, value);
351
- domNode.setAttribute(attrNameRaw, attrData);
352
- }
353
-
354
- // store value
355
- this.attributes[key] = value;
356
- }
357
- this.attributesRaw[attrNameRaw] = domNode.attributes[i].value;
358
- }
359
- }
360
-
361
- // component attributes can have a data type prefix [prefix]:data-[name]="[val]"
362
- // returns the prefix
363
- private attributePrefix(attrName: string): string|null {
364
- const index = attrName.indexOf(':');
365
- if (index < 0) {
366
- return null;
367
- }
368
- return attrName.substring(0, index);
369
- }
370
-
371
- // returns the user defined data type of given attribute
372
- // for example number:data-total returns 'number'
373
- private attributeDataType(attrName: string): 'string'|'number'|'object'|'boolean'|'any' {
374
- const prefix = this.attributePrefix(attrName);
375
-
376
- if (
377
- prefix === 'string' ||
378
- prefix === 'number' ||
379
- prefix === 'object' ||
380
- prefix === 'boolean'
381
- ) {
382
- return prefix;
383
- }
384
-
385
- // unrecognized attribute prefix
386
- return 'any';
387
- }
388
-
389
- // removes the data-type prefix from given attribute name
390
- // for example number:data-total returns data-total
391
- private attributeUnprefixed(attrName: string): string {
392
- const index = attrName.indexOf(':');
393
- if (index < 0) {
394
- return attrName;
395
- }
396
- return attrName.substring(index + 1);
397
- }
398
-
399
- // compile/fill in data for current component
400
- protected fillData(data: LooseObject): void {
401
- if (this.entry && this.entry.static === true) {
402
- // defined as static component, skip compilation
403
- this.dom.innerHTML = this.entry.html;
404
- return;
405
- }
406
- const html = this.entry ? this.entry.html : this.dom.innerHTML;
407
- this.dom.innerHTML = this.document.application.handlebars.compile(html, data);
408
- }
409
- }
@@ -1,114 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
- import * as path from 'node:path';
3
- import { ComponentEntry, StructuredConfig } from '../Types';
4
- import { Application } from './Application.js';
5
-
6
- export class Components {
7
-
8
- config: StructuredConfig;
9
-
10
- // upper-case component name -> ComponentEntry
11
- private readonly components: Record<string, ComponentEntry> = {};
12
- componentNames: Array<string> = [];
13
-
14
- constructor(app: Application) {
15
- this.config = app.config;
16
- }
17
-
18
- public loadComponents(relativeToPath?: string): void {
19
- if (relativeToPath === undefined) {
20
- relativeToPath = path.resolve((this.config.runtime === 'Node.js' ? '../' : './') + this.config.components.path);
21
- if (! existsSync(relativeToPath)) {
22
- throw new Error(`Components path not found, expected to find:\n${relativeToPath}`);
23
- }
24
- }
25
- const components = readdirSync(relativeToPath);
26
-
27
- components.forEach(async (component) => {
28
- // check if directory
29
- // absolute path to a directory, or the component's HTML file
30
- const absolutePath = relativeToPath + '/' + component;
31
- const isDirectory = statSync(absolutePath).isDirectory();
32
-
33
- if (isDirectory) {
34
- this.loadComponents(absolutePath);
35
- } else {
36
- // file, register component entry
37
- if (component.endsWith('.html') || component.endsWith('.hbs')) {
38
- // remove .html to get componentName
39
- const componentNameParts = component.split('.');
40
- const componentName = componentNameParts.slice(0, componentNameParts.length - 1).join('.');
41
-
42
- const pathAbsolute = relativeToPath || '';
43
- const pathRelative = path.relative(this.config.runtime === 'Node.js' ? '../' : './', pathAbsolute);
44
- const pathBuild = path.resolve('./' + pathRelative);
45
- const pathRelativeToViews = path.relative(`./${this.config.components.path}`, pathRelative);
46
-
47
- const pathHTML = `${pathAbsolute}/${component}`;
48
-
49
- // server side js file path (may not exist)
50
- const jsServerPath = `${pathBuild}/${componentName}.${this.config.runtime === 'Node.js' ? 'js' : 'ts'}`;
51
- const hasServerJS = existsSync(jsServerPath);
52
-
53
- // client side js file path (may not exist)
54
- const jsClientPath = `${pathBuild}/${componentName}.client.${this.config.runtime === 'Node.js' ? 'js' : 'ts'}`;
55
- const hasClientJS = existsSync(jsClientPath);
56
-
57
- const entry: ComponentEntry = {
58
- name: componentName,
59
- path: {
60
- absolute: pathAbsolute,
61
- relative: pathRelative,
62
- relativeToViews: `${pathRelativeToViews}/${component}`,
63
- build: pathBuild,
64
- html: pathHTML,
65
- jsClient: hasClientJS ? jsClientPath : undefined,
66
- jsServer: hasServerJS ? jsServerPath : undefined
67
- },
68
- hasJS : existsSync(jsServerPath),
69
- html: this.loadHTML(absolutePath),
70
- exportData: false,
71
- static: false
72
- }
73
-
74
- // load client side initializer
75
- if (hasClientJS) {
76
- const initializer = await import('file:///' + jsClientPath);
77
- entry.initializer = initializer.init;
78
- }
79
-
80
- if (hasServerJS) {
81
- // load and instantiate component's module
82
- const componentConstructor = await import('file:///' + entry.path.jsServer);
83
- entry.module = new componentConstructor.default();
84
-
85
- entry.renderTagName = entry.module?.tagName || 'div';
86
- entry.exportData = typeof entry.module?.exportData === 'boolean' ? entry.module.exportData : false;
87
- entry.exportFields = entry.module?.exportFields;
88
- entry.attributes = entry.module?.attributes;
89
- entry.static = typeof entry.module?.static === 'boolean' ? entry.module.static : false;
90
- }
91
-
92
- this.components[componentName.toUpperCase()] = entry;
93
- this.componentNames.push(entry.name);
94
- }
95
- }
96
- });
97
- }
98
-
99
- // get component by name
100
- public getByName(name: string): null|ComponentEntry {
101
- return this.components[name.toUpperCase()] || null;
102
- }
103
-
104
- // load HTML from given path
105
- private loadHTML(path: string): string {
106
- return this.stripComments(readFileSync(path).toString());
107
- }
108
-
109
- // remove all HTML comments
110
- private stripComments(html: string): string {
111
- return html.replaceAll(/<!--(?!-?>)(?!.*--!>)(?!.*<!--(?!>)).*?(?<!<!-)-->/g, '');
112
- }
113
-
114
- }
@@ -1,29 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from "node:http";
2
- import { LooseObject } from "../Types.js";
3
-
4
- export class Cookies {
5
-
6
- // parse cookies sent with given request into an object
7
- public parse(request: IncomingMessage): LooseObject {
8
- if (! request.headers.cookie) {return {};}
9
- const cookieString = request.headers.cookie;
10
- const cookiePairs = cookieString.split(';');
11
-
12
- const cookies: LooseObject = {}
13
-
14
- cookiePairs.forEach((cookiePair) => {
15
- const parts = cookiePair.trim().split('=');
16
- cookies[parts.shift() || ''] = parts.join('=');
17
- });
18
-
19
- return cookies;
20
- }
21
-
22
- // set a cookie for given response
23
- // sets the Set-Cookie header, which will be sent with the output
24
- public set(response: ServerResponse, name: string, value: string|number, lifetimeSeconds: number, path: string = '/', sameSite: 'Strict' | 'Lax' | 'None' = 'Strict', domain?: string): void {
25
- const expiresAt = lifetimeSeconds > 0 ? new Date(new Date().getTime() + lifetimeSeconds * 1000).toUTCString() : 0;
26
- response.appendHeader('Set-Cookie', `${name}=${value}; Expires=${expiresAt}; Path=${path}; SameSite=${sameSite}${domain ? `; domain=${domain}` : ''}`);
27
- }
28
-
29
- }