@plantops/web-kit 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.
Files changed (42) hide show
  1. package/README.md +88 -0
  2. package/dist/claims.d.ts +35 -0
  3. package/dist/claims.d.ts.map +1 -0
  4. package/dist/claims.js +76 -0
  5. package/dist/errors.d.ts +46 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/errors.js +52 -0
  8. package/dist/grants-provider.d.ts +51 -0
  9. package/dist/grants-provider.d.ts.map +1 -0
  10. package/dist/grants-provider.js +46 -0
  11. package/dist/iam-provider.d.ts +95 -0
  12. package/dist/iam-provider.d.ts.map +1 -0
  13. package/dist/iam-provider.js +188 -0
  14. package/dist/index.d.ts +51 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +50 -0
  17. package/dist/plantops-provider.d.ts +48 -0
  18. package/dist/plantops-provider.d.ts.map +1 -0
  19. package/dist/plantops-provider.js +8 -0
  20. package/dist/require-auth.d.ts +45 -0
  21. package/dist/require-auth.d.ts.map +1 -0
  22. package/dist/require-auth.js +58 -0
  23. package/dist/scope-coverage.d.ts +54 -0
  24. package/dist/scope-coverage.d.ts.map +1 -0
  25. package/dist/scope-coverage.js +41 -0
  26. package/dist/token-store.d.ts +92 -0
  27. package/dist/token-store.d.ts.map +1 -0
  28. package/dist/token-store.js +144 -0
  29. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  30. package/dist/use-async.d.ts +43 -0
  31. package/dist/use-async.d.ts.map +1 -0
  32. package/dist/use-async.js +76 -0
  33. package/dist/use-navigation.d.ts +32 -0
  34. package/dist/use-navigation.d.ts.map +1 -0
  35. package/dist/use-navigation.js +24 -0
  36. package/dist/use-notices.d.ts +47 -0
  37. package/dist/use-notices.d.ts.map +1 -0
  38. package/dist/use-notices.js +60 -0
  39. package/dist/use-permission.d.ts +91 -0
  40. package/dist/use-permission.d.ts.map +1 -0
  41. package/dist/use-permission.js +48 -0
  42. package/package.json +49 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAErE,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,CAAC;AAoCzE;;;;;;GAMG;AACH,qBAAa,iBAAkB,YAAW,UAAU;IAItC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAHvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;gBAE7B,UAAU,GAAE,MAA0B;IAQnE;;;;;;;;;OASG;IACH,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAcnD,gEAAgE;IAChE,OAAO,IAAI,IAAI;IAKf,IAAI,IAAI,YAAY,GAAG,IAAI;IAI3B,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI;IAUxC,WAAW,IAAI,aAAa,GAAG,IAAI;IAQnC,+EAA+E;IAC/E,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI;IAMlD,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,IAAI;CAGb"}
