@excom/provider-storage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.rush/temp/chunked-rush-logs/provider-storage.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/provider-storage.build_docs.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/provider-storage.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_docs/all.log +1 -0
- package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_docs/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +3 -0
- package/config/rig.json +5 -0
- package/index.ts +18 -0
- package/package.json +45 -0
- package/provider-storage.ts +138 -0
- package/rush-logs/provider-storage.apply-exports.cache.log +1 -0
- package/rush-logs/provider-storage.apply-exports.log +1 -0
- package/rush-logs/provider-storage.build_docs.cache.log +1 -0
- package/rush-logs/provider-storage.build_docs.log +1 -0
- package/rush-logs/provider-storage.build_package-metas.cache.log +1 -0
- package/rush-logs/provider-storage.build_package-metas.log +1 -0
- package/support/custom-elements.json +167 -0
- package/support/demos/simple.html +18 -0
- package/support/dist-docs/provider-storage.md +118 -0
- package/support/docs/README.md +55 -0
- package/support/package-meta.json +99 -0
- package/support/tests/__snapshots__/simple.view.test.ts.snap +23 -0
- package/support/tests/provider-storage.test.ts +520 -0
- package/support/tests/simple.view.test.ts +44 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
package/config/rig.json
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ProviderStorage } from "./provider-storage";
|
|
2
|
+
|
|
3
|
+
ProviderStorage.define();
|
|
4
|
+
|
|
5
|
+
export { ProviderStorage };
|
|
6
|
+
export type { ProviderStorageChangedEvent } from "./provider-storage";
|
|
7
|
+
|
|
8
|
+
type T_HTMLProviderStorageElement = typeof ProviderStorage.CustomElement;
|
|
9
|
+
declare global {
|
|
10
|
+
interface HTMLProviderStorageElement extends T_HTMLProviderStorageElement {}
|
|
11
|
+
interface Window {
|
|
12
|
+
HTMLProviderStorageElement: HTMLProviderStorageElement;
|
|
13
|
+
}
|
|
14
|
+
interface HTMLElementTagNameMap {
|
|
15
|
+
"provider-storage": HTMLProviderStorageElement;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export type { HTMLProviderStorageElement };
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@excom/provider-storage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "<provider-storage> custom element",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24.13.0"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@excom/kit-logger": "^0.1.0",
|
|
12
|
+
"@excom/neutron": "^0.1.0"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@excom/heft-rig": "^0.1.0"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"url": "excom-dev/nucleus",
|
|
20
|
+
"directory": "packages/provider-storage"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/provider-storage/support/docs/README.md",
|
|
23
|
+
"bugs": "https://github.com/excom-dev/nucleus/issues",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"provider-storage",
|
|
26
|
+
"neutron",
|
|
27
|
+
"custom-elements",
|
|
28
|
+
"localstorage",
|
|
29
|
+
"sessionstorage"
|
|
30
|
+
],
|
|
31
|
+
"excom": {
|
|
32
|
+
"packageType": "kit-element"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
|
|
36
|
+
"build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
|
|
37
|
+
"format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
|
|
38
|
+
"test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
|
|
39
|
+
"coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
|
|
40
|
+
"dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
|
|
41
|
+
"preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
|
|
42
|
+
"build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs",
|
|
43
|
+
"build:docs": "node node_modules/@excom/heft-rig/scripts/build-docs.mjs"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { KitLogger } from "@excom/kit-logger";
|
|
2
|
+
import { Neutron, TEvent } from "@excom/neutron";
|
|
3
|
+
|
|
4
|
+
export type ProviderStorageChangedEvent = TEvent & {
|
|
5
|
+
type: "provider-storage-changed";
|
|
6
|
+
detail: {
|
|
7
|
+
/** The `key-name` whose stored value changed. */
|
|
8
|
+
keyName: string;
|
|
9
|
+
/** `provision` before the re-read (`null` when nothing was read). */
|
|
10
|
+
oldValue: unknown;
|
|
11
|
+
/** `provision` after the re-read (`null` when absent / not valid JSON). */
|
|
12
|
+
newValue: unknown;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type StoreProps = { keyName?: string | null; storeName?: string | null };
|
|
17
|
+
|
|
18
|
+
const getStore = (storeName?: string | null) =>
|
|
19
|
+
storeName === "session" ? sessionStorage : localStorage;
|
|
20
|
+
|
|
21
|
+
/** Read `key-name` out of `store-name` as JSON → provision / state effect. */
|
|
22
|
+
const readStore = ({ keyName, storeName }: StoreProps) => {
|
|
23
|
+
try {
|
|
24
|
+
return {
|
|
25
|
+
provision: JSON.parse(getStore(storeName).getItem(keyName!) || "null"),
|
|
26
|
+
isSuccess: true,
|
|
27
|
+
isError: false,
|
|
28
|
+
};
|
|
29
|
+
} catch (e) {
|
|
30
|
+
KitLogger.error("provider-storage", e);
|
|
31
|
+
return { provision: null, isSuccess: false, isError: true };
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reads a JSON value out of `localStorage` / `sessionStorage` under
|
|
37
|
+
* `key-name` into `provision`. Read-only: it never writes. It re-reads
|
|
38
|
+
* whenever `key-name` / `store-name` is set or changes, and — for
|
|
39
|
+
* `localStorage` — whenever another tab writes the same key (the
|
|
40
|
+
* browser's `storage` event). Same-tab writes do not fire `storage`,
|
|
41
|
+
* so after your own `setItem` re-set `key-name` to force a read.
|
|
42
|
+
*
|
|
43
|
+
* @fires provider-storage-changed - Dispatched after a `storage` event
|
|
44
|
+
* from another tab for this element's `store-name` + `key-name` (or a
|
|
45
|
+
* `clear()` of that store) has been re-read into `provision` — so
|
|
46
|
+
* `provision`, `is-success` / `is-error` are already updated when it
|
|
47
|
+
* fires. The read also publishes `neutron-provision`, as usual.
|
|
48
|
+
* @type ProviderStorageChangedEvent
|
|
49
|
+
*/
|
|
50
|
+
export const ProviderStorage = Neutron({
|
|
51
|
+
tag: "provider-storage",
|
|
52
|
+
props: {
|
|
53
|
+
// options
|
|
54
|
+
/**
|
|
55
|
+
* @option
|
|
56
|
+
* Storage key to read. Setting or changing it re-reads immediately
|
|
57
|
+
* (parsed as JSON; `null` if absent). Removing it clears
|
|
58
|
+
* `provision` and both states.
|
|
59
|
+
* @values <storage key>
|
|
60
|
+
*/
|
|
61
|
+
keyName: String,
|
|
62
|
+
/**
|
|
63
|
+
* @option
|
|
64
|
+
* Which Web Storage area to read. `local` survives the tab / browser
|
|
65
|
+
* closing and syncs across tabs; `session` is per-tab and gone when
|
|
66
|
+
* the tab closes. Changing it re-reads `key-name` from the new store.
|
|
67
|
+
* @values local, session
|
|
68
|
+
* @default local
|
|
69
|
+
*/
|
|
70
|
+
storeName: String,
|
|
71
|
+
// state
|
|
72
|
+
/**
|
|
73
|
+
* @provision
|
|
74
|
+
* `JSON.parse(store.getItem(keyName))`, or `null` if the key is
|
|
75
|
+
* absent or the read failed. Not reflected as an attribute.
|
|
76
|
+
*/
|
|
77
|
+
provision: Object,
|
|
78
|
+
/**
|
|
79
|
+
* @state
|
|
80
|
+
* The last read (parse) succeeded — including a legitimately absent
|
|
81
|
+
* key (`provision` is `null`, not an error).
|
|
82
|
+
*/
|
|
83
|
+
isSuccess: Boolean,
|
|
84
|
+
/**
|
|
85
|
+
* @state
|
|
86
|
+
* `JSON.parse` threw on the last read (the stored value isn't valid
|
|
87
|
+
* JSON). `provision` is `null`.
|
|
88
|
+
*/
|
|
89
|
+
isError: Boolean,
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
.defineMethods({
|
|
93
|
+
// `storage` fires in *other* tabs only, for every key of every store.
|
|
94
|
+
_handleStorage: (
|
|
95
|
+
{ keyName, storeName, provision }: StoreProps & { provision: unknown },
|
|
96
|
+
e: StorageEvent
|
|
97
|
+
) => {
|
|
98
|
+
if (!keyName || e.storageArea !== getStore(storeName)) return null;
|
|
99
|
+
// `key === null` is `clear()`; it took this key with it
|
|
100
|
+
if (e.key !== null && e.key !== keyName) return null;
|
|
101
|
+
const next = readStore({ keyName, storeName });
|
|
102
|
+
return {
|
|
103
|
+
...next,
|
|
104
|
+
emit: [
|
|
105
|
+
"provider-storage-changed",
|
|
106
|
+
{
|
|
107
|
+
detail: {
|
|
108
|
+
keyName,
|
|
109
|
+
oldValue: provision,
|
|
110
|
+
newValue: next.provision,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
// Tracked listener: Neutron drops it on disconnect and restores it on
|
|
118
|
+
// reconnect / move, so no `isMoving` bookkeeping here.
|
|
119
|
+
.onConnected(
|
|
120
|
+
// @ts-ignore TODO defineMethods
|
|
121
|
+
({ _handleStorage }) => ({
|
|
122
|
+
addListener: ["storage", _handleStorage, { target: window }],
|
|
123
|
+
})
|
|
124
|
+
)
|
|
125
|
+
// One read per batch, whichever of the two changed (including first mount).
|
|
126
|
+
.onEffect(["keyName", "storeName"], ({ keyName, storeName }, previous) =>
|
|
127
|
+
keyName
|
|
128
|
+
? readStore({ keyName, storeName })
|
|
129
|
+
: // `key-name` removed: nothing to show
|
|
130
|
+
"keyName" in previous && {
|
|
131
|
+
provision: null,
|
|
132
|
+
isSuccess: false,
|
|
133
|
+
isError: false,
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// <provider-storage key-name="results"></provider-storage>
|
|
138
|
+
// <provider-storage key-name="draft" store-name="session"></provider-storage>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Caching has been disabled for this project's "apply-exports" command.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This project does not define the caching behavior of the "build:docs" command, so caching has been disabled.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"modules": [
|
|
4
|
+
{
|
|
5
|
+
"kind": "javascript-module",
|
|
6
|
+
"path": "provider-storage.ts",
|
|
7
|
+
"declarations": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "class",
|
|
10
|
+
"name": "ProviderStorage",
|
|
11
|
+
"customElement": true,
|
|
12
|
+
"tagName": "provider-storage",
|
|
13
|
+
"description": "The `key-name` whose stored value changed.",
|
|
14
|
+
"attributes": [
|
|
15
|
+
{
|
|
16
|
+
"name": "key-name",
|
|
17
|
+
"type": {
|
|
18
|
+
"text": "string"
|
|
19
|
+
},
|
|
20
|
+
"description": "Storage key to read. Setting or changing it re-reads immediately (parsed as JSON; `null` if absent). Removing it clears `provision` and both states.",
|
|
21
|
+
"fieldName": "keyName",
|
|
22
|
+
"values": [
|
|
23
|
+
"<storage key>"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "store-name",
|
|
28
|
+
"type": {
|
|
29
|
+
"text": "string"
|
|
30
|
+
},
|
|
31
|
+
"description": "Which Web Storage area to read. `local` survives the tab / browser closing and syncs across tabs; `session` is per-tab and gone when the tab closes. Changing it re-reads `key-name` from the new store.",
|
|
32
|
+
"fieldName": "storeName",
|
|
33
|
+
"default": "local",
|
|
34
|
+
"values": [
|
|
35
|
+
"local, session"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "is-success",
|
|
40
|
+
"type": {
|
|
41
|
+
"text": "boolean"
|
|
42
|
+
},
|
|
43
|
+
"description": "The last read (parse) succeeded — including a legitimately absent key (`provision` is `null`, not an error).",
|
|
44
|
+
"fieldName": "isSuccess"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "is-error",
|
|
48
|
+
"type": {
|
|
49
|
+
"text": "boolean"
|
|
50
|
+
},
|
|
51
|
+
"description": "`JSON.parse` threw on the last read (the stored value isn't valid JSON). `provision` is `null`.",
|
|
52
|
+
"fieldName": "isError"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"members": [
|
|
56
|
+
{
|
|
57
|
+
"kind": "field",
|
|
58
|
+
"name": "keyName",
|
|
59
|
+
"type": {
|
|
60
|
+
"text": "string"
|
|
61
|
+
},
|
|
62
|
+
"privacy": "public",
|
|
63
|
+
"readonly": false,
|
|
64
|
+
"description": "Storage key to read. Setting or changing it re-reads immediately (parsed as JSON; `null` if absent). Removing it clears `provision` and both states.",
|
|
65
|
+
"_neutron": {
|
|
66
|
+
"surface": "option"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"kind": "field",
|
|
71
|
+
"name": "storeName",
|
|
72
|
+
"type": {
|
|
73
|
+
"text": "string"
|
|
74
|
+
},
|
|
75
|
+
"privacy": "public",
|
|
76
|
+
"readonly": false,
|
|
77
|
+
"description": "Which Web Storage area to read. `local` survives the tab / browser closing and syncs across tabs; `session` is per-tab and gone when the tab closes. Changing it re-reads `key-name` from the new store.",
|
|
78
|
+
"default": "local",
|
|
79
|
+
"_neutron": {
|
|
80
|
+
"surface": "option"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"kind": "field",
|
|
85
|
+
"name": "provision",
|
|
86
|
+
"type": {
|
|
87
|
+
"text": "object"
|
|
88
|
+
},
|
|
89
|
+
"privacy": "public",
|
|
90
|
+
"readonly": false,
|
|
91
|
+
"description": "`JSON.parse(store.getItem(keyName))`, or `null` if the key is absent or the read failed. Not reflected as an attribute.",
|
|
92
|
+
"_neutron": {
|
|
93
|
+
"surface": "option"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"kind": "field",
|
|
98
|
+
"name": "isSuccess",
|
|
99
|
+
"type": {
|
|
100
|
+
"text": "boolean"
|
|
101
|
+
},
|
|
102
|
+
"privacy": "public",
|
|
103
|
+
"readonly": true,
|
|
104
|
+
"description": "The last read (parse) succeeded — including a legitimately absent key (`provision` is `null`, not an error).",
|
|
105
|
+
"_neutron": {
|
|
106
|
+
"surface": "state"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"kind": "field",
|
|
111
|
+
"name": "isError",
|
|
112
|
+
"type": {
|
|
113
|
+
"text": "boolean"
|
|
114
|
+
},
|
|
115
|
+
"privacy": "public",
|
|
116
|
+
"readonly": true,
|
|
117
|
+
"description": "`JSON.parse` threw on the last read (the stored value isn't valid JSON). `provision` is `null`.",
|
|
118
|
+
"_neutron": {
|
|
119
|
+
"surface": "state"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"events": [
|
|
124
|
+
{
|
|
125
|
+
"name": "provider-storage-changed",
|
|
126
|
+
"description": "Dispatched after a `storage` event from another tab for this element's `store-name` + `key-name` (or a `clear()` of that store) has been re-read into `provision` — so `provision`, `is-success` / `is-error` are already updated when it fires. The read also publishes `neutron-provision`, as usual.",
|
|
127
|
+
"type": {
|
|
128
|
+
"text": "ProviderStorageChangedEvent",
|
|
129
|
+
"expanded": "CustomEvent & { type: \"provider-storage-changed\"; detail: { keyName: string; oldValue: unknown; newValue: unknown; }; bubbles: true; cancelable: true; composed: true }"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"_neutron": {
|
|
134
|
+
"provisions": [
|
|
135
|
+
{
|
|
136
|
+
"name": "provision",
|
|
137
|
+
"type": {
|
|
138
|
+
"text": "object"
|
|
139
|
+
},
|
|
140
|
+
"description": "`JSON.parse(store.getItem(keyName))`, or `null` if the key is absent or the read failed. Not reflected as an attribute.",
|
|
141
|
+
"fieldName": "provision"
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
"exports": [
|
|
148
|
+
{
|
|
149
|
+
"kind": "js",
|
|
150
|
+
"name": "ProviderStorage",
|
|
151
|
+
"declaration": {
|
|
152
|
+
"name": "ProviderStorage",
|
|
153
|
+
"module": "provider-storage.ts"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"kind": "custom-element-definition",
|
|
158
|
+
"name": "provider-storage",
|
|
159
|
+
"declaration": {
|
|
160
|
+
"name": "ProviderStorage",
|
|
161
|
+
"module": "provider-storage.ts"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div>
|
|
2
|
+
<event-handler role="button" fire-event="demo-seed-storage">
|
|
3
|
+
Seed localStorage & re-read
|
|
4
|
+
</event-handler>
|
|
5
|
+
<provider-storage></provider-storage>
|
|
6
|
+
<output>Not read yet — click above.</output>
|
|
7
|
+
<small role="note">Same-tab writes only show up after
|
|
8
|
+
<code>key-name</code> is re-set; a write from another tab is picked up
|
|
9
|
+
live.</small>
|
|
10
|
+
<quark-sheet>
|
|
11
|
+
@use "/demo-utils" as *;
|
|
12
|
+
|
|
13
|
+
:scope {
|
|
14
|
+
@on demo-seed-storage (handle: seedDemoStorage);
|
|
15
|
+
@on neutron-provision (handle: setOutputFromElementData);
|
|
16
|
+
}
|
|
17
|
+
</quark-sheet>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# provider-storage
|
|
2
|
+
|
|
3
|
+
Read a JSON value out of `localStorage` / `sessionStorage` into `provision`,
|
|
4
|
+
declaratively — and keep it in sync across tabs. No app JS required to display
|
|
5
|
+
cached client state.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Declarative read** Point `key-name` at a storage key and read the result
|
|
10
|
+
- **Local or session** `store-name` picks `localStorage` (default) /
|
|
11
|
+
`sessionStorage`
|
|
12
|
+
- **Live across tabs** A write from another tab re-reads and fires
|
|
13
|
+
`provider-storage-changed`
|
|
14
|
+
- **Reactive to attributes** Changing `key-name` / `store-name` re-reads
|
|
15
|
+
immediately
|
|
16
|
+
- **Tiny, read-only** Never writes
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
`@excom/provider-storage` v0.1.0
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @excom/provider-storage
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @excom/provider-storage
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
yarn add @excom/provider-storage
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Import
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import "@excom/provider-storage";
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<provider-storage key-name="user-preferences"></provider-storage>
|
|
47
|
+
<provider-storage key-name="checkout-draft" store-name="session"></provider-storage>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Read-only.** It reads on every `key-name` / `store-name` set/change and never
|
|
51
|
+
writes. Removing `key-name` clears `provision` and both states.
|
|
52
|
+
|
|
53
|
+
**Live across tabs, not within one.** Browsers fire `storage` only in *other*
|
|
54
|
+
tabs, so:
|
|
55
|
+
|
|
56
|
+
- A write from another tab (or a `clear()` there) re-reads and fires
|
|
57
|
+
`provider-storage-changed` — no app code needed. `sessionStorage` is
|
|
58
|
+
per-tab, so this only applies to `store-name="local"`.
|
|
59
|
+
- A write by your own app code (`localStorage.setItem(...)`) in the same tab
|
|
60
|
+
won't appear until you re-trigger a read — re-set `key-name` (e.g. to `""`
|
|
61
|
+
and back) after writing.
|
|
62
|
+
|
|
63
|
+
### API Reference
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
#### Attributes
|
|
67
|
+
|
|
68
|
+
| Name | Surface | Type | Default | Values | Description |
|
|
69
|
+
| --- | --- | --- | --- | --- | --- |
|
|
70
|
+
| `key-name` | option | `string` | | `<storage key>` | Storage key to read. Setting or changing it re-reads immediately (parsed as JSON; `null` if absent). Removing it clears `provision` and both states. |
|
|
71
|
+
| `store-name` | option | `string` | `"local"` | `local, session` | Which Web Storage area to read. `local` survives the tab / browser closing and syncs across tabs; `session` is per-tab and gone when the tab closes. Changing it re-reads `key-name` from the new store. |
|
|
72
|
+
| `is-success` | state | `boolean` | | | The last read (parse) succeeded — including a legitimately absent key (`provision` is `null`, not an error). |
|
|
73
|
+
| `is-error` | state | `boolean` | | | `JSON.parse` threw on the last read (the stored value isn't valid JSON). `provision` is `null`. |
|
|
74
|
+
|
|
75
|
+
#### Provision
|
|
76
|
+
|
|
77
|
+
| Name | Type | Description |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `provision` | `object` | `JSON.parse(store.getItem(keyName))`, or `null` if the key is absent or the read failed. Not reflected as an attribute. |
|
|
80
|
+
|
|
81
|
+
#### Fires
|
|
82
|
+
|
|
83
|
+
| Name | Type | Description |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| `provider-storage-changed` | `ProviderStorageChangedEvent` (`CustomEvent & { type: "provider-storage-changed"; detail: { keyName: string; oldValue: unknown; newValue: unknown; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched after a `storage` event from another tab for this element's `store-name` + `key-name` (or a `clear()` of that store) has been re-read into `provision` — so `provision`, `is-success` / `is-error` are already updated when it fires. The read also publishes `neutron-provision`, as usual. |
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### Examples
|
|
90
|
+
|
|
91
|
+
#### Seed & re-read
|
|
92
|
+
|
|
93
|
+
Since this element never writes, the demo below seeds a value from a button
|
|
94
|
+
(re-setting `key-name` afterward to force the same-tab re-read) so you can see
|
|
95
|
+
it work without opening devtools. Open this page in a second tab and click
|
|
96
|
+
there too — this tab updates on its own.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<div>
|
|
101
|
+
<event-handler role="button" fire-event="demo-seed-storage">
|
|
102
|
+
Seed localStorage & re-read
|
|
103
|
+
</event-handler>
|
|
104
|
+
<provider-storage></provider-storage>
|
|
105
|
+
<output>Not read yet — click above.</output>
|
|
106
|
+
<small role="note">Same-tab writes only show up after
|
|
107
|
+
<code>key-name</code> is re-set; a write from another tab is picked up
|
|
108
|
+
live.</small>
|
|
109
|
+
<quark-sheet>
|
|
110
|
+
@use "/demo-utils" as *;
|
|
111
|
+
|
|
112
|
+
:scope {
|
|
113
|
+
@on demo-seed-storage (handle: seedDemoStorage);
|
|
114
|
+
@on neutron-provision (handle: setOutputFromElementData);
|
|
115
|
+
}
|
|
116
|
+
</quark-sheet>
|
|
117
|
+
</div>
|
|
118
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# provider-storage
|
|
2
|
+
|
|
3
|
+
Read a JSON value out of `localStorage` / `sessionStorage` into `provision`,
|
|
4
|
+
declaratively — and keep it in sync across tabs. No app JS required to display
|
|
5
|
+
cached client state.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Declarative read** Point `key-name` at a storage key and read the result
|
|
10
|
+
- **Local or session** `store-name` picks `localStorage` (default) /
|
|
11
|
+
`sessionStorage`
|
|
12
|
+
- **Live across tabs** A write from another tab re-reads and fires
|
|
13
|
+
`provider-storage-changed`
|
|
14
|
+
- **Reactive to attributes** Changing `key-name` / `store-name` re-reads
|
|
15
|
+
immediately
|
|
16
|
+
- **Tiny, read-only** Never writes
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
<include-content is-active template-ref="/views/install-section/install-section.html"></include-content>
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<provider-storage key-name="user-preferences"></provider-storage>
|
|
26
|
+
<provider-storage key-name="checkout-draft" store-name="session"></provider-storage>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Read-only.** It reads on every `key-name` / `store-name` set/change and never
|
|
30
|
+
writes. Removing `key-name` clears `provision` and both states.
|
|
31
|
+
|
|
32
|
+
**Live across tabs, not within one.** Browsers fire `storage` only in *other*
|
|
33
|
+
tabs, so:
|
|
34
|
+
|
|
35
|
+
- A write from another tab (or a `clear()` there) re-reads and fires
|
|
36
|
+
`provider-storage-changed` — no app code needed. `sessionStorage` is
|
|
37
|
+
per-tab, so this only applies to `store-name="local"`.
|
|
38
|
+
- A write by your own app code (`localStorage.setItem(...)`) in the same tab
|
|
39
|
+
won't appear until you re-trigger a read — re-set `key-name` (e.g. to `""`
|
|
40
|
+
and back) after writing.
|
|
41
|
+
|
|
42
|
+
### API Reference
|
|
43
|
+
|
|
44
|
+
<include-content is-active template-ref="/views/api-reference/api-reference.html"></include-content>
|
|
45
|
+
|
|
46
|
+
### Examples
|
|
47
|
+
|
|
48
|
+
#### Seed & re-read
|
|
49
|
+
|
|
50
|
+
Since this element never writes, the demo below seeds a value from a button
|
|
51
|
+
(re-setting `key-name` afterward to force the same-tab re-read) so you can see
|
|
52
|
+
it work without opening devtools. Open this page in a second tab and click
|
|
53
|
+
there too — this tab updates on its own.
|
|
54
|
+
|
|
55
|
+
<include-content data-demo="simple"></include-content>
|