@relax.js/core 1.0.3 → 1.0.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 (90) hide show
  1. package/README.md +194 -188
  2. package/dist/DependencyInjection.d.ts +45 -27
  3. package/dist/collections/LinkedList.d.ts +9 -8
  4. package/dist/collections/index.js +1 -1
  5. package/dist/collections/index.js.map +3 -3
  6. package/dist/collections/index.mjs +1 -1
  7. package/dist/collections/index.mjs.map +3 -3
  8. package/dist/di/index.js +1 -1
  9. package/dist/di/index.js.map +3 -3
  10. package/dist/di/index.mjs +1 -1
  11. package/dist/di/index.mjs.map +3 -3
  12. package/dist/elements/index.js +1 -1
  13. package/dist/elements/index.js.map +1 -1
  14. package/dist/errors.d.ts +20 -0
  15. package/dist/forms/FormValidator.d.ts +3 -22
  16. package/dist/forms/ValidationRules.d.ts +4 -6
  17. package/dist/forms/index.js +1 -1
  18. package/dist/forms/index.js.map +4 -4
  19. package/dist/forms/index.mjs +1 -1
  20. package/dist/forms/index.mjs.map +4 -4
  21. package/dist/forms/setFormData.d.ts +39 -1
  22. package/dist/html/TableRenderer.d.ts +1 -0
  23. package/dist/html/index.js +1 -1
  24. package/dist/html/index.js.map +3 -3
  25. package/dist/html/index.mjs +1 -1
  26. package/dist/html/index.mjs.map +3 -3
  27. package/dist/html/template.d.ts +4 -0
  28. package/dist/http/ServerSentEvents.d.ts +1 -1
  29. package/dist/http/SimpleWebSocket.d.ts +1 -1
  30. package/dist/http/http.d.ts +1 -0
  31. package/dist/http/index.js +1 -1
  32. package/dist/http/index.js.map +3 -3
  33. package/dist/http/index.mjs +1 -1
  34. package/dist/http/index.mjs.map +3 -3
  35. package/dist/i18n/icu.d.ts +1 -1
  36. package/dist/i18n/index.js +1 -1
  37. package/dist/i18n/index.js.map +2 -2
  38. package/dist/i18n/index.mjs +1 -1
  39. package/dist/i18n/index.mjs.map +2 -2
  40. package/dist/index.js +3 -3
  41. package/dist/index.js.map +3 -3
  42. package/dist/index.mjs +3 -3
  43. package/dist/index.mjs.map +3 -3
  44. package/dist/routing/NavigateRouteEvent.d.ts +4 -4
  45. package/dist/routing/index.js +3 -3
  46. package/dist/routing/index.js.map +3 -3
  47. package/dist/routing/index.mjs +3 -3
  48. package/dist/routing/index.mjs.map +3 -3
  49. package/dist/routing/navigation.d.ts +1 -1
  50. package/dist/routing/routeTargetRegistry.d.ts +1 -0
  51. package/dist/routing/types.d.ts +2 -1
  52. package/dist/templates/NodeTemplate.d.ts +3 -1
  53. package/dist/utils/index.d.ts +1 -1
  54. package/dist/utils/index.js +1 -1
  55. package/dist/utils/index.js.map +3 -3
  56. package/dist/utils/index.mjs +1 -1
  57. package/dist/utils/index.mjs.map +3 -3
  58. package/docs/Architecture.md +333 -333
  59. package/docs/DependencyInjection.md +277 -237
  60. package/docs/Errors.md +87 -87
  61. package/docs/GettingStarted.md +238 -231
  62. package/docs/Pipes.md +5 -5
  63. package/docs/Translations.md +167 -312
  64. package/docs/WhyRelaxjs.md +336 -336
  65. package/docs/api.json +93193 -0
  66. package/docs/elements/dom.md +102 -102
  67. package/docs/forms/creating-form-components.md +924 -924
  68. package/docs/forms/form-api.md +94 -94
  69. package/docs/forms/forms.md +99 -99
  70. package/docs/forms/patterns.md +311 -311
  71. package/docs/forms/reading-writing.md +465 -365
  72. package/docs/forms/validation.md +351 -351
  73. package/docs/html/TableRenderer.md +291 -291
  74. package/docs/html/html.md +175 -175
  75. package/docs/html/index.md +54 -54
  76. package/docs/html/template.md +422 -422
  77. package/docs/http/HttpClient.md +459 -459
  78. package/docs/http/ServerSentEvents.md +184 -184
  79. package/docs/http/index.md +109 -109
  80. package/docs/i18n/i18n.md +49 -4
  81. package/docs/i18n/intl-standard.md +178 -178
  82. package/docs/routing/RouteLink.md +98 -98
  83. package/docs/routing/Routing.md +332 -332
  84. package/docs/routing/layouts.md +207 -207
  85. package/docs/setup/bootstrapping.md +154 -0
  86. package/docs/setup/build-and-deploy.md +183 -0
  87. package/docs/setup/project-structure.md +170 -0
  88. package/docs/setup/vite.md +175 -0
  89. package/docs/utilities.md +143 -143
  90. package/package.json +4 -2