@@ -0,0 +1,144 @@
1
+ 'use client';
2
+ /** Default slot. Namespaced so two PlantOps consoles on one host can differ. */
3
+ export const TOKEN_STORAGE_KEY = 'plantops.tokens';
4
+ function safeParse(raw) {
5
+ if (raw === null)
6
+ return null;
7
+ try {
8
+ const parsed = JSON.parse(raw);
9
+ if (typeof parsed !== 'object' || parsed === null)
10
+ return null;
11
+ const { tokens, identity } = parsed;
12
+ if (typeof tokens !== 'object' ||
13
+ tokens === null ||
14
+ typeof tokens.accessToken !== 'string') {
15
+ return null;
16
+ }
17
+ return {
18
+ tokens: {
19
+ accessToken: tokens.accessToken,
20
+ refreshToken: typeof tokens.refreshToken === 'string' ? tokens.refreshToken : null,
21
+ expiresAt: typeof tokens.expiresAt === 'number' ? tokens.expiresAt : null,
22
+ },
23
+ identity: identity != null &&
24
+ typeof identity.email === 'string' &&
25
+ typeof identity.clientSlug === 'string'
26
+ ? { email: identity.email, clientSlug: identity.clientSlug }
27
+ : null,
28
+ };
29
+ }
30
+ catch {
31
+ // A hand-edited or half-written entry is treated as "signed out" rather
32
+ // than crashing the shell. The user signs in again; nothing is lost that
33
+ // was not already unusable.
34
+ return null;
35
+ }
36
+ }
37
+ /**
38
+ * A `TokenStore` backed by `localStorage`, with change notification.
39
+ *
40
+ * Safe to construct during server rendering: every method tolerates the absence
41
+ * of `window`, answering "no session", which is the correct answer on a server
42
+ * that has no user.
43
+ */
44
+ export class BrowserTokenStore {
45
+ storageKey;
46
+ listeners = new Set();
47
+ onStorage;
48
+ constructor(storageKey = TOKEN_STORAGE_KEY) {
49
+ this.storageKey = storageKey;
50
+ this.onStorage = (event) => {
51
+ if (event.storageArea !== this.storage())
52
+ return;
53
+ if (event.key !== null && event.key !== this.storageKey)
54
+ return;
55
+ this.emit(this.readSession());
56
+ };
57
+ }
58
+ /**
59
+ * Notified on every change, in this tab and in others.
60
+ *
61
+ * The `storage` listener is attached with the first subscriber and detached
62
+ * with the last, rather than in the constructor. That is what makes the store
63
+ * survive React's development double-mount: a listener attached at
64
+ * construction and removed on unmount is gone for good after the first
65
+ * remount — cross-tab sign-out then silently stops working, in development
66
+ * only, which is the worst place for it to hide.
67
+ */
68
+ subscribe(listener) {
69
+ if (this.listeners.size === 0) {
70
+ globalThis.addEventListener?.('storage', this.onStorage);
71
+ }
72
+ this.listeners.add(listener);
73
+ return () => {
74
+ this.listeners.delete(listener);
75
+ if (this.listeners.size === 0) {
76
+ globalThis.removeEventListener?.('storage', this.onStorage);
77
+ }
78
+ };
79
+ }
80
+ /** Drops every subscriber and the window listener with them. */
81
+ dispose() {
82
+ this.listeners.clear();
83
+ globalThis.removeEventListener?.('storage', this.onStorage);
84
+ }
85
+ read() {
86
+ return this.readSession()?.tokens ?? null;
87
+ }
88
+ write(tokens) {
89
+ if (tokens === null) {
90
+ this.clear();
91
+ return;
92
+ }
93
+ // A refresh rotates the tokens and knows nothing about the identity hint,
94
+ // so the hint is carried across rather than dropped on every renewal.
95
+ this.writeSession({ tokens, identity: this.readSession()?.identity ?? null });
96
+ }
97
+ readSession() {
98
+ try {
99
+ return safeParse(this.storage()?.getItem(this.storageKey) ?? null);
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ /** Records who signed in, for the header. Never used to authorise anything. */
106
+ writeIdentity(identity) {
107
+ const current = this.readSession();
108
+ if (current === null)
109
+ return;
110
+ this.writeSession({ ...current, identity });
111
+ }
112
+ clear() {
113
+ try {
114
+ this.storage()?.removeItem(this.storageKey);
115
+ }
116
+ catch {
117
+ /* storage unavailable — nothing was written to remove */
118
+ }
119
+ this.emit(null);
120
+ }
121
+ writeSession(session) {
122
+ try {
123
+ this.storage()?.setItem(this.storageKey, JSON.stringify(session));
124
+ }
125
+ catch {
126
+ // Quota, private mode, or a policy that blocks storage. The session still
127
+ // works for this page's lifetime through the client's in-memory copy; it
128
+ // simply will not survive a reload.
129
+ }
130
+ this.emit(session);
131
+ }
132
+ storage() {
133
+ try {
134
+ return globalThis.localStorage ?? null;
135
+ }
136
+ catch {
137
+ return null;
138
+ }
139
+ }
140
+ emit(session) {
141
+ for (const listener of this.listeners)
142
+ listener(session);
143
+ }
144
+ }