structured-fw 0.8.4 → 0.8.5

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 (95) hide show
  1. package/README.md +116 -18
  2. package/build/system/server/Application.d.ts +3 -1
  3. package/build/system/server/Application.js +11 -0
  4. package/build/system/server/Components.d.ts +1 -1
  5. package/build/system/server/DocumentHead.d.ts +1 -1
  6. package/build/system/server/FormValidation.d.ts +1 -1
  7. package/package.json +10 -3
  8. package/app/models/README.md +0 -9
  9. package/app/routes/README.md +0 -19
  10. package/app/views/README.md +0 -1
  11. package/app/views/layout.html +0 -1
  12. package/build/Config.d.ts +0 -2
  13. package/build/Config.js +0 -30
  14. package/build/app/Types.d.ts +0 -5
  15. package/build/app/Types.js +0 -1
  16. package/build/app/global.d.ts +0 -3
  17. package/build/app/global.js +0 -1
  18. package/build/app/models/Users.d.ts +0 -0
  19. package/build/app/models/Users.js +0 -1
  20. package/build/app/routes/Auth.d.ts +0 -0
  21. package/build/app/routes/Auth.js +0 -1
  22. package/build/app/routes/Test.d.ts +0 -2
  23. package/build/app/routes/Test.js +0 -101
  24. package/build/app/routes/Todo.d.ts +0 -0
  25. package/build/app/routes/Todo.js +0 -1
  26. package/build/app/routes/Upload.d.ts +0 -0
  27. package/build/app/routes/Upload.js +0 -1
  28. package/build/app/routes/Validation.d.ts +0 -2
  29. package/build/app/routes/Validation.js +0 -34
  30. package/build/app/routess/Auth.d.ts +0 -0
  31. package/build/app/routess/Auth.js +0 -1
  32. package/build/app/routess/Test.d.ts +0 -2
  33. package/build/app/routess/Test.js +0 -101
  34. package/build/app/routess/Todo.d.ts +0 -0
  35. package/build/app/routess/Todo.js +0 -1
  36. package/build/app/routess/Upload.d.ts +0 -0
  37. package/build/app/routess/Upload.js +0 -1
  38. package/build/app/routess/Validation.d.ts +0 -2
  39. package/build/app/routess/Validation.js +0 -34
  40. package/build/app/views/components/ClientImport/ClientImport.client.d.ts +0 -2
  41. package/build/app/views/components/ClientImport/ClientImport.client.js +0 -4
  42. package/build/app/views/components/ClientImport/Export.d.ts +0 -1
  43. package/build/app/views/components/ClientImport/Export.js +0 -1
  44. package/build/app/views/components/Conditionals/Conditionals.client.d.ts +0 -2
  45. package/build/app/views/components/Conditionals/Conditionals.client.js +0 -43
  46. package/build/app/views/components/FormTest/FormTestNested/FormTestNested.d.ts +0 -8
  47. package/build/app/views/components/FormTest/FormTestNested/FormTestNested.js +0 -7
  48. package/build/app/views/components/ModelsTest/ModelsTest.client.d.ts +0 -2
  49. package/build/app/views/components/ModelsTest/ModelsTest.client.js +0 -5
  50. package/build/app/views/components/MultipartForm/MultipartForm.client.d.ts +0 -0
  51. package/build/app/views/components/MultipartForm/MultipartForm.client.js +0 -1
  52. package/build/app/views/components/PassObject/PassObject.d.ts +0 -10
  53. package/build/app/views/components/PassObject/PassObject.js +0 -10
  54. package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.d.ts +0 -6
  55. package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.js +0 -6
  56. package/build/app/views/components/RedrawAbort/RedrawAbort.client.d.ts +0 -2
  57. package/build/app/views/components/RedrawAbort/RedrawAbort.client.js +0 -6
  58. package/build/app/views/components/RedrawAbort/RedrawAbort.d.ts +0 -8
  59. package/build/app/views/components/RedrawAbort/RedrawAbort.js +0 -8
  60. package/build/app/views/components/ServerSideContext/ServerSideContext.d.ts +0 -7
  61. package/build/app/views/components/ServerSideContext/ServerSideContext.js +0 -10
  62. package/build/assets/ts/Export.d.ts +0 -1
  63. package/build/assets/ts/Export.js +0 -1
  64. package/build/index.d.ts +0 -1
  65. package/build/index.js +0 -8
  66. package/build/tsconfig.tsbuildinfo +0 -1
  67. package/jsr.json +0 -35
  68. package/system/EventEmitter.ts +0 -38
  69. package/system/Helpers.ts +0 -97
  70. package/system/Symbols.ts +0 -6
  71. package/system/Types.ts +0 -232
  72. package/system/Util.ts +0 -488
  73. package/system/bin/structured.ts +0 -115
  74. package/system/client/App.ts +0 -11
  75. package/system/client/Client.ts +0 -9
  76. package/system/client/ClientComponent.ts +0 -1107
  77. package/system/client/DataStore.ts +0 -101
  78. package/system/client/DataStoreView.ts +0 -82
  79. package/system/client/Net.ts +0 -58
  80. package/system/client/NetRequest.ts +0 -64
  81. package/system/global.d.ts +0 -12
  82. package/system/server/Application.ts +0 -239
  83. package/system/server/Component.ts +0 -409
  84. package/system/server/Components.ts +0 -114
  85. package/system/server/Cookies.ts +0 -29
  86. package/system/server/Document.ts +0 -163
  87. package/system/server/DocumentHead.ts +0 -150
  88. package/system/server/FormValidation.ts +0 -231
  89. package/system/server/Handlebars.ts +0 -51
  90. package/system/server/Request.ts +0 -502
  91. package/system/server/Session.ts +0 -151
  92. package/system/server/dom/DOMFragment.ts +0 -7
  93. package/system/server/dom/DOMNode.ts +0 -140
  94. package/system/server/dom/HTMLParser.ts +0 -238
  95. package/tsconfig.json +0 -31