@@ -0,0 +1,183 @@
1
+ Build and Deploy
2
+ ================
3
+
4
+ Relaxjs apps build and deploy like any Vite + TypeScript project. This page covers the two things beginners get wrong: the **base path** setting and the **SPA fallback** on the hosting side.
5
+
6
+ ---
7
+
8
+ ## Building for production
9
+
10
+ ```bash
11
+ npm run build
12
+ ```
13
+
14
+ Vite outputs static files to `dist/`:
15
+
16
+ ```
17
+ dist/
18
+ ├── index.html
19
+ ├── assets/
20
+ │ ├── index-a1b2c3.js
21
+ │ └── index-d4e5f6.css
22
+ └── favicon.ico
23
+ ```
24
+
25
+ All paths inside `dist/` are relative to the root of your domain by default. If you are deploying to `https://example.com/`, you can upload `dist/` as-is.
26
+
27
+ ---
28
+
29
+ ## Deploying to a subpath
30
+
31
+ If the app lives at `https://example.com/my-app/` instead of the domain root, set `base` in `vite.config.ts`:
32
+
33
+ ```ts
34
+ import { defineConfig } from 'vite';
35
+
36
+ export default defineConfig({
37
+ base: '/my-app/',
38
+ });
39
+ ```
40
+
41
+ The trailing slash matters. With this set, Vite rewrites every asset URL in `index.html` to start with `/my-app/`, and relaxjs routing respects it for `<r-link>` and `navigate(...)`.
42
+
43
+ If you deploy to both a subpath (staging) and the root (production), read the base from an environment variable:
44
+
45
+ ```ts
46
+ export default defineConfig({
47
+ base: process.env.VITE_BASE ?? '/',
48
+ });
49
+ ```
50
+
51
+ ---
52
+
53
+ ## SPA fallback (the most common deployment bug)
54
+
55
+ Relaxjs routing uses the History API. A route like `/profile/42` is a URL the browser shows, but it is **not** a file on disk. When the user reloads that URL, the server looks for `/profile/42/index.html`, doesn't find it, and returns a 404.
56
+
57
+ The fix is to tell the server: *for any URL that is not a real file, serve `index.html` instead*. How to configure this depends on the host.
58
+
59
+ ### Nginx
60
+
61
+ ```nginx
62
+ location / {
63
+ try_files $uri $uri/ /index.html;
64
+ }
65
+ ```
66
+
67
+ ### Apache (`.htaccess`)
68
+
69
+ ```apache
70
+ RewriteEngine On
71
+ RewriteBase /
72
+ RewriteRule ^index\.html$ - [L]
73
+ RewriteCond %{REQUEST_FILENAME} !-f
74
+ RewriteCond %{REQUEST_FILENAME} !-d
75
+ RewriteRule . /index.html [L]
76
+ ```
77
+
78
+ ### Netlify (`netlify.toml` or `_redirects`)
79
+
80
+ ```
81
+ /* /index.html 200
82
+ ```
83
+
84
+ ### Vercel (`vercel.json`)
85
+
86
+ ```json
87
+ {
88
+ "rewrites": [
89
+ { "source": "/(.*)", "destination": "/index.html" }
90
+ ]
91
+ }
92
+ ```
93
+
94
+ ### Static file servers (local testing)
95
+
96
+ For testing the built app locally:
97
+
98
+ ```bash
99
+ npx serve dist --single
100
+ ```
101
+
102
+ The `--single` flag enables SPA fallback.
103
+
104
+ ---
105
+
106
+ ## Multiple HTML layouts
107
+
108
+ [Level 7](../GettingStarted.md) of the Getting Started guide introduces layouts: separate HTML files for distinct UI structures (for example, `index.html` for the authenticated app and `public.html` for login/register pages).
109
+
110
+ Vite builds every HTML file listed in the `rollupOptions.input` config. Add each layout:
111
+
112
+ ```ts
113
+ import { defineConfig } from 'vite';
114
+ import { resolve } from 'path';
115
+
116
+ export default defineConfig({
117
+ build: {
118
+ rollupOptions: {
119
+ input: {
120
+ main: resolve(__dirname, 'index.html'),
121
+ public: resolve(__dirname, 'public.html'),
122
+ },
123
+ },
124
+ },
125
+ });
126
+ ```
127
+
128
+ The SPA fallback must still point at the correct layout. With layouts, the rewrite rules get more nuanced: only fall back to `index.html` for authenticated routes, and to `public.html` for public routes. Most hosts handle this with path-prefixed rewrite rules:
129
+
130
+ ```
131
+ /login → /public.html
132
+ /register → /public.html
133
+ /* → /index.html
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Environment variables
139
+
140
+ Vite exposes variables prefixed with `VITE_` as `import.meta.env.VITE_*` at build time:
141
+
142
+ ```
143
+ # .env.production
144
+ VITE_API_URL=https://api.example.com
145
+ ```
146
+
147
+ ```ts
148
+ const apiUrl = import.meta.env.VITE_API_URL;
149
+ ```
150
+
151
+ Do not put secrets here. Everything in `import.meta.env` ends up in the public bundle.
152
+
153
+ ---
154
+
155
+ ## Checklist before deploying
156
+
157
+ - [ ] `npm run build` succeeds with no warnings
158
+ - [ ] `base` in `vite.config.ts` matches the deployed URL path
159
+ - [ ] SPA fallback is configured on the host
160
+ - [ ] Environment variables are set for the target (dev, staging, prod)
161
+ - [ ] `index.html` references assets via relative URLs (Vite handles this automatically when `base` is correct)
162
+ - [ ] `public/` contains only files that need fixed URLs
163
+
164
+ ---
165
+
166
+ ## Troubleshooting
167
+
168
+ ### Assets load from the wrong path
169
+
170
+ The `base` config does not match the deploy path. For `https://example.com/my-app/`, `base` must be `/my-app/` with a trailing slash.
171
+
172
+ ### Reloading a route shows 404
173
+
174
+ The SPA fallback is missing. Configure the host to serve `index.html` for unknown paths.
175
+
176
+ ### Blank page in production, works in dev
177
+
178
+ Check the browser console. Common causes:
179
+ - A service was imported *after* a component that injects it
180
+ - `import.meta.env.VITE_*` is missing (undefined at runtime)
181
+ - A translation namespace failed to load because the JSON file was not copied to `dist/`
182
+
183
+ Translation files inside `src/i18n/locales/` are loaded via `fetch()` at runtime, so they need to be reachable from the browser. Either move them to `public/i18n/locales/` or import each JSON statically if your i18n loader is configured for it.
@@ -0,0 +1,170 @@
1
+ Project Structure
2
+ =================
3
+
4
+ Relaxjs doesn't force a folder layout, but the one below works well for Vite projects and matches how the examples in this documentation are written.
5
+
6
+ ---
7
+
8
+ ## Recommended layout
9
+
10
+ ```
11
+ my-app/
12
+ ├── index.html
13
+ ├── package.json
14
+ ├── tsconfig.json
15
+ ├── vite.config.ts
16
+ ├── public/
17
+ │ └── favicon.ico
18
+ └── src/
19
+ ├── main.ts
20
+ ├── styles.css
21
+ ├── components/
22
+ │ ├── HomePage.ts
23
+ │ ├── UserPanel.ts
24
+ │ └── AppShell.ts
25
+ ├── services/
26
+ │ ├── ApiClient.ts
27
+ │ └── UserService.ts
28
+ └── i18n/
29
+ └── locales/
30
+ ├── en/
31
+ │ ├── r-common.json
32
+ │ └── r-validation.json
33
+ └── sv/
34
+ ├── r-common.json
35
+ └── r-validation.json
36
+ ```
37
+
38
+ Each folder has one job. Beginners can skip folders they don't need yet — a small app may only have `src/main.ts` and `src/components/`.
39
+
40
+ ---
41
+
42
+ ## `index.html`
43
+
44
+ Vite treats `index.html` as the project entry. It loads `src/main.ts` as an ES module:
45
+
46
+ ```html
47
+ <!DOCTYPE html>
48
+ <html>
49
+ <head>
50
+ <meta charset="UTF-8" />
51
+ <title>My App</title>
52
+ <link rel="stylesheet" href="/src/styles.css" />
53
+ </head>
54
+ <body>
55
+ <r-route-target></r-route-target>
56
+ <script type="module" src="/src/main.ts"></script>
57
+ </body>
58
+ </html>
59
+ ```
60
+
61
+ Keep the body minimal. Put page content inside components, not in the HTML file.
62
+
63
+ ---
64
+
65
+ ## `src/main.ts`
66
+
67
+ The bootstrap file. It imports services, components, and starts i18n and routing. See [Bootstrapping](bootstrapping.md) for the exact order and a full example.
68
+
69
+ ---
70
+
71
+ ## `src/components/`
72
+
73
+ One file per custom element. Use **PascalCase filenames** and **kebab-case tag names**:
74
+
75
+ ```ts
76
+ // src/components/UserPanel.ts
77
+ import { html } from '@relax.js/core/html';
78
+
79
+ export class UserPanel extends HTMLElement {
80
+ connectedCallback() {
81
+ const result = html`<h2>Profile</h2>`({});
82
+ this.appendChild(result.fragment);
83
+ }
84
+ }
85
+
86
+ customElements.define('user-panel', UserPanel);
87
+ ```
88
+
89
+ The file ends with `customElements.define(...)` so importing the file is enough to register the tag.
90
+
91
+ ---
92
+
93
+ ## `src/services/`
94
+
95
+ One file per injectable service. Services use `@ContainerService`:
96
+
97
+ ```ts
98
+ // src/services/ApiClient.ts
99
+ import { ContainerService } from '@relax.js/core/di';
100
+
101
+ @ContainerService()
102
+ export class ApiClient {
103
+ async get(path: string) {
104
+ const res = await fetch(path);
105
+ return res.json();
106
+ }
107
+ }
108
+ ```
109
+
110
+ Services are plain classes. They don't extend `HTMLElement`.
111
+
112
+ ---
113
+
114
+ ## `src/i18n/locales/`
115
+
116
+ Translation files, one JSON per locale per namespace:
117
+
118
+ ```
119
+ src/i18n/locales/en/r-common.json
120
+ src/i18n/locales/en/r-validation.json
121
+ src/i18n/locales/sv/r-common.json
122
+ src/i18n/locales/sv/r-validation.json
123
+ ```
124
+
125
+ The structure is fixed: `{locale}/{namespace}.json`. See [i18n](../i18n/i18n.md) for the file format.
126
+
127
+ ---
128
+
129
+ ## `public/`
130
+
131
+ Static files served at the web root. Use it for favicons, robots.txt, and assets that need stable URLs. Vite copies everything in `public/` to the build output unchanged.
132
+
133
+ Prefer imports for assets used by code:
134
+
135
+ ```ts
136
+ import logoUrl from './assets/logo.svg';
137
+ ```
138
+
139
+ Use `public/` only when the file must have a known fixed URL.
140
+
141
+ ---
142
+
143
+ ## `src/styles.css`
144
+
145
+ Global styles live in one file, imported from `index.html`. Per-component styles can be inline `<style>` tags inside each component's template, or a sibling `.css` file imported by the component module.
146
+
147
+ Relaxjs does not ship reset styles. Style semantic HTML directly and use the `.form` class to scope form layouts. See [Forms](../forms/forms.md).
148
+
149
+ ---
150
+
151
+ ## Scaling up
152
+
153
+ As the app grows, split `components/` by feature:
154
+
155
+ ```
156
+ src/components/
157
+ ├── home/
158
+ │ ├── HomePage.ts
159
+ │ └── WelcomeBanner.ts
160
+ ├── profile/
161
+ │ ├── UserPanel.ts
162
+ │ └── EditForm.ts
163
+ └── shared/
164
+ ├── AppShell.ts
165
+ └── NavBar.ts
166
+ ```
167
+
168
+ Keep `services/` flat until you have more than ten services, then group the same way.
169
+
170
+ Avoid a `utils/` dump folder. If a helper is shared by two features, move it into `src/shared/` with a clear filename.
@@ -0,0 +1,175 @@
1
+ Vite Setup
2
+ ==========
3
+
4
+ Relaxjs uses the modern **Stage 3 decorator** syntax (the one being standardized by TC39, supported by TypeScript 5.0 and newer). This is different from the older "experimental" decorators that many guides still describe.
5
+
6
+ This page shows how to set up a Vite + TypeScript project so that relaxjs decorators like `@RegisterValidator` work out of the box.
7
+
8
+ ---
9
+
10
+ ## Requirements
11
+
12
+ | Tool | Minimum version | Why |
13
+ |------------|-----------------|-----|
14
+ | Node.js | 18 or newer | Required by Vite 5+ |
15
+ | TypeScript | 5.0 or newer | Stage 3 decorator support |
16
+ | Vite | 5.3 or newer | Bundled esbuild transforms Stage 3 decorators |
17
+
18
+ If you created your project with `npm create vite@latest` recently, you already meet these.
19
+
20
+ ---
21
+
22
+ ## Step 1: Create a Vite project
23
+
24
+ ```bash
25
+ npm create vite@latest my-app -- --template vanilla-ts
26
+ cd my-app
27
+ npm install
28
+ npm install @relax.js/core
29
+ ```
30
+
31
+ The `vanilla-ts` template gives you TypeScript without a framework, which is what relaxjs is designed for.
32
+
33
+ ---
34
+
35
+ ## Step 2: Configure `tsconfig.json`
36
+
37
+ Open `tsconfig.json` and make sure these options are set:
38
+
39
+ ```json
40
+ {
41
+ "compilerOptions": {
42
+ "target": "ES2022",
43
+ "module": "ESNext",
44
+ "moduleResolution": "Bundler",
45
+ "experimentalDecorators": false,
46
+ "useDefineForClassFields": true,
47
+ "strict": true,
48
+ "skipLibCheck": true,
49
+ "lib": ["ES2022", "DOM"]
50
+ },
51
+ "include": ["src"]
52
+ }
53
+ ```
54
+
55
+ The important lines for decorators:
56
+
57
+ - **`"experimentalDecorators": false`** — this is the switch. When `false` (or omitted), TypeScript uses the new Stage 3 decorators. When `true`, it uses the old legacy decorators, which are not compatible with relaxjs.
58
+ - **`"target": "ES2022"`** — any value from `ES2022` upward works. Do not pick a lower target; esbuild needs a modern target to emit clean decorator output.
59
+ - **`"useDefineForClassFields": true`** — required so class fields behave as the standard specifies. Relaxjs form components rely on this.
60
+
61
+ You do **not** need `emitDecoratorMetadata`. That option only applies to legacy decorators and is ignored here.
62
+
63
+ ---
64
+
65
+ ## Step 3: `vite.config.ts`
66
+
67
+ A minimal config is enough. Vite picks up your `tsconfig.json` automatically.
68
+
69
+ ```ts
70
+ import { defineConfig } from 'vite';
71
+
72
+ export default defineConfig({
73
+ server: {
74
+ port: 3000,
75
+ },
76
+ });
77
+ ```
78
+
79
+ No plugins are required for decorators. Vite's built-in esbuild handles the transform.
80
+
81
+ ---
82
+
83
+ ## Step 4: Try a relaxjs decorator
84
+
85
+ Create `src/main.ts`:
86
+
87
+ ```ts
88
+ import { RegisterValidator, ValidationContext } from '@relax.js/core/forms';
89
+
90
+ @RegisterValidator('even')
91
+ class EvenValidation {
92
+ validate(value: string, context: ValidationContext) {
93
+ const num = parseInt(value, 10);
94
+ if (isNaN(num) || num % 2 !== 0) {
95
+ context.addError('Value must be an even number');
96
+ }
97
+ }
98
+ }
99
+
100
+ console.log('EvenValidation registered');
101
+ ```
102
+
103
+ Run the dev server:
104
+
105
+ ```bash
106
+ npm run dev
107
+ ```
108
+
109
+ Open the browser console. If you see `EvenValidation registered` and no errors, your decorator setup is working.
110
+
111
+ ---
112
+
113
+ ## Troubleshooting
114
+
115
+ ### "Decorators are not valid here" or a parse error
116
+
117
+ Your TypeScript version is older than 5.0. Update it:
118
+
119
+ ```bash
120
+ npm install -D typescript@latest
121
+ ```
122
+
123
+ Then restart your editor so it picks up the new version.
124
+
125
+ ### Code compiles but decorator never runs
126
+
127
+ You probably have `"experimentalDecorators": true` left over from an older template. Set it to `false` and restart the dev server.
128
+
129
+ ### Syntax error at runtime, only in the browser
130
+
131
+ Your Vite (and therefore esbuild) version is too old to transform Stage 3 decorators. Upgrade:
132
+
133
+ ```bash
134
+ npm install -D vite@latest
135
+ ```
136
+
137
+ Vite 5.3 and newer bundle an esbuild version that handles the new decorator syntax.
138
+
139
+ ### Error about `ElementInternals` or missing DOM types
140
+
141
+ Make sure `"lib"` includes `"DOM"` in `tsconfig.json`. Without it, TypeScript does not know about browser APIs that relaxjs uses.
142
+
143
+ ---
144
+
145
+ ## Vitest (optional)
146
+
147
+ If you add Vitest for testing, no extra decorator configuration is needed. Vitest uses the same esbuild transform as Vite, so the settings above apply to tests automatically.
148
+
149
+ ```bash
150
+ npm install -D vitest jsdom
151
+ ```
152
+
153
+ ```ts
154
+ // vitest.config.ts
155
+ import { defineConfig } from 'vitest/config';
156
+
157
+ export default defineConfig({
158
+ test: {
159
+ environment: 'jsdom',
160
+ globals: true,
161
+ },
162
+ });
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Summary
168
+
169
+ For relaxjs decorators to work, you need three things:
170
+
171
+ 1. TypeScript 5.0+
172
+ 2. `"experimentalDecorators": false` in `tsconfig.json`
173
+ 3. Vite 5.3+ (modern esbuild)
174
+
175
+ With those in place, decorators like `@RegisterValidator` work without any plugin or extra setup.