@prabhask5/stellar-engine 1.1.6 → 1.1.8
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 +68 -25
- package/dist/actions/remoteChange.d.ts +143 -18
- package/dist/actions/remoteChange.d.ts.map +1 -1
- package/dist/actions/remoteChange.js +182 -58
- package/dist/actions/remoteChange.js.map +1 -1
- package/dist/actions/truncateTooltip.d.ts +56 -0
- package/dist/actions/truncateTooltip.d.ts.map +1 -0
- package/dist/actions/truncateTooltip.js +312 -0
- package/dist/actions/truncateTooltip.js.map +1 -0
- package/dist/auth/admin.d.ts +40 -3
- package/dist/auth/admin.d.ts.map +1 -1
- package/dist/auth/admin.js +45 -5
- package/dist/auth/admin.js.map +1 -1
- package/dist/auth/crypto.d.ts +55 -5
- package/dist/auth/crypto.d.ts.map +1 -1
- package/dist/auth/crypto.js +58 -5
- package/dist/auth/crypto.js.map +1 -1
- package/dist/auth/deviceVerification.d.ts +236 -20
- package/dist/auth/deviceVerification.d.ts.map +1 -1
- package/dist/auth/deviceVerification.js +293 -40
- package/dist/auth/deviceVerification.js.map +1 -1
- package/dist/auth/displayUtils.d.ts +98 -0
- package/dist/auth/displayUtils.d.ts.map +1 -0
- package/dist/auth/displayUtils.js +133 -0
- package/dist/auth/displayUtils.js.map +1 -0
- package/dist/auth/loginGuard.d.ts +108 -14
- package/dist/auth/loginGuard.d.ts.map +1 -1
- package/dist/auth/loginGuard.js +153 -31
- package/dist/auth/loginGuard.js.map +1 -1
- package/dist/auth/offlineCredentials.d.ts +132 -15
- package/dist/auth/offlineCredentials.d.ts.map +1 -1
- package/dist/auth/offlineCredentials.js +167 -23
- package/dist/auth/offlineCredentials.js.map +1 -1
- package/dist/auth/offlineLogin.d.ts +96 -10
- package/dist/auth/offlineLogin.d.ts.map +1 -1
- package/dist/auth/offlineLogin.js +82 -15
- package/dist/auth/offlineLogin.js.map +1 -1
- package/dist/auth/offlineSession.d.ts +83 -9
- package/dist/auth/offlineSession.d.ts.map +1 -1
- package/dist/auth/offlineSession.js +104 -13
- package/dist/auth/offlineSession.js.map +1 -1
- package/dist/auth/resolveAuthState.d.ts +70 -8
- package/dist/auth/resolveAuthState.d.ts.map +1 -1
- package/dist/auth/resolveAuthState.js +142 -46
- package/dist/auth/resolveAuthState.js.map +1 -1
- package/dist/auth/singleUser.d.ts +390 -37
- package/dist/auth/singleUser.d.ts.map +1 -1
- package/dist/auth/singleUser.js +505 -133
- package/dist/auth/singleUser.js.map +1 -1
- package/dist/bin/install-pwa.d.ts +25 -0
- package/dist/bin/install-pwa.d.ts.map +1 -0
- package/dist/bin/install-pwa.js +2197 -0
- package/dist/bin/install-pwa.js.map +1 -0
- package/dist/config.d.ts +132 -12
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +87 -9
- package/dist/config.js.map +1 -1
- package/dist/conflicts.d.ts +246 -23
- package/dist/conflicts.d.ts.map +1 -1
- package/dist/conflicts.js +495 -46
- package/dist/conflicts.js.map +1 -1
- package/dist/data.d.ts +338 -18
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +385 -34
- package/dist/data.js.map +1 -1
- package/dist/database.d.ts +72 -14
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +120 -29
- package/dist/database.js.map +1 -1
- package/dist/debug.d.ts +77 -1
- package/dist/debug.d.ts.map +1 -1
- package/dist/debug.js +88 -1
- package/dist/debug.js.map +1 -1
- package/dist/deviceId.d.ts +38 -7
- package/dist/deviceId.d.ts.map +1 -1
- package/dist/deviceId.js +68 -10
- package/dist/deviceId.js.map +1 -1
- package/dist/engine.d.ts +175 -3
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +831 -110
- package/dist/engine.js.map +1 -1
- package/dist/entries/actions.d.ts +14 -0
- package/dist/entries/actions.d.ts.map +1 -1
- package/dist/entries/actions.js +27 -1
- package/dist/entries/actions.js.map +1 -1
- package/dist/entries/auth.d.ts +16 -0
- package/dist/entries/auth.d.ts.map +1 -1
- package/dist/entries/auth.js +73 -1
- package/dist/entries/auth.js.map +1 -1
- package/dist/entries/config.d.ts +12 -0
- package/dist/entries/config.d.ts.map +1 -1
- package/dist/entries/config.js +18 -1
- package/dist/entries/config.js.map +1 -1
- package/dist/entries/kit.d.ts +21 -9
- package/dist/entries/kit.d.ts.map +1 -1
- package/dist/entries/kit.js +57 -8
- package/dist/entries/kit.js.map +1 -1
- package/dist/entries/stores.d.ts +11 -0
- package/dist/entries/stores.d.ts.map +1 -1
- package/dist/entries/stores.js +43 -2
- package/dist/entries/stores.js.map +1 -1
- package/dist/entries/types.d.ts +11 -1
- package/dist/entries/types.d.ts.map +1 -1
- package/dist/entries/types.js +10 -0
- package/dist/entries/types.js.map +1 -1
- package/dist/entries/utils.d.ts +7 -1
- package/dist/entries/utils.d.ts.map +1 -1
- package/dist/entries/utils.js +23 -2
- package/dist/entries/utils.js.map +1 -1
- package/dist/entries/vite.d.ts +20 -0
- package/dist/entries/vite.d.ts.map +1 -0
- package/dist/entries/vite.js +26 -0
- package/dist/entries/vite.js.map +1 -0
- package/dist/index.d.ts +33 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +176 -21
- package/dist/index.js.map +1 -1
- package/dist/kit/auth.d.ts +80 -0
- package/dist/kit/auth.d.ts.map +1 -0
- package/dist/kit/auth.js +72 -0
- package/dist/kit/auth.js.map +1 -0
- package/dist/kit/confirm.d.ts +111 -0
- package/dist/kit/confirm.d.ts.map +1 -0
- package/dist/kit/confirm.js +169 -0
- package/dist/kit/confirm.js.map +1 -0
- package/dist/kit/loads.d.ts +189 -0
- package/dist/kit/loads.d.ts.map +1 -0
- package/dist/kit/loads.js +205 -0
- package/dist/kit/loads.js.map +1 -0
- package/dist/kit/server.d.ts +175 -0
- package/dist/kit/server.d.ts.map +1 -0
- package/dist/kit/server.js +297 -0
- package/dist/kit/server.js.map +1 -0
- package/dist/kit/sw.d.ts +176 -0
- package/dist/kit/sw.d.ts.map +1 -0
- package/dist/kit/sw.js +320 -0
- package/dist/kit/sw.js.map +1 -0
- package/dist/queue.d.ts +274 -0
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +556 -38
- package/dist/queue.js.map +1 -1
- package/dist/realtime.d.ts +241 -27
- package/dist/realtime.d.ts.map +1 -1
- package/dist/realtime.js +633 -109
- package/dist/realtime.js.map +1 -1
- package/dist/runtime/runtimeConfig.d.ts +91 -16
- package/dist/runtime/runtimeConfig.d.ts.map +1 -1
- package/dist/runtime/runtimeConfig.js +146 -19
- package/dist/runtime/runtimeConfig.js.map +1 -1
- package/dist/stores/authState.d.ts +150 -11
- package/dist/stores/authState.d.ts.map +1 -1
- package/dist/stores/authState.js +169 -17
- package/dist/stores/authState.js.map +1 -1
- package/dist/stores/network.d.ts +39 -0
- package/dist/stores/network.d.ts.map +1 -1
- package/dist/stores/network.js +169 -16
- package/dist/stores/network.js.map +1 -1
- package/dist/stores/remoteChanges.d.ts +327 -52
- package/dist/stores/remoteChanges.d.ts.map +1 -1
- package/dist/stores/remoteChanges.js +337 -75
- package/dist/stores/remoteChanges.js.map +1 -1
- package/dist/stores/sync.d.ts +130 -0
- package/dist/stores/sync.d.ts.map +1 -1
- package/dist/stores/sync.js +167 -7
- package/dist/stores/sync.js.map +1 -1
- package/dist/supabase/auth.d.ts +326 -19
- package/dist/supabase/auth.d.ts.map +1 -1
- package/dist/supabase/auth.js +374 -26
- package/dist/supabase/auth.js.map +1 -1
- package/dist/supabase/client.d.ts +79 -6
- package/dist/supabase/client.d.ts.map +1 -1
- package/dist/supabase/client.js +158 -15
- package/dist/supabase/client.js.map +1 -1
- package/dist/supabase/validate.d.ts +101 -7
- package/dist/supabase/validate.d.ts.map +1 -1
- package/dist/supabase/validate.js +117 -8
- package/dist/supabase/validate.js.map +1 -1
- package/dist/sw/build/vite-plugin.d.ts +74 -0
- package/dist/sw/build/vite-plugin.d.ts.map +1 -0
- package/dist/sw/build/vite-plugin.js +183 -0
- package/dist/sw/build/vite-plugin.js.map +1 -0
- package/dist/sw/sw.js +669 -0
- package/dist/types.d.ts +150 -45
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +12 -10
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +55 -13
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +83 -22
- package/dist/utils.js.map +1 -1
- package/package.json +20 -22
- package/src/components/DeferredChangesBanner.svelte +477 -0
- package/src/components/SyncStatus.svelte +1732 -0
- package/dist/crdt/awareness.d.ts +0 -54
- package/dist/crdt/awareness.d.ts.map +0 -1
- package/dist/crdt/awareness.js +0 -219
- package/dist/crdt/awareness.js.map +0 -1
- package/dist/crdt/doc.d.ts +0 -56
- package/dist/crdt/doc.d.ts.map +0 -1
- package/dist/crdt/doc.js +0 -130
- package/dist/crdt/doc.js.map +0 -1
- package/dist/crdt/index.d.ts +0 -15
- package/dist/crdt/index.d.ts.map +0 -1
- package/dist/crdt/index.js +0 -20
- package/dist/crdt/index.js.map +0 -1
- package/dist/crdt/offline.d.ts +0 -91
- package/dist/crdt/offline.d.ts.map +0 -1
- package/dist/crdt/offline.js +0 -353
- package/dist/crdt/offline.js.map +0 -1
- package/dist/crdt/sync.d.ts +0 -58
- package/dist/crdt/sync.d.ts.map +0 -1
- package/dist/crdt/sync.js +0 -399
- package/dist/crdt/sync.js.map +0 -1
- package/dist/crdt/types.d.ts +0 -62
- package/dist/crdt/types.d.ts.map +0 -1
- package/dist/crdt/types.js +0 -7
- package/dist/crdt/types.js.map +0 -1
- package/dist/email/sendEmail.d.ts +0 -31
- package/dist/email/sendEmail.d.ts.map +0 -1
- package/dist/email/sendEmail.js +0 -39
- package/dist/email/sendEmail.js.map +0 -1
- package/dist/email/validateSmtp.d.ts +0 -18
- package/dist/email/validateSmtp.d.ts.map +0 -1
- package/dist/email/validateSmtp.js +0 -33
- package/dist/email/validateSmtp.js.map +0 -1
- package/dist/entries/crdt.d.ts +0 -3
- package/dist/entries/crdt.d.ts.map +0 -1
- package/dist/entries/crdt.js +0 -13
- package/dist/entries/crdt.js.map +0 -1
- package/dist/entries/email.d.ts +0 -4
- package/dist/entries/email.d.ts.map +0 -1
- package/dist/entries/email.js +0 -4
- package/dist/entries/email.js.map +0 -1
- package/dist/kit/authPresets.d.ts +0 -28
- package/dist/kit/authPresets.d.ts.map +0 -1
- package/dist/kit/authPresets.js +0 -23
- package/dist/kit/authPresets.js.map +0 -1
- package/dist/kit/configEndpoint.d.ts +0 -18
- package/dist/kit/configEndpoint.d.ts.map +0 -1
- package/dist/kit/configEndpoint.js +0 -27
- package/dist/kit/configEndpoint.js.map +0 -1
- package/dist/kit/deployEndpoint.d.ts +0 -22
- package/dist/kit/deployEndpoint.d.ts.map +0 -1
- package/dist/kit/deployEndpoint.js +0 -79
- package/dist/kit/deployEndpoint.js.map +0 -1
- package/dist/kit/layoutLoad.d.ts +0 -23
- package/dist/kit/layoutLoad.d.ts.map +0 -1
- package/dist/kit/layoutLoad.js +0 -41
- package/dist/kit/layoutLoad.js.map +0 -1
- package/dist/kit/protectedLoad.d.ts +0 -16
- package/dist/kit/protectedLoad.d.ts.map +0 -1
- package/dist/kit/protectedLoad.js +0 -28
- package/dist/kit/protectedLoad.js.map +0 -1
- package/dist/kit/setupLoad.d.ts +0 -11
- package/dist/kit/setupLoad.d.ts.map +0 -1
- package/dist/kit/setupLoad.js +0 -28
- package/dist/kit/setupLoad.js.map +0 -1
- package/dist/kit/validateEndpoint.d.ts +0 -9
- package/dist/kit/validateEndpoint.d.ts.map +0 -1
- package/dist/kit/validateEndpoint.js +0 -25
- package/dist/kit/validateEndpoint.js.map +0 -1
- package/dist/kit/vercelApi.d.ts +0 -6
- package/dist/kit/vercelApi.d.ts.map +0 -1
- package/dist/kit/vercelApi.js +0 -48
- package/dist/kit/vercelApi.js.map +0 -1
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Svelte action for truncated-text tooltips.
|
|
3
|
+
*
|
|
4
|
+
* Provides a `use:truncateTooltip` directive that enforces CSS text-overflow
|
|
5
|
+
* ellipsis on the target element and shows a floating tooltip with the
|
|
6
|
+
* **full** text whenever the content is visually truncated.
|
|
7
|
+
*
|
|
8
|
+
* **Behaviour by device type:**
|
|
9
|
+
* - **Desktop** — tooltip appears on `mouseenter`, hides on `mouseleave`.
|
|
10
|
+
* Only shown when `scrollWidth > clientWidth` (text is actually clipped).
|
|
11
|
+
* - **Mobile** — tooltip appears on `touchstart` (tap), dismisses on
|
|
12
|
+
* tap-outside or after a 3-second auto-dismiss timeout.
|
|
13
|
+
*
|
|
14
|
+
* A **singleton** tooltip `<div>` is lazily appended to `document.body` and
|
|
15
|
+
* reused across all instances of the action to avoid DOM bloat.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <span class="my-text" use:truncateTooltip>{longText}</span>
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see {@link truncateTooltip} for the Svelte action export
|
|
23
|
+
* @see {@link showTooltip} for the display logic
|
|
24
|
+
* @see {@link positionTooltip} for the positioning algorithm
|
|
25
|
+
*/
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// SINGLETON TOOLTIP STATE
|
|
28
|
+
// =============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* The shared tooltip DOM element — lazily created by {@link getTooltip}.
|
|
31
|
+
* Only one tooltip element exists in the entire document, regardless of
|
|
32
|
+
* how many `use:truncateTooltip` instances are active.
|
|
33
|
+
*/
|
|
34
|
+
let tooltipEl = null;
|
|
35
|
+
/**
|
|
36
|
+
* Handle for the mobile auto-dismiss `setTimeout`.
|
|
37
|
+
* Cleared on manual hide to prevent stale timeouts from
|
|
38
|
+
* dismissing a newly-shown tooltip.
|
|
39
|
+
*/
|
|
40
|
+
let hideTimeout = null;
|
|
41
|
+
/**
|
|
42
|
+
* The DOM element that currently "owns" the visible tooltip.
|
|
43
|
+
* Used to prevent hide events from one element dismissing another's tooltip,
|
|
44
|
+
* and to implement tap-toggle behaviour on mobile (tap same element = hide).
|
|
45
|
+
*/
|
|
46
|
+
let currentOwner = null;
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// TOOLTIP ELEMENT MANAGEMENT
|
|
49
|
+
// =============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Return the singleton tooltip element, creating it on first call.
|
|
52
|
+
*
|
|
53
|
+
* The element is given the CSS class `truncate-tooltip` and an ARIA
|
|
54
|
+
* `role="tooltip"` attribute for accessibility. The consuming app must
|
|
55
|
+
* provide CSS for `.truncate-tooltip` (positioning, background, etc.)
|
|
56
|
+
* and a `.visible` modifier class to control opacity/display.
|
|
57
|
+
*
|
|
58
|
+
* @returns The shared tooltip `HTMLElement`.
|
|
59
|
+
*/
|
|
60
|
+
function getTooltip() {
|
|
61
|
+
if (!tooltipEl) {
|
|
62
|
+
tooltipEl = document.createElement('div');
|
|
63
|
+
tooltipEl.className = 'truncate-tooltip';
|
|
64
|
+
tooltipEl.setAttribute('role', 'tooltip');
|
|
65
|
+
document.body.appendChild(tooltipEl);
|
|
66
|
+
}
|
|
67
|
+
return tooltipEl;
|
|
68
|
+
}
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// TRUNCATION DETECTION
|
|
71
|
+
// =============================================================================
|
|
72
|
+
/**
|
|
73
|
+
* Check whether an element's text content is visually truncated.
|
|
74
|
+
*
|
|
75
|
+
* Compares `scrollWidth` (full content width including overflow) against
|
|
76
|
+
* `clientWidth` (visible width). When `scrollWidth` exceeds `clientWidth`,
|
|
77
|
+
* the CSS `text-overflow: ellipsis` rule is hiding part of the text.
|
|
78
|
+
*
|
|
79
|
+
* @param el - The DOM element to test.
|
|
80
|
+
* @returns `true` if the text overflows its container.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* if (isTruncated(spanElement)) {
|
|
85
|
+
* showTooltip(spanElement);
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
function isTruncated(el) {
|
|
90
|
+
return el.scrollWidth > el.clientWidth;
|
|
91
|
+
}
|
|
92
|
+
// =============================================================================
|
|
93
|
+
// TOOLTIP POSITIONING
|
|
94
|
+
// =============================================================================
|
|
95
|
+
/**
|
|
96
|
+
* Position the tooltip relative to an anchor element.
|
|
97
|
+
*
|
|
98
|
+
* Default placement is **centred above** the anchor with an 8 px gap.
|
|
99
|
+
* Two edge-case corrections are applied:
|
|
100
|
+
* 1. If the tooltip would overflow the **top** of the viewport, it flips
|
|
101
|
+
* to appear **below** the anchor instead.
|
|
102
|
+
* 2. The horizontal position is clamped to keep the tooltip within the
|
|
103
|
+
* viewport (8 px padding on each side).
|
|
104
|
+
*
|
|
105
|
+
* @param tooltip - The tooltip element to reposition.
|
|
106
|
+
* @param anchor - The element the tooltip should point at.
|
|
107
|
+
*/
|
|
108
|
+
function positionTooltip(tooltip, anchor) {
|
|
109
|
+
const rect = anchor.getBoundingClientRect();
|
|
110
|
+
const tooltipRect = tooltip.getBoundingClientRect();
|
|
111
|
+
/* ── Default: centred above the anchor ──── */
|
|
112
|
+
let left = rect.left + rect.width / 2 - tooltipRect.width / 2;
|
|
113
|
+
let top = rect.top - tooltipRect.height - 8;
|
|
114
|
+
/* Flip below if tooltip would overflow the top edge */
|
|
115
|
+
if (top < 4) {
|
|
116
|
+
top = rect.bottom + 8;
|
|
117
|
+
}
|
|
118
|
+
/* ── Horizontal clamping ──── */
|
|
119
|
+
const maxLeft = window.innerWidth - tooltipRect.width - 8;
|
|
120
|
+
left = Math.max(8, Math.min(left, maxLeft));
|
|
121
|
+
tooltip.style.left = `${left}px`;
|
|
122
|
+
tooltip.style.top = `${top}px`;
|
|
123
|
+
}
|
|
124
|
+
// =============================================================================
|
|
125
|
+
// SHOW / HIDE LOGIC
|
|
126
|
+
// =============================================================================
|
|
127
|
+
/**
|
|
128
|
+
* Show the tooltip for the given anchor element.
|
|
129
|
+
*
|
|
130
|
+
* Exits early if the anchor's text is **not** truncated (no tooltip needed).
|
|
131
|
+
* Positioning is deferred to the next animation frame so that the tooltip's
|
|
132
|
+
* dimensions are accurate after its `textContent` is set.
|
|
133
|
+
*
|
|
134
|
+
* @param anchor - The element whose full text should be displayed in the tooltip.
|
|
135
|
+
*/
|
|
136
|
+
function showTooltip(anchor) {
|
|
137
|
+
if (!isTruncated(anchor))
|
|
138
|
+
return;
|
|
139
|
+
const tooltip = getTooltip();
|
|
140
|
+
const fullText = anchor.textContent?.trim() || '';
|
|
141
|
+
if (!fullText)
|
|
142
|
+
return;
|
|
143
|
+
/* Cancel any pending auto-dismiss from a previous mobile tap */
|
|
144
|
+
if (hideTimeout) {
|
|
145
|
+
clearTimeout(hideTimeout);
|
|
146
|
+
hideTimeout = null;
|
|
147
|
+
}
|
|
148
|
+
tooltip.textContent = fullText;
|
|
149
|
+
tooltip.classList.add('visible');
|
|
150
|
+
currentOwner = anchor;
|
|
151
|
+
/* Position after content is set so dimensions are correct */
|
|
152
|
+
requestAnimationFrame(() => {
|
|
153
|
+
positionTooltip(tooltip, anchor);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Hide the tooltip and reset ownership state.
|
|
158
|
+
*
|
|
159
|
+
* Safe to call even when no tooltip is visible — the function is a no-op
|
|
160
|
+
* in that case. Also clears any pending auto-dismiss timeout.
|
|
161
|
+
*/
|
|
162
|
+
function hideTooltip() {
|
|
163
|
+
if (tooltipEl) {
|
|
164
|
+
tooltipEl.classList.remove('visible');
|
|
165
|
+
}
|
|
166
|
+
currentOwner = null;
|
|
167
|
+
if (hideTimeout) {
|
|
168
|
+
clearTimeout(hideTimeout);
|
|
169
|
+
hideTimeout = null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// DEVICE DETECTION
|
|
174
|
+
// =============================================================================
|
|
175
|
+
/**
|
|
176
|
+
* Detect whether the current device supports touch input.
|
|
177
|
+
*
|
|
178
|
+
* Uses feature detection (`ontouchstart` in `window` or `maxTouchPoints`)
|
|
179
|
+
* rather than user-agent sniffing, which is more reliable across browsers
|
|
180
|
+
* and avoids false negatives on hybrid devices.
|
|
181
|
+
*
|
|
182
|
+
* @returns `true` on touch-capable devices.
|
|
183
|
+
*/
|
|
184
|
+
function isTouchDevice() {
|
|
185
|
+
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
186
|
+
}
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// SVELTE ACTION EXPORT
|
|
189
|
+
// =============================================================================
|
|
190
|
+
/**
|
|
191
|
+
* Svelte action that applies truncation-aware tooltips to an element.
|
|
192
|
+
*
|
|
193
|
+
* **On mount the action:**
|
|
194
|
+
* 1. Forces CSS `overflow: hidden`, `text-overflow: ellipsis`, and
|
|
195
|
+
* `white-space: nowrap` on the node to guarantee ellipsis rendering.
|
|
196
|
+
* 2. Registers `mouseenter` / `mouseleave` handlers for desktop hover.
|
|
197
|
+
* 3. Registers `touchstart` handlers for mobile tap-to-show behaviour.
|
|
198
|
+
* 4. Registers a document-level `touchstart` listener for tap-outside
|
|
199
|
+
* dismissal on mobile.
|
|
200
|
+
*
|
|
201
|
+
* The returned `destroy` callback cleans up all listeners and hides the
|
|
202
|
+
* tooltip if the destroyed node was its current owner.
|
|
203
|
+
*
|
|
204
|
+
* @param node - The DOM element to enhance with truncation tooltips.
|
|
205
|
+
* @returns A Svelte action lifecycle object with a `destroy` method.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```svelte
|
|
209
|
+
* <span class="my-text" use:truncateTooltip>{longText}</span>
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export function truncateTooltip(node) {
|
|
213
|
+
/* ── Apply ellipsis CSS ──── */
|
|
214
|
+
node.style.overflow = 'hidden';
|
|
215
|
+
node.style.textOverflow = 'ellipsis';
|
|
216
|
+
node.style.whiteSpace = 'nowrap';
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// DESKTOP: HOVER HANDLERS
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
/**
|
|
221
|
+
* Show tooltip on mouse enter (desktop only).
|
|
222
|
+
* Skips on touch devices to avoid double-triggering with touch handlers.
|
|
223
|
+
*/
|
|
224
|
+
function handleMouseEnter() {
|
|
225
|
+
if (isTouchDevice())
|
|
226
|
+
return;
|
|
227
|
+
showTooltip(node);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Hide tooltip on mouse leave, but only if this node owns the tooltip.
|
|
231
|
+
* Prevents one element's mouseleave from dismissing another's tooltip.
|
|
232
|
+
*/
|
|
233
|
+
function handleMouseLeave() {
|
|
234
|
+
if (currentOwner === node) {
|
|
235
|
+
hideTooltip();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// MOBILE: TAP HANDLERS
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
/**
|
|
242
|
+
* Toggle tooltip on tap (mobile only).
|
|
243
|
+
*
|
|
244
|
+
* Prevents default to avoid triggering navigation or text selection.
|
|
245
|
+
* Stops propagation to prevent the document-level tap-outside handler
|
|
246
|
+
* from immediately dismissing the tooltip.
|
|
247
|
+
* Auto-dismisses after 3 seconds.
|
|
248
|
+
*
|
|
249
|
+
* @param e - The touch event.
|
|
250
|
+
*/
|
|
251
|
+
function handleTap(e) {
|
|
252
|
+
if (!isTouchDevice())
|
|
253
|
+
return;
|
|
254
|
+
if (!isTruncated(node))
|
|
255
|
+
return;
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
e.stopPropagation();
|
|
258
|
+
/* Toggle off if already showing for this node */
|
|
259
|
+
if (currentOwner === node) {
|
|
260
|
+
hideTooltip();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
showTooltip(node);
|
|
264
|
+
/* Auto-dismiss after 3 s on mobile */
|
|
265
|
+
hideTimeout = setTimeout(hideTooltip, 3000);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Dismiss the tooltip when the user taps outside the anchor or tooltip
|
|
269
|
+
* (mobile only). Ignores taps on the anchor itself or inside the tooltip
|
|
270
|
+
* to prevent unintended dismissal.
|
|
271
|
+
*
|
|
272
|
+
* @param e - The document-level touch event.
|
|
273
|
+
*/
|
|
274
|
+
function handleTapOutside(e) {
|
|
275
|
+
if (!currentOwner || currentOwner !== node)
|
|
276
|
+
return;
|
|
277
|
+
const target = e.target;
|
|
278
|
+
if (target === node || node.contains(target))
|
|
279
|
+
return;
|
|
280
|
+
if (tooltipEl && (target === tooltipEl || tooltipEl.contains(target)))
|
|
281
|
+
return;
|
|
282
|
+
hideTooltip();
|
|
283
|
+
}
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// EVENT LISTENER REGISTRATION
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
node.addEventListener('mouseenter', handleMouseEnter);
|
|
288
|
+
node.addEventListener('mouseleave', handleMouseLeave);
|
|
289
|
+
node.addEventListener('touchstart', handleTap, { passive: false });
|
|
290
|
+
document.addEventListener('touchstart', handleTapOutside, { passive: true });
|
|
291
|
+
// ---------------------------------------------------------------------------
|
|
292
|
+
// SVELTE ACTION LIFECYCLE
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
return {
|
|
295
|
+
/**
|
|
296
|
+
* Cleanup handler — removes all event listeners and hides the tooltip
|
|
297
|
+
* if this node was the active owner. Prevents memory leaks from
|
|
298
|
+
* orphaned document-level listeners.
|
|
299
|
+
*/
|
|
300
|
+
destroy() {
|
|
301
|
+
node.removeEventListener('mouseenter', handleMouseEnter);
|
|
302
|
+
node.removeEventListener('mouseleave', handleMouseLeave);
|
|
303
|
+
node.removeEventListener('touchstart', handleTap);
|
|
304
|
+
document.removeEventListener('touchstart', handleTapOutside);
|
|
305
|
+
/* Clean up tooltip if this node was the active owner */
|
|
306
|
+
if (currentOwner === node) {
|
|
307
|
+
hideTooltip();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=truncateTooltip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncateTooltip.js","sourceRoot":"","sources":["../../src/actions/truncateTooltip.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/admin.d.ts
CHANGED
|
@@ -1,12 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Admin
|
|
2
|
+
* @fileoverview Admin Privilege Resolution
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Provides a single entry point for determining whether a given Supabase user
|
|
5
|
+
* holds administrative privileges within the application.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - Delegates entirely to the host application's `config.auth.adminCheck` callback.
|
|
9
|
+
* - In single-user mode, every authenticated user is implicitly an admin (there is
|
|
10
|
+
* only one user, so restricting admin access would be meaningless).
|
|
11
|
+
* - If the engine has not been initialized yet (e.g., during early bootstrap),
|
|
12
|
+
* the function silently returns `false` rather than throwing, ensuring safe
|
|
13
|
+
* usage in guards and UI conditionals before full initialization.
|
|
14
|
+
*
|
|
15
|
+
* Security considerations:
|
|
16
|
+
* - This is a **client-side convenience check** only. It must NOT be relied upon
|
|
17
|
+
* as an authorization boundary -- server-side RLS policies and edge-function
|
|
18
|
+
* guards are the true security layer.
|
|
19
|
+
* - The `adminCheck` callback is provided by the host app and can inspect any
|
|
20
|
+
* property on the Supabase `User` object (e.g., `app_metadata.role`).
|
|
21
|
+
*
|
|
22
|
+
* @module auth/admin
|
|
5
23
|
*/
|
|
6
24
|
import type { User } from '@supabase/supabase-js';
|
|
7
25
|
/**
|
|
8
26
|
* Check if a user has admin privileges.
|
|
9
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* Uses the `adminCheck` function from engine config if provided.
|
|
29
|
+
* Falls back to `false` when no check is configured or the engine
|
|
30
|
+
* is not yet initialized.
|
|
31
|
+
*
|
|
32
|
+
* @param user - The Supabase user object to evaluate, or `null` if no user
|
|
33
|
+
* is currently authenticated.
|
|
34
|
+
* @returns `true` if the user is considered an admin, `false` otherwise.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { isAdmin } from 'stellar-engine/auth/admin';
|
|
39
|
+
*
|
|
40
|
+
* const admin = isAdmin(currentUser);
|
|
41
|
+
* if (admin) {
|
|
42
|
+
* showAdminPanel();
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @see {@link getEngineConfig} for how the admin check callback is registered.
|
|
10
47
|
*/
|
|
11
48
|
export declare function isAdmin(user: User | null): boolean;
|
|
12
49
|
//# sourceMappingURL=admin.d.ts.map
|
package/dist/auth/admin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAOlD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAclD"}
|
package/dist/auth/admin.js
CHANGED
|
@@ -1,17 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Admin
|
|
2
|
+
* @fileoverview Admin Privilege Resolution
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Provides a single entry point for determining whether a given Supabase user
|
|
5
|
+
* holds administrative privileges within the application.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - Delegates entirely to the host application's `config.auth.adminCheck` callback.
|
|
9
|
+
* - In single-user mode, every authenticated user is implicitly an admin (there is
|
|
10
|
+
* only one user, so restricting admin access would be meaningless).
|
|
11
|
+
* - If the engine has not been initialized yet (e.g., during early bootstrap),
|
|
12
|
+
* the function silently returns `false` rather than throwing, ensuring safe
|
|
13
|
+
* usage in guards and UI conditionals before full initialization.
|
|
14
|
+
*
|
|
15
|
+
* Security considerations:
|
|
16
|
+
* - This is a **client-side convenience check** only. It must NOT be relied upon
|
|
17
|
+
* as an authorization boundary -- server-side RLS policies and edge-function
|
|
18
|
+
* guards are the true security layer.
|
|
19
|
+
* - The `adminCheck` callback is provided by the host app and can inspect any
|
|
20
|
+
* property on the Supabase `User` object (e.g., `app_metadata.role`).
|
|
21
|
+
*
|
|
22
|
+
* @module auth/admin
|
|
5
23
|
*/
|
|
6
24
|
import { getEngineConfig } from '../config';
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// PUBLIC API
|
|
27
|
+
// =============================================================================
|
|
7
28
|
/**
|
|
8
29
|
* Check if a user has admin privileges.
|
|
9
|
-
*
|
|
30
|
+
*
|
|
31
|
+
* Uses the `adminCheck` function from engine config if provided.
|
|
32
|
+
* Falls back to `false` when no check is configured or the engine
|
|
33
|
+
* is not yet initialized.
|
|
34
|
+
*
|
|
35
|
+
* @param user - The Supabase user object to evaluate, or `null` if no user
|
|
36
|
+
* is currently authenticated.
|
|
37
|
+
* @returns `true` if the user is considered an admin, `false` otherwise.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { isAdmin } from 'stellar-engine/auth/admin';
|
|
42
|
+
*
|
|
43
|
+
* const admin = isAdmin(currentUser);
|
|
44
|
+
* if (admin) {
|
|
45
|
+
* showAdminPanel();
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @see {@link getEngineConfig} for how the admin check callback is registered.
|
|
10
50
|
*/
|
|
11
51
|
export function isAdmin(user) {
|
|
12
52
|
try {
|
|
13
53
|
const config = getEngineConfig();
|
|
14
|
-
|
|
54
|
+
/* Single-user mode: the sole user owns everything, so admin is implicit. */
|
|
15
55
|
if (config.auth?.mode === 'single-user')
|
|
16
56
|
return true;
|
|
17
57
|
if (config.auth?.adminCheck) {
|
|
@@ -19,7 +59,7 @@ export function isAdmin(user) {
|
|
|
19
59
|
}
|
|
20
60
|
}
|
|
21
61
|
catch {
|
|
22
|
-
|
|
62
|
+
/* Engine not initialized yet -- safe to swallow; callers expect a boolean. */
|
|
23
63
|
}
|
|
24
64
|
return false;
|
|
25
65
|
}
|
package/dist/auth/admin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,OAAO,CAAC,IAAiB;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;QAEjC,4EAA4E;QAC5E,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,aAAa;YAAE,OAAO,IAAI,CAAC;QAErD,IAAI,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;IAChF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/auth/crypto.d.ts
CHANGED
|
@@ -1,14 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared
|
|
3
|
-
*
|
|
2
|
+
* @fileoverview Shared Cryptographic Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides deterministic, one-way hashing primitives used throughout the auth
|
|
5
|
+
* subsystem for password storage, gate-code verification, and credential caching.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - Uses the Web Crypto API (`crypto.subtle`) which is available in all modern
|
|
9
|
+
* browsers, Web Workers, and server-side runtimes (Node 15+, Deno, Bun).
|
|
10
|
+
* - Hashing is always SHA-256, producing a 64-character lowercase hex digest.
|
|
11
|
+
* This is NOT a password-hashing algorithm (bcrypt/argon2); it is used only
|
|
12
|
+
* for **local** cache comparisons. The server-side Supabase auth layer uses
|
|
13
|
+
* bcrypt for actual credential storage.
|
|
14
|
+
*
|
|
15
|
+
* Security considerations:
|
|
16
|
+
* - SHA-256 is collision-resistant but NOT suitable for brute-force-resistant
|
|
17
|
+
* password storage on its own. It is acceptable here because the hashed values
|
|
18
|
+
* are only stored in the client-side IndexedDB for offline pre-checking and
|
|
19
|
+
* are never transmitted to a server.
|
|
20
|
+
* - The `isAlreadyHashed` helper uses a regex heuristic (64-char hex). If a
|
|
21
|
+
* user's actual password happens to be a 64-char hex string, it will be
|
|
22
|
+
* misidentified as already hashed. This is an acceptable edge case given the
|
|
23
|
+
* vanishingly low probability and the local-only usage context.
|
|
24
|
+
*
|
|
25
|
+
* @module auth/crypto
|
|
4
26
|
*/
|
|
5
27
|
/**
|
|
6
|
-
* Hash a value using SHA-256 via Web Crypto API.
|
|
7
|
-
*
|
|
28
|
+
* Hash a value using SHA-256 via the Web Crypto API.
|
|
29
|
+
*
|
|
30
|
+
* Encodes the input string as UTF-8, computes the SHA-256 digest, and returns
|
|
31
|
+
* the result as a 64-character lowercase hexadecimal string.
|
|
32
|
+
*
|
|
33
|
+
* @param value - The plaintext string to hash (e.g., a password or gate code).
|
|
34
|
+
* @returns A promise that resolves to a 64-character hex string representing
|
|
35
|
+
* the SHA-256 digest.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const hashed = await hashValue('my-secret-password');
|
|
40
|
+
* // hashed === 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' (example)
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @see {@link isAlreadyHashed} to check whether a string is already a hex digest.
|
|
8
44
|
*/
|
|
9
45
|
export declare function hashValue(value: string): Promise<string>;
|
|
10
46
|
/**
|
|
11
|
-
* Check if a stored value is already hashed (64-
|
|
47
|
+
* Check if a stored value is already hashed (64-character hex string).
|
|
48
|
+
*
|
|
49
|
+
* Used to distinguish between legacy plaintext credentials and modern
|
|
50
|
+
* SHA-256-hashed credentials in IndexedDB, enabling backward-compatible
|
|
51
|
+
* verification without a migration step.
|
|
52
|
+
*
|
|
53
|
+
* @param value - The string to test.
|
|
54
|
+
* @returns `true` if the value matches the pattern of a SHA-256 hex digest
|
|
55
|
+
* (exactly 64 lowercase hexadecimal characters), `false` otherwise.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* isAlreadyHashed('abc123'); // false
|
|
60
|
+
* isAlreadyHashed('e3b0c442...b855'); // true (64-char hex)
|
|
61
|
+
* ```
|
|
12
62
|
*/
|
|
13
63
|
export declare function isAlreadyHashed(value: string): boolean;
|
|
14
64
|
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/auth/crypto.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/auth/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD"}
|
package/dist/auth/crypto.js
CHANGED
|
@@ -1,10 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared
|
|
3
|
-
*
|
|
2
|
+
* @fileoverview Shared Cryptographic Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides deterministic, one-way hashing primitives used throughout the auth
|
|
5
|
+
* subsystem for password storage, gate-code verification, and credential caching.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - Uses the Web Crypto API (`crypto.subtle`) which is available in all modern
|
|
9
|
+
* browsers, Web Workers, and server-side runtimes (Node 15+, Deno, Bun).
|
|
10
|
+
* - Hashing is always SHA-256, producing a 64-character lowercase hex digest.
|
|
11
|
+
* This is NOT a password-hashing algorithm (bcrypt/argon2); it is used only
|
|
12
|
+
* for **local** cache comparisons. The server-side Supabase auth layer uses
|
|
13
|
+
* bcrypt for actual credential storage.
|
|
14
|
+
*
|
|
15
|
+
* Security considerations:
|
|
16
|
+
* - SHA-256 is collision-resistant but NOT suitable for brute-force-resistant
|
|
17
|
+
* password storage on its own. It is acceptable here because the hashed values
|
|
18
|
+
* are only stored in the client-side IndexedDB for offline pre-checking and
|
|
19
|
+
* are never transmitted to a server.
|
|
20
|
+
* - The `isAlreadyHashed` helper uses a regex heuristic (64-char hex). If a
|
|
21
|
+
* user's actual password happens to be a 64-char hex string, it will be
|
|
22
|
+
* misidentified as already hashed. This is an acceptable edge case given the
|
|
23
|
+
* vanishingly low probability and the local-only usage context.
|
|
24
|
+
*
|
|
25
|
+
* @module auth/crypto
|
|
4
26
|
*/
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// PUBLIC API
|
|
29
|
+
// =============================================================================
|
|
5
30
|
/**
|
|
6
|
-
* Hash a value using SHA-256 via Web Crypto API.
|
|
7
|
-
*
|
|
31
|
+
* Hash a value using SHA-256 via the Web Crypto API.
|
|
32
|
+
*
|
|
33
|
+
* Encodes the input string as UTF-8, computes the SHA-256 digest, and returns
|
|
34
|
+
* the result as a 64-character lowercase hexadecimal string.
|
|
35
|
+
*
|
|
36
|
+
* @param value - The plaintext string to hash (e.g., a password or gate code).
|
|
37
|
+
* @returns A promise that resolves to a 64-character hex string representing
|
|
38
|
+
* the SHA-256 digest.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const hashed = await hashValue('my-secret-password');
|
|
43
|
+
* // hashed === 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' (example)
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @see {@link isAlreadyHashed} to check whether a string is already a hex digest.
|
|
8
47
|
*/
|
|
9
48
|
export async function hashValue(value) {
|
|
10
49
|
const encoder = new TextEncoder();
|
|
@@ -14,7 +53,21 @@ export async function hashValue(value) {
|
|
|
14
53
|
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
15
54
|
}
|
|
16
55
|
/**
|
|
17
|
-
* Check if a stored value is already hashed (64-
|
|
56
|
+
* Check if a stored value is already hashed (64-character hex string).
|
|
57
|
+
*
|
|
58
|
+
* Used to distinguish between legacy plaintext credentials and modern
|
|
59
|
+
* SHA-256-hashed credentials in IndexedDB, enabling backward-compatible
|
|
60
|
+
* verification without a migration step.
|
|
61
|
+
*
|
|
62
|
+
* @param value - The string to test.
|
|
63
|
+
* @returns `true` if the value matches the pattern of a SHA-256 hex digest
|
|
64
|
+
* (exactly 64 lowercase hexadecimal characters), `false` otherwise.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* isAlreadyHashed('abc123'); // false
|
|
69
|
+
* isAlreadyHashed('e3b0c442...b855'); // true (64-char hex)
|
|
70
|
+
* ```
|
|
18
71
|
*/
|
|
19
72
|
export function isAlreadyHashed(value) {
|
|
20
73
|
return /^[0-9a-f]{64}$/.test(value);
|
package/dist/auth/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/auth/crypto.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/auth/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAa;IAC3C,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC"}
|