package/README.md CHANGED
@@ -5,6 +5,9 @@ Framework allows the developer to develop self-contained components which are re
5
5
 
6
6
  It works with Node.js and Deno runtimes. Other runtimes are not tested.
7
7
 
8
+ > [!NOTE]
9
+ > While Structured framework is in development for a couple of years and is production tested, the npm package is introduced recently and there were still issues that came up with it in the past few versions. Since version 0.8.3 all should be functional. Please update to latest version and check for updates regularly until the npm package is as stable as the framework itself. Feel free to open issues on the github page if you have any issues with the npm package or the framework. _Structured followed versioning x.y.z where z was a single digit 0-9, but since there were a couple of versions that introduced no changes to the framework and were just npm package tweaks, I decided to make an exception to the rule and allow myself to use 2 digits for such updates._
10
+
8
11
  - [Why Structured](#why-structured)
9
12
  - [Audience](#audience)
10
13
  - [Getting started](#getting-started)
@@ -38,6 +41,17 @@ npm install @types/node
38
41
  ### Create boilerplate
39
42
  `npx structured init`
40
43
 
44
+ ### Create a test route
45
+ Create a file `/app/routes/Test.ts`:
46
+ ```
47
+ import { Application } from 'structured-fw/Application';
48
+ export default function(app: Application) {
49
+ app.request.on('GET', '/test', async()=> {
50
+ return 'Hello, World!';
51
+ });
52
+ }
53
+ ```
54
+
41
55
  ### Compile
42
56
  `tsc`\
43
57
  This will create a directory `build` (or whatever you have in tsconfig.json as compilerOptions.outputDir)
@@ -54,6 +68,8 @@ cd build
54
68
  pm2 start index.js --name="[appName]"
55
69
  ```
56
70
 
71
+ If you followed the above steps, you should be able to access `http://localhost:9191/test` in your browser and see the output `Hello, World!`.
72
+
57
73
  # Key concepts
58
74
 
59
75
  ## Application
@@ -344,13 +360,25 @@ app.request.on('GET', '/home', async (ctx) => {
344
360
  });
345
361
  ```
346
362
 
363
+ > [!TIP]
364
+ > Since version 0.8.4 Document extends EventEmitter, and "componentCreated" event is emitted whenever a component instance is created within the Document.\
365
+ > This makes the following possible:
366
+ > ```
367
+ > app.on('documentCreated', (doc) => {
368
+ > doc.on('componentCreated', (component) => {
369
+ > // do something with the document or the component
370
+ > })
371
+ > })
372
+ > ```
373
+
347
374
  ## Component
348
- A component is comprised of 1-3 files. It always must include one HTML file, while server side and client side files are optional.
375
+ A component is comprised of [1-3 files](#component-parts). It always must include one HTML file, while server side and client side files are optional.
349
376
  * HTML file probably requires no explanation
350
377
  * server side file, code that runs on the server and makes data available to HTML and client side code
351
378
  * client side file, code that runs on the client (in the browser)
352
379
 
353
- You should never need to instantiate a Component on your own. You will always load a Component representing your page into a document (using `Document.loadComponent(componentName: string)`), which will know what to do from there.
380
+ > [!TIP]
381
+ > You should never need to instantiate a Component on your own. You will always load a Component representing your page into a document (using `Document.loadComponent(componentName: string)`), which will know what to do from there.
354
382
 
355
383
  Example component files:
356
384
  - `/app/views/`
@@ -370,6 +398,12 @@ It is recommended, but not necessary, that you contain each component in it's ow
370
398
  - Components HTML file can have a `.hbs` extension (which allows for better Handlebars syntax highlighting)
371
399
  - Components can reside at any depth in the file structure
372
400
 
401
+ ### Component parts
402
+ - [Component HTML](#component-html) (_ComponentName.html_)
403
+ - [Component server-side code](#component-server-side-code) (_ComponentName.ts_)
404
+ - [Component client-side code](#component-client-side-code) (_ComponentName.client.ts_)
405
+
406
+ ### Component HTML
373
407
  Let's create a HelloWorld Component `/app/views/HelloWorld/HelloWorld.html`:\
374
408
  `Hello, World!`
375
409
 
@@ -389,10 +423,15 @@ export default function(app: Application) {
389
423
  You can now run the app and if you open /hello/world in the browser you will see:\
390
424
  `Hello, World!` - which came from your HelloWorld component.
391
425
 
392
- That was the simplest possible example, let's make it more interesting.
426
+ > [!TIP]
427
+ > It is recommended to use .hbs (Handlebars) extension as you will get better syntax highlighting in most IDEs. Other than syntax highlighting there is no difference between using html or hbs extension.
428
+
429
+ That was the simplest possible example, let's make it more interesting by adding some server-side code.
430
+
431
+ ### Component server-side code
393
432
  Create a new file `/app/views/HelloWorld/HelloWorld.ts` (server side component code):
394
433
  ```
395
- import { ComponentScaffold } from 'system/Types.js';
434
+ import { ComponentScaffold } from 'structured-fw/Types';
396
435
  export default class HelloWorld implements ComponentScaffold {
397
436
  async getData(): Promise<{
398
437
  luckyNumber: number
@@ -423,10 +462,18 @@ Your lucky number is [a number from 0-100]
423
462
  This demonstrates the use of a *server side component code* to make data available to HTML.
424
463
  We just generated a random number, but the data could be anything and will more often come from a database, session, or be provided by the parent component.
425
464
 
465
+ > [!IMPORTANT]
466
+ > Server side `getData` will receive the following arguments:
467
+ > - `data: LooseObject` any data passed in (either by attributes, ClientComponent.add or ClientComponent.redraw)
468
+ > - `ctx: RequestContext` - current `RequestContext`, you will often use this to access for example ctx.data (`RequestContextData`) or ctx.sessionId to interact with session
469
+ > - `app: Application` - your Application instance. You can use it to, for example, access the session in combination with ctx.sessionId
470
+
426
471
  Let's make it even more interesting by adding some client side code to it.
472
+
473
+ ### Component client-side code
427
474
  Create `/app/views/HelloWorld/HelloWorld.client.ts`:
428
475
  ```
429
- import { InitializerFunction } from 'system/Types.js';
476
+ import { InitializerFunction } from 'structured-fw/Types';
430
477
  export const init: InitializerFunction = async function() {
431
478
  const generateNew = this.ref<HTMLButtonElement>('newNumber');
432
479
 
@@ -475,7 +522,7 @@ Parent says your lucky number is {{number}}.
475
522
  That's it. Since `AnotherComponent` has no server side code, all data passed to it is exported to HTML, hence the `number` you passed from `HelloWorld` will be readily available for use. If AnotherComponent had a server side part, the process is a bit different, it will receive it as part of the `data`, but can choose whether to make it available to the HTML, or just make use of it and return other stuff. Let's see how that works.
476
523
  Create `/app/views/AnotherComponent/AnotherComponent.ts`:
477
524
  ```
478
- import { ComponentScaffold } from 'system/Types.js';
525
+ import { ComponentScaffold } from 'structured-fw/Types';
479
526
  export default class AnotherComponent implements ComponentScaffold {
480
527
  async getData(data: { number: number }): Promise<{
481
528
  parentSuggests: number,
@@ -506,15 +553,9 @@ What about client side? **By default, data returned by server side code is not a
506
553
  > [!NOTE]
507
554
  > Whenever a component with server-side code is rendered, `getData` is automatically called and anything it returns is available in HTML. You can export all returned data to client-side code by setting `exportData = true` or you can export some of the fields by setting `exportFields = ["field1", "field2", ...]` as a direct property of the class. To access the exported data from client-side use `ClientComponent`.`getData(key: string)` which will be `this.getData(key:string)` within client side code.
508
555
 
509
- > [!IMPORTANT]
510
- > Server side `getData` will receive the following arguments:
511
- > - `data: LooseObject` any data passed in (either by attributes, ClientComponent.add or ClientComponent.redraw)
512
- > - `ctx: RequestContext` - current `RequestContext`, you will often use this to access for example ctx.data (`RequestContextData`) or ctx.sessionId to interact with session
513
- > - `app: Application` - your Application instance. You can use it to, for example, access the session in combination with ctx.sessionId
514
-
515
556
  Let's create a client side code for `AnotherComponent` and export the `betterNumber` to it, create `/app/views/AnotherComponent/AnotherComponent.client.ts`:
516
557
  ```
517
- import { InitializerFunction } from 'system/Types.js';
558
+ import { InitializerFunction } from 'structured-fw/Types';
518
559
  export const init: InitializerFunction = async function() {
519
560
  const betterNumber = this.getData<number>('betterNumber');
520
561
 
@@ -524,7 +565,7 @@ export const init: InitializerFunction = async function() {
524
565
 
525
566
  And let's update `AnotherComponent.ts` to export `betterNumber`:
526
567
  ```
527
- import { ComponentScaffold } from 'system/Types.js';
568
+ import { ComponentScaffold } from 'structured-fw/Types';
528
569
  export default class AnotherComponent implements ComponentScaffold {
529
570
  exportFields = ['betterNumber'];
530
571
  async getData(data: { number: number }): Promise<{
@@ -546,7 +587,7 @@ This concept is wrong to start with, if we want a component to be independent, i
546
587
 
547
588
  Let's say we wanted to access the `parent` Component from `AnotherComponent`:
548
589
  ```
549
- import { InitializerFunction } from 'system/Types.js';
590
+ import { InitializerFunction } from 'structured-fw/Types';
550
591
  export const init: InitializerFunction = async function() {
551
592
  const betterNumber = this.getData<number>('betterNumber');
552
593
 
@@ -557,7 +598,7 @@ Here we accessed the `parent` and obtained it's `name`.
557
598
 
558
599
  *"But we did not send any data to the parent here"* - correct, we did not, and we won't, instead we can inform them we have some data available, or that an event they might be interested in has occurred, and if they care, so be it:
559
600
  ```
560
- import { InitializerFunction } from 'system/Types.js';
601
+ import { InitializerFunction } from 'structured-fw/Types';
561
602
  export const init: InitializerFunction = async function() {
562
603
  const betterNumber = this.getData<number>('betterNumber');
563
604
 
@@ -567,7 +608,7 @@ export const init: InitializerFunction = async function() {
567
608
 
568
609
  We emitted an `event` with `eventName` = "`truth`" and a `payload`, which in this case is a string, but can be of any type. If the parent cares about it (or for that matter, not necessarily the parent, but anyone in the component tree), they can subscribe to that event. Let's subscribe to the event from `HelloWorld` (`HelloWorld.client.ts`):
569
610
  ```
570
- import { InitializerFunction } from 'system/Types.js';
611
+ import { InitializerFunction } from 'structured-fw/Types';
571
612
  export const init: InitializerFunction = async function() {
572
613
 
573
614
  const child = this.find('AnotherComponent'); // ClientComponent | null
@@ -608,13 +649,67 @@ Methods:
608
649
  - `query(componentName: string, recursive: boolean = true): Array<ClientComponent>` - return all components with given name found within this component, if `recursive = false`, only direct children are considered
609
650
  - `ref<T>(refName: string): T` - get a HTMLElement or ClientComponent that has attribute `ref="[refName]"`
610
651
  - `arrayRef<T>(refName: string): Array<T>` - get an array of HTMLElement or ClientComponent that have attribute `array:ref="[refName]"`
611
- - `add(appendTo: HTMLElement, componentName: string, data?: LooseObject)` - add `componentName` component to `appendTo` element, optionally passing `data` to the component when it's being rendered
652
+ - `add(appendTo: HTMLElement, componentName: string, data?: LooseObject): Promise<ClientComponent | null>` - add `componentName` component to `appendTo` element, optionally passing `data` to the component when it's being rendered. Returns a promise that resolves with added ClientComponent or null if something went wrong
653
+ - `redraw(data?: LooseObject): Promise<void>` - redraw the component, optionally provide data which will be available server side
654
+
655
+ ### Conditionals
656
+ You can make any DOM node within your components conditionally shown/hidden using `data-if` attribute.\
657
+ For example:
658
+ ```
659
+ <div data-if="showDiv"></div>
660
+ ```
661
+ Above div will only be shown if store.showDiv = true
662
+
663
+ You can also use `!` to invert the value, `!showDiv` in which case div would be shown if showDiv is false.
664
+
665
+ You can also use comparison:
666
+ ```
667
+ <div data-if="val === 1"></div>
668
+ <div data-if="val == 1"></div>
669
+ <div data-if="val !== 1"></div>
670
+ <div data-if="val != 1"></div>
671
+ <div data-if="val > 1"></div>
672
+ <div data-if="val < 1"></div>
673
+ <div data-if="val <= 1"></div>
674
+ <div data-if="val >= 1"></div>
675
+ ```
676
+
677
+ The right hand side of the comparison does not have to be boolean or number. It can be a string or any primitive value, but the numeric comparisons don't make sense in such case.
678
+
679
+ You can also define callbacks and use them as the condition, in you ComponentName.client.ts:
680
+ ```
681
+ import { InitializerFunction } from 'structured-fw/Types';
682
+ export const init: InitializerFunction = async function() {
683
+ this.conditionalCallback('showDiv', () => {
684
+ // return a boolean here
685
+ });
686
+ }
687
+ ```
688
+
689
+ then in ComponentName.html:
690
+ ```
691
+ <div data-if="showDiv()"></div>
692
+ ```
693
+
694
+ **Basic animation/transitions**\
695
+ If you use conditionals on any DOM node, you may also enable basic animations/transitions using following attributes:
696
+ - Enable transition:
697
+ - `data-transition-show-slide="durationMilliseconds"` - when DOM node is shown, slide it in
698
+ - `data-transition-hide-slide="durationMilliseconds"` - when DOM node is hidden, slide it out
699
+ - `data-transition-show-fade="durationMilliseconds"` - fade DOM node in
700
+ - `data-transition-hide-fade="durationMilliseconds"` - fade DOM node out
701
+ - Modify transition (slide only)
702
+ - `data-transform-origin-show="CSS transform origin"` - from where does the component slide in for example `0% 50%` to slide it in from mid-left
703
+ - `data-transform-origin-hide="CSS transform origin"` - where does the component slide out to for example `100% 100%` to slide it out to bottom-right
704
+ - `data-transition-axis-show="X | Y"` - slide animation axis
705
+ - `data-transition-axis-hide="X | Y"` - slide animation axis
612
706
 
613
707
  ## Good to know
614
708
  - [Using CSS frameworks](#css-frameworks)
615
709
  - [Using JS runtimes other than Node.js](#runtimes)
616
710
  - [Why not JSR](#jsr)
617
711
  - [Best practices](#best-practices)
712
+ - [Having an issue?](#issues-and-feedback)
618
713
 
619
714
  ### CSS frameworks
620
715
  We rarely write all CSS from scratch, usually we use a CSS framework to speed us up. Structured allows you to work with any CSS frameworks such as Tailwind, PostCSS or Bootstrap.
@@ -759,6 +854,9 @@ If you ran `npx structured init`, it has created /app/models for you. Structured
759
854
 
760
855
  You can create additional code separation, for example, it would make sense to have /app/lib for code that interfaces an API, or have /app/Util.ts where you export utility functions. Structured boilerplate does not include these as not all applications will need them.
761
856
 
857
+ ### Issues and feedback
858
+ If you have any issues with the framework or the npm package, please don't hesitate to open an issue on [github](https://github.com/julijan/structured). Feedback is also welcome!
859
+
762
860
  ## Why Structured
763
861
  Framework was developed by someone who has been a web developer for almost 20 years (me), and did not like the path web development has taken.
764
862
  \
@@ -1,5 +1,5 @@
1
1
  import { Server } from 'node:http';
2
- import { ApplicationEvents, LooseObject, RequestContext, StructuredConfig } from '../Types';
2
+ import { ApplicationEvents, LooseObject, RequestContext, StructuredConfig } from '../Types.js';
3
3
  import { Document } from './Document.js';
4
4
  import { Components } from './Components.js';
5
5
  import { Session } from './Session.js';
@@ -8,6 +8,7 @@ import { Handlebars } from './Handlebars.js';
8
8
  import { Cookies } from './Cookies.js';
9
9
  export declare class Application {
10
10
  readonly config: StructuredConfig;
11
+ private initialized;
11
12
  server: null | Server;
12
13
  listening: boolean;
13
14
  private readonly eventEmitter;
@@ -25,6 +26,7 @@ export declare class Application {
25
26
  importEnv<T extends LooseObject>(smartPrimitives?: boolean): T;
26
27
  exportContextFields(...fields: Array<keyof RequestContextData>): void;
27
28
  contentType(extension: string): string | false;
29
+ registerPlugin(callback: (app: Application) => void): void;
28
30
  private respondWithComponent;
29
31
  memoryUsage(): NodeJS.MemoryUsage;
30
32
  printMemoryUsage(): void;
@@ -12,6 +12,7 @@ import { Handlebars } from './Handlebars.js';
12
12
  import { Cookies } from './Cookies.js';
13
13
  export class Application {
14
14
  constructor(config) {
15
+ this.initialized = false;
15
16
  this.server = null;
16
17
  this.listening = false;
17
18
  this.eventEmitter = new EventEmitter();
@@ -28,6 +29,9 @@ export class Application {
28
29
  }
29
30
  }
30
31
  async init() {
32
+ if (this.initialized) {
33
+ return;
34
+ }
31
35
  this.eventEmitter.setMaxListeners(10);
32
36
  try {
33
37
  await this.handlebars.loadHelpers('../Helpers.js');
@@ -63,6 +67,7 @@ export class Application {
63
67
  return '';
64
68
  }, this, true);
65
69
  await this.start();
70
+ this.initialized = true;
66
71
  }
67
72
  start() {
68
73
  return new Promise((resolve, reject) => {
@@ -136,6 +141,12 @@ export class Application {
136
141
  contentType(extension) {
137
142
  return mime.contentType(extension);
138
143
  }
144
+ registerPlugin(callback) {
145
+ if (this.initialized) {
146
+ console.warn('Plugin registered after app is initialized, some plugin features may not work.');
147
+ }
148
+ callback(this);
149
+ }
139
150
  async respondWithComponent(ctx, componentName, attributes, unwrap = true) {
140
151
  const component = this.components.getByName(componentName);
141
152
  if (component) {
@@ -1,4 +1,4 @@
1
- import { ComponentEntry, StructuredConfig } from '../Types';
1
+ import { ComponentEntry, StructuredConfig } from '../Types.js';
2
2
  import { Application } from './Application.js';
3
3
  export declare class Components {
4
4
  config: StructuredConfig;
@@ -1,4 +1,4 @@
1
- import { DocumentResource } from '../Types';
1
+ import { DocumentResource } from '../Types.js';
2
2
  export declare class DocumentHead {
3
3
  title: string;
4
4
  js: Array<DocumentResource>;
@@ -1,4 +1,4 @@
1
- import { FormValidationEntry, PostedDataDecoded, ValidationResult, ValidationRuleWithArguments, ValidatorErrorDecorator, ValidatorFunction } from '../Types';
1
+ import { FormValidationEntry, PostedDataDecoded, ValidationResult, ValidationRuleWithArguments, ValidatorErrorDecorator, ValidatorFunction } from '../Types.js';
2
2
  export declare class FormValidation {
3
3
  fieldRules: Array<FormValidationEntry>;
4
4
  singleError: boolean;
package/package.json CHANGED
@@ -14,13 +14,12 @@
14
14
  "license": "MIT",
15
15
  "type": "module",
16
16
  "main": "build/index",
17
- "version": "0.8.4",
17
+ "version": "0.8.5",
18
18
  "scripts": {
19
19
  "develop": "tsc --watch",
20
20
  "startDev": "cd build && nodemon --watch '../app/**/*' --watch '../build/**/*' -e js,html,css index.js",
21
21
  "start": "cd build && node index.js",
22
- "prepublish": "tsc && mv tsconfig.json _ts.json",
23
- "postpublish": "mv _ts.json tsconfig.json"
22
+ "prepublish": "tsc"
24
23
  },
25
24
  "bin": {
26
25
  "structured": "./build/system/bin/structured.js"
@@ -35,6 +34,14 @@
35
34
  "mime-types": "^3.0.0",
36
35
  "ts-md5": "^1.3.1"
37
36
  },
37
+ "files": [
38
+ "build/system/*",
39
+ "app/Types.ts",
40
+ "index.ts",
41
+ "Config.ts",
42
+ "README.md",
43
+ "LICENSE"
44
+ ],
38
45
  "exports" : {
39
46
  "./Types": "./build/system/Types.js",
40
47
  "./Symbols": "./build/system/Symbols.js",
@@ -1,9 +0,0 @@
1
- You don't have to use this directory, but if you are keen on MVC concept, this would be the place for your models.
2
-
3
- There are not specific rules for these files. They are never loaded automaticallt by the framework, instead you should
4
- load them where you want to use them.
5
-
6
- Usually a model will export a class or a class instance, but it could be a function, set of functions, or anything really.
7
-
8
- Models are meant to abstract the logic and data layer for a specific entity of your app. For example a web shop would likely
9
- have a model Product.ts which exports a class with methods such as create(productData: ProductData) to create a new product and delete(productId: number) to delete an existing product.
@@ -1,19 +0,0 @@
1
- Routes:
2
-
3
- You can add routes directly in index.ts using app.request.on
4
- but in order to avoid clutter and keep the code structured, you should add the routes here (/app/routes)
5
-
6
- All files in this directory will be loaded once the server is started,
7
- so feel free to separate your routes in as many files as you feel makes sense, eg. it would make sense
8
- to have Auth.ts that would add routes for everything auth related, such as /login, /register, etc...
9
-
10
- Example:
11
-
12
- export default async function(app: Application) {
13
-
14
- app.request.on('GET', '/login', async (ctx) => {
15
- // ctx is a RequestContext
16
- ctx.response.write('Login page');
17
- });
18
-
19
- }
@@ -1 +0,0 @@
1
- Place for pages and components
@@ -1 +0,0 @@
1
- {{{layoutComponent component data attributes}}}
package/build/Config.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { StructuredConfig } from 'structured-fw/Types';
2
- export declare const config: StructuredConfig;
package/build/Config.js DELETED
@@ -1,30 +0,0 @@
1
- export const config = {
2
- envPrefix: 'STRUCTURED',
3
- autoInit: true,
4
- url: {
5
- removeTrailingSlash: true,
6
- componentRender: '/componentRender',
7
- isAsset: function (uri) {
8
- return uri.indexOf('/assets/') === 0;
9
- }
10
- },
11
- routes: {
12
- path: '/app/routes'
13
- },
14
- components: {
15
- path: '/app/views',
16
- componentNameAttribute: 'structured-component'
17
- },
18
- session: {
19
- cookieName: 'session',
20
- keyLength: 24,
21
- durationSeconds: 60 * 60,
22
- garbageCollectIntervalSeconds: 60
23
- },
24
- http: {
25
- port: 9191,
26
- host: '0.0.0.0',
27
- linkHeaderRel: 'preload'
28
- },
29
- runtime: 'Node.js'
30
- };
@@ -1,5 +0,0 @@
1
- export {};
2
- declare global {
3
- interface RequestContextData {
4
- }
5
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,3 +0,0 @@
1
- export interface RequestContextData {
2
- user: string;
3
- }
@@ -1 +0,0 @@
1
- export {};
File without changes
@@ -1 +0,0 @@
1
- "use strict";
File without changes
@@ -1 +0,0 @@
1
- "use strict";
@@ -1,2 +0,0 @@
1
- import { Application } from '../../system/server/Application.js';
2
- export default function (app: Application): void;
@@ -1,101 +0,0 @@
1
- import { Document } from '../../system/server/Document.js';
2
- import { Request } from '../../system/server/Request.js';
3
- export default function (app) {
4
- app.request.on('GET', '/test/form', async (ctx) => {
5
- const doc = new Document(app, 'Form test', ctx);
6
- await doc.loadComponent('FormTestNested', { test: 3 });
7
- return doc;
8
- });
9
- app.request.on('POST', '/test/form', async (ctx) => {
10
- console.log(JSON.stringify(ctx.body, undefined, 4));
11
- const userImage = ctx.files.user.image[0];
12
- ctx.response.setHeader('Content-Type', userImage.type);
13
- ctx.respondWith(userImage.data);
14
- });
15
- app.request.on('GET', '/test/client_import', async (ctx) => {
16
- const doc = new Document(app, 'Test', ctx);
17
- await doc.loadComponent('ClientImport', { xyz: 10, asd: 12 });
18
- ctx.respondWith(doc);
19
- });
20
- app.request.on('GET', '/test/redraw', async (ctx) => {
21
- const doc = new Document(app, 'Test', ctx);
22
- await doc.loadComponent('RedrawAbort');
23
- ctx.respondWith(doc);
24
- });
25
- app.request.on('GET', '/test/models', async (ctx) => {
26
- const doc = new Document(app, 'Test', ctx);
27
- await doc.loadComponent('ModelsTest');
28
- ctx.respondWith(doc);
29
- });
30
- app.request.on('GET', '/getargs', async (ctx) => {
31
- ctx.respondWith(ctx.getArgs);
32
- });
33
- app.request.on('GET', '/form/multipart', async (ctx) => {
34
- const doc = new Document(app, 'Test multipart form', ctx);
35
- await doc.loadComponent('MultipartForm');
36
- ctx.respondWith(doc);
37
- });
38
- app.request.on('POST', '/form/multipart', async (ctx) => {
39
- console.log(ctx.files);
40
- ctx.respondWith(ctx.body);
41
- });
42
- app.request.on('GET', '/conditional', async (ctx) => {
43
- const doc = new Document(app, 'Test multipart form', ctx);
44
- await doc.loadComponent('Conditionals');
45
- ctx.respondWith(doc);
46
- });
47
- app.request.on('GET', '/urldecode', async (ctx) => {
48
- const noVal = 'noVal';
49
- const noValNested = 'filters[beds][min]';
50
- const simple = 'simple=2';
51
- const simpleObj = 'person[name]=fname&person[last_name]=lname';
52
- const simpleArray = 'colors[]=red&colors[]=blue';
53
- const orderedArray = 'colorsOrdered[1]=red&colorsOrdered[0]=blue';
54
- const indexedArray = 'months[0]=Jan&months[1]=Feb&months[2]=Mar';
55
- const arrayOfObjects = 'users[0][name]=John&users[0][email]=johndoe@gmail.com&users[1][name]=Tim&users[1][email]=tim@gmail.com';
56
- const objectWithArrayValues = 'user[name]=John&user[last_name]=Doe&user[sports][]=table tennis&user[sports][]=football';
57
- const objectWithObjectValues = 'data[paper][props][size]=10x13&data[paper][props][type]=matte';
58
- const nestedArraySimple = 'colorStack[0][]=red&colorStack[0][]=blue&colorStack[1][]=green';
59
- const arrayOfObjectsWithArrayValues = 'usersArr[0][name]=John&usersArr[0][email]=johndoe@gmail.com&usersArr[0][sports][]=football&usersArr[0][sports][]=basketball&usersArr[1][name]=Too&usersArr[1][email]=tootoo@gmail.com&usersArr[1][sports][]=bocce&usersArr[1][sports][]=cricket&usersArr[1][sports][]=dancing';
60
- const missingValue = 'missing=';
61
- const missingArrayValue = 'missingArr[]=';
62
- const objBlankValue = `objBlank[0][email]=`;
63
- const spaces = `spaces=value%20with%20spaces`;
64
- const special = `garbage=` + encodeURIComponent('value!@#&$%^*()');
65
- const nonLatin = `key3=` + encodeURIComponent('привет');
66
- const t = new Date().getTime();
67
- const test = Request.queryStringDecode([noVal, noValNested, simple, simpleObj, simpleArray, orderedArray, indexedArray, arrayOfObjects, objectWithArrayValues, objectWithObjectValues, nestedArraySimple, arrayOfObjectsWithArrayValues, missingValue, nonLatin, spaces, special, objBlankValue, missingArrayValue, simple].join('&'));
68
- const dur = new Date().getTime() - t;
69
- console.log(dur);
70
- ctx.respondWith(test);
71
- });
72
- app.request.on('GET', '/client_import', async (ctx) => {
73
- const doc = new Document(app, 'Test multipart form', ctx);
74
- await doc.loadComponent('ClientImport');
75
- doc.head.add(`
76
- <script type="importmap">
77
- {
78
- imports: {
79
- 'components/*' : '/build/app/views/components/*'
80
- }
81
- }
82
- </script>
83
- `);
84
- ctx.respondWith(doc);
85
- });
86
- app.request.on('GET', '/serverclass', async (ctx) => {
87
- const doc = new Document(app, 'Test multipart form', ctx);
88
- await doc.loadComponent('ServerSideContext');
89
- ctx.respondWith(doc);
90
- });
91
- app.request.on('GET', '/routes/new', async (ctx) => {
92
- const doc = new Document(app, 'Test multipart form', ctx);
93
- await doc.loadComponent('Conditionals');
94
- return doc;
95
- });
96
- app.request.on('GET', '/passObj', async (ctx) => {
97
- const doc = new Document(app, 'Test pass obj', ctx);
98
- await doc.loadComponent('PassObject');
99
- ctx.respondWith(doc);
100
- });
101
- }
File without changes
@@ -1 +0,0 @@
1
- "use strict";
File without changes
@@ -1 +0,0 @@
1
- "use strict";
@@ -1,2 +0,0 @@
1
- import { Application } from "../../system/server/Application.js";
2
- export default function (app: Application): void;
@@ -1,34 +0,0 @@
1
- import { FormValidation } from '../../system/server/FormValidation.js';
2
- import { Document } from "../../system/server/Document.js";
3
- export default function (app) {
4
- let validator = new FormValidation();
5
- validator.singleError = true;
6
- validator.addRule('name', 'Name', ['required', ['minLength', 3]]);
7
- validator.addRule('email', 'Email', ['validEmail']);
8
- validator.addRule('number', 'Number', ['number', 'required']);
9
- validator.addRule('numeric', 'Numeric', ['numeric', 'required']);
10
- validator.addRule('float', 'Float', ['float', 'required']);
11
- app.request.on('POST', '/validation', async (ctx) => {
12
- if (ctx.body) {
13
- let validationResult = await validator.validate(ctx.body);
14
- if (validationResult.valid) {
15
- ctx.response.write('Valid');
16
- }
17
- else {
18
- app.session.setValue(ctx.sessionId, 'validationErrors', validationResult.errors);
19
- app.session.setValue(ctx.sessionId, 'formValues', ctx.body);
20
- app.request.redirect(ctx.response, '/validation');
21
- }
22
- }
23
- });
24
- app.request.on('GET', '/validation', async (ctx) => {
25
- let doc = new Document(app, 'Form validation');
26
- await doc.loadView('pages/validation', app.session.extract(ctx.sessionId, [
27
- { validationErrors: 'errors' },
28
- { formValues: 'values' }
29
- ]));
30
- ctx.response.write(doc.toString());
31
- app.session.removeValue(ctx.sessionId, 'validationErrors');
32
- app.session.removeValue(ctx.sessionId, 'formValues');
33
- });
34
- }
File without changes
@@ -1 +0,0 @@
1
- "use strict";
@@ -1,2 +0,0 @@
1
- import { Application } from '../../system/server/Application.js';
2
- export default function (app: Application): void;