@alwatr/flux 9.18.1 → 9.19.1
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.
- package/README.md +128 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +3 -3
- package/package.json +4 -3
- package/src/main.ts +1 -0
package/README.md
CHANGED
|
@@ -268,6 +268,58 @@ console.log(userPrefs.get()); // {theme: 'dark', lang: 'fa'}
|
|
|
268
268
|
- **Type-safe** — full TypeScript support
|
|
269
269
|
- **Migration-friendly** — bump `schemaVersion` to reset storage
|
|
270
270
|
|
|
271
|
+
### 🌐 **SSR State Hydration**
|
|
272
|
+
|
|
273
|
+
`@alwatr/embedded-data` bridges the gap between server-rendered HTML and client-side reactive state. The server embeds initial data as JSON inside `<script type="application/json">` tags; the client extracts, validates, and feeds it into signals — **zero extra HTTP round-trips, zero flash of empty content**.
|
|
274
|
+
|
|
275
|
+
```html
|
|
276
|
+
<!-- Server renders this into the HTML -->
|
|
277
|
+
<script
|
|
278
|
+
type="application/json"
|
|
279
|
+
data-user-profile
|
|
280
|
+
>
|
|
281
|
+
{"userId": 42, "name": "Ali", "role": "admin"}
|
|
282
|
+
</script>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import {EmbeddedDataCollector} from '@alwatr/flux';
|
|
287
|
+
import {lazy} from '@alwatr/lazy';
|
|
288
|
+
|
|
289
|
+
interface UserProfile {
|
|
290
|
+
userId: number;
|
|
291
|
+
name: string;
|
|
292
|
+
role: 'admin' | 'user';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function isUserProfile(data: unknown): data is UserProfile {
|
|
296
|
+
return typeof data === 'object' && data !== null && 'userId' in data;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Lazy: extraction runs only when .value is first accessed — not at module load.
|
|
300
|
+
export const userProfile = lazy(() =>
|
|
301
|
+
new EmbeddedDataCollector<UserProfile>('data-user-profile', isUserProfile).collect(),
|
|
302
|
+
);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// Feed into a signal — the rest of the app reacts normally
|
|
307
|
+
import {createStateSignal} from '@alwatr/flux';
|
|
308
|
+
|
|
309
|
+
const userSignal = createStateSignal<UserProfile | null>({
|
|
310
|
+
name: 'user',
|
|
311
|
+
initialValue: userProfile.value, // hydrated from DOM, no API call needed
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Why this matters:**
|
|
316
|
+
|
|
317
|
+
- **No flash of empty content** — state is available synchronously on first render
|
|
318
|
+
- **No extra API call** — server already sent the data in the HTML payload
|
|
319
|
+
- **SSR-safe** — `EmbeddedDataCollector` guards against missing `document` in Node.js/Bun
|
|
320
|
+
- **Memory-efficient** — script tag content is cleared after extraction (GC hint)
|
|
321
|
+
- **Type-safe** — optional type-guard validator ensures runtime safety
|
|
322
|
+
|
|
271
323
|
### 📄 **Page-Ready Signal for MPA**
|
|
272
324
|
|
|
273
325
|
Lightweight page identity system for Multi-Page Applications:
|
|
@@ -915,6 +967,81 @@ Same as `createLocalStorageProvider` but uses `sessionStorage`.
|
|
|
915
967
|
|
|
916
968
|
---
|
|
917
969
|
|
|
970
|
+
### Embedded Data
|
|
971
|
+
|
|
972
|
+
#### `EmbeddedDataCollector<T>`
|
|
973
|
+
|
|
974
|
+
Extracts, parses, and validates JSON embedded in `<script type="application/json">` DOM nodes. Designed for SSR state hydration — the server renders initial state into the HTML, the client reads it on boot without an extra HTTP round-trip.
|
|
975
|
+
|
|
976
|
+
```typescript
|
|
977
|
+
import {EmbeddedDataCollector} from '@alwatr/flux';
|
|
978
|
+
|
|
979
|
+
// HTML: <script type="application/json" data-config>{"apiUrl":"https://api.example.com"}</script>
|
|
980
|
+
|
|
981
|
+
const collector = new EmbeddedDataCollector<AppConfig>('data-config');
|
|
982
|
+
const config = collector.collect(); // AppConfig | null
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
**Constructor:**
|
|
986
|
+
|
|
987
|
+
```typescript
|
|
988
|
+
new EmbeddedDataCollector<T>(
|
|
989
|
+
attributeName: string, // HTML attribute to query (e.g. 'data-config')
|
|
990
|
+
validator?: (data: unknown) => data is T // optional type-guard
|
|
991
|
+
)
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
**`collect(): T | null`**
|
|
995
|
+
|
|
996
|
+
Runs the full extraction pipeline:
|
|
997
|
+
|
|
998
|
+
1. `querySelector('script[attributeName]')` — SSR-safe, returns `null` if `document` is undefined
|
|
999
|
+
2. Read `textContent`, then set it to `''` (GC hint)
|
|
1000
|
+
3. `JSON.parse()`
|
|
1001
|
+
4. Run `validator` if provided
|
|
1002
|
+
5. Return typed data or `null` on any failure
|
|
1003
|
+
|
|
1004
|
+
**With type-guard validation:**
|
|
1005
|
+
|
|
1006
|
+
```typescript
|
|
1007
|
+
import {EmbeddedDataCollector} from '@alwatr/flux';
|
|
1008
|
+
import {createStateSignal} from '@alwatr/flux';
|
|
1009
|
+
|
|
1010
|
+
interface CartState {
|
|
1011
|
+
items: {id: number; qty: number}[];
|
|
1012
|
+
total: number;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function isCartState(data: unknown): data is CartState {
|
|
1016
|
+
return typeof data === 'object' && data !== null && Array.isArray((data as CartState).items);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// Extract once at boot, feed into signal
|
|
1020
|
+
const collector = new EmbeddedDataCollector<CartState>('data-cart', isCartState);
|
|
1021
|
+
|
|
1022
|
+
const cartSignal = createStateSignal<CartState>({
|
|
1023
|
+
name: 'cart',
|
|
1024
|
+
initialValue: collector.collect() ?? {items: [], total: 0},
|
|
1025
|
+
});
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
**Combine with `@alwatr/lazy` for deferred extraction:**
|
|
1029
|
+
|
|
1030
|
+
```typescript
|
|
1031
|
+
import {lazy} from '@alwatr/lazy';
|
|
1032
|
+
import {EmbeddedDataCollector} from '@alwatr/flux';
|
|
1033
|
+
|
|
1034
|
+
// Extraction is deferred until .value is first accessed
|
|
1035
|
+
export const serverConfig = lazy(() =>
|
|
1036
|
+
new EmbeddedDataCollector<ServerConfig>('data-server-config', isServerConfig).collect(),
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
// Somewhere in your bootstrap code:
|
|
1040
|
+
const config = serverConfig.value; // extracted here, cached forever
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
---
|
|
1044
|
+
|
|
918
1045
|
### Render State
|
|
919
1046
|
|
|
920
1047
|
#### `renderState<R, T>(state, renderRecord, thisArg?)`
|
|
@@ -1172,6 +1299,7 @@ todosSignal.subscribe((todos) => {
|
|
|
1172
1299
|
- **[@alwatr/signal](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/signal)** — Fine-grained reactive signals (part of Flux)
|
|
1173
1300
|
- **[@alwatr/action](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/action)** — Global event delegation action bus (part of Flux)
|
|
1174
1301
|
- **[@alwatr/directive](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/directive)** — Attribute-based DOM directives (part of Flux)
|
|
1302
|
+
- **[@alwatr/embedded-data](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/embedded-data)** — Extract and validate embedded JSON from DOM script tags for SSR hydration (part of Flux)
|
|
1175
1303
|
- **[@alwatr/fsm](https://github.com/Alwatr/alwatr/tree/next/pkg/fsm)** — Type-safe Finite State Machine
|
|
1176
1304
|
- **[@alwatr/nanotron](https://github.com/Alwatr/alwatr/tree/next/pkg/nanotron)** — Lightweight API server framework
|
|
1177
1305
|
- **[@alwatr/nitrobase](https://github.com/Alwatr/alwatr/tree/next/pkg/nitrobase)** — In-memory JSON database
|
package/dist/main.d.ts
CHANGED
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,mBAAmB,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,mBAAmB,qBAAqB,CAAC"}
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/* 📦 @alwatr/flux v9.
|
|
2
|
-
export*from"@alwatr/signal";export*from"@alwatr/action";export*from"@alwatr/directive";export*from"@alwatr/render-state";export*from"@alwatr/local-storage";export*from"@alwatr/session-storage";export*from"@alwatr/page-ready";import{html as
|
|
1
|
+
/* 📦 @alwatr/flux v9.19.1 */
|
|
2
|
+
export*from"@alwatr/signal";export*from"@alwatr/action";export*from"@alwatr/directive";export*from"@alwatr/embedded-data";export*from"@alwatr/render-state";export*from"@alwatr/local-storage";export*from"@alwatr/session-storage";export*from"@alwatr/page-ready";import{html as e,render as t,noChange as a,nothing as p}from"lit-html";import{ifDefined as n}from"lit-html/directives/if-defined.js";import{cache as g}from"lit-html/directives/cache.js";import{classMap as i}from"lit-html/directives/class-map.js";import{when as s}from"lit-html/directives/when.js";export{s as when,t as render,p as nothing,a as noChange,n as ifDefined,e as html,i as classMap,g as cache};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=11DBD23AF7A728BB64756E2164756E21
|
|
5
5
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts", "../src/lit-html.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// UI and reactive bundle — signals, actions, directives, and client-side storage.\n// This package aggregates all UI-layer nanolibs for convenient single-import usage.\n\nexport * from '@alwatr/signal';\nexport * from '@alwatr/action';\nexport * from '@alwatr/directive';\nexport * from '@alwatr/render-state';\nexport * from '@alwatr/local-storage';\nexport * from '@alwatr/session-storage';\nexport * from '@alwatr/page-ready';\nexport * from './lit-html.js';\nexport type * from '@alwatr/type-helper';\n",
|
|
5
|
+
"// UI and reactive bundle — signals, actions, directives, and client-side storage.\n// This package aggregates all UI-layer nanolibs for convenient single-import usage.\n\nexport * from '@alwatr/signal';\nexport * from '@alwatr/action';\nexport * from '@alwatr/directive';\nexport * from '@alwatr/embedded-data';\nexport * from '@alwatr/render-state';\nexport * from '@alwatr/local-storage';\nexport * from '@alwatr/session-storage';\nexport * from '@alwatr/page-ready';\nexport * from './lit-html.js';\nexport type * from '@alwatr/type-helper';\n",
|
|
6
6
|
"/**\n * Curated re-exports from `lit-html` for use within `@alwatr/flux`.\n *\n * Only the subset of `lit-html` APIs that are commonly needed in a Flux-based\n * application is exported here. This keeps the public surface minimal and\n * avoids pulling in advanced directive utilities that most consumers never use.\n *\n * **Exported APIs:**\n * - `html` — tagged template literal that produces a `TemplateResult`\n * - `render` — renders a `TemplateResult` into a DOM container\n * - `noChange` — sentinel that tells lit-html to leave the current part value unchanged\n * - `nothing` — sentinel that renders nothing (removes the node/attribute)\n * - `ifDefined` — renders a value only when it is not `undefined`\n * - `cache` — caches rendered templates to avoid re-parsing on state changes\n * - `classMap` — efficiently sets/removes CSS classes from an object map\n * - `when` — conditional rendering helper (`when(condition, trueCase, falseCase)`)\n *\n * @example\n * ```typescript\n * import {html, render, classMap, when} from '@alwatr/flux';\n *\n * const template = (isActive: boolean) => html`\n * <div class=${classMap({active: isActive, hidden: !isActive})}>\n * ${when(isActive, () => html`<span>Active</span>`, () => html`<span>Inactive</span>`)}\n * </div>\n * `;\n *\n * render(template(true), document.getElementById('app')!);\n * ```\n */\nexport {html, render, noChange, nothing} from 'lit-html';\n// export {Directive, PartType, directive} from 'lit-html/directive.js';\n// export {AsyncDirective} from 'lit-html/async-directive.js';\n// export {unsafeSVG} from 'lit-html/directives/unsafe-svg.js';\nexport {ifDefined} from 'lit-html/directives/if-defined.js';\nexport {cache} from 'lit-html/directives/cache.js';\nexport {classMap} from 'lit-html/directives/class-map.js';\nexport {when} from 'lit-html/directives/when.js';\n\n// export type {Part, PartInfo} from 'lit-html/directive.js';\n// export type {LitUnstable} from 'lit-html';\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";AAGA,4BACA,4BACA,+BACA,kCACA,mCACA,qCACA,
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": ";AAGA,4BACA,4BACA,+BACA,mCACA,kCACA,mCACA,qCACA,gCCoBA,eAAQ,YAAM,cAAQ,aAAU,iBAIhC,oBAAQ,0CACR,gBAAQ,qCACR,mBAAQ,yCACR,eAAQ",
|
|
9
|
+
"debugId": "11DBD23AF7A728BB64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwatr/flux",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.19.1",
|
|
4
4
|
"description": "UI and reactive library bundle for ECMAScript (JavaScript/TypeScript) projects — signals, actions, directives, and storage.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "S. Ali Mihandoost <ali.mihandoost@gmail.com> (https://ali.mihandoost.com)",
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
},
|
|
22
22
|
"sideEffects": false,
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@alwatr/action": "9.
|
|
24
|
+
"@alwatr/action": "9.19.1",
|
|
25
25
|
"@alwatr/directive": "9.18.0",
|
|
26
|
+
"@alwatr/embedded-data": "9.19.1",
|
|
26
27
|
"@alwatr/local-storage": "9.16.0",
|
|
27
28
|
"@alwatr/page-ready": "9.16.0",
|
|
28
29
|
"@alwatr/render-state": "9.16.0",
|
|
@@ -82,5 +83,5 @@
|
|
|
82
83
|
"ui",
|
|
83
84
|
"unidirectional-data-flow"
|
|
84
85
|
],
|
|
85
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "63f91e7a209c8d38d53b3042e0301941b231411b"
|
|
86
87
|
}
|
package/src/main.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export * from '@alwatr/signal';
|
|
5
5
|
export * from '@alwatr/action';
|
|
6
6
|
export * from '@alwatr/directive';
|
|
7
|
+
export * from '@alwatr/embedded-data';
|
|
7
8
|
export * from '@alwatr/render-state';
|
|
8
9
|
export * from '@alwatr/local-storage';
|
|
9
10
|
export * from '@alwatr/session-storage';
|