@marktiderman/genesis-native 0.1.0 → 0.3.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/README.md +354 -10
- package/dist/block-C5-noMhB.d.ts +223 -0
- package/dist/blocks-a1YQ1Elf.d.ts +1540 -0
- package/dist/chunk-65P7RU4V.js +49 -0
- package/dist/chunk-65P7RU4V.js.map +1 -0
- package/dist/chunk-6BT4PTGH.js +37 -0
- package/dist/chunk-6BT4PTGH.js.map +1 -0
- package/dist/chunk-75YP72G6.js +374 -0
- package/dist/chunk-75YP72G6.js.map +1 -0
- package/dist/chunk-NGYNYJPL.js +90 -0
- package/dist/chunk-NGYNYJPL.js.map +1 -0
- package/dist/chunk-VKTIHNAV.js +95 -0
- package/dist/chunk-VKTIHNAV.js.map +1 -0
- package/dist/{chunk-MMTPWUWI.js → chunk-VTJDIKFC.js} +9 -6
- package/dist/chunk-VTJDIKFC.js.map +1 -0
- package/dist/chunk-XE7LC7K6.js +29 -0
- package/dist/chunk-XE7LC7K6.js.map +1 -0
- package/dist/dev-warnings.d.ts +34 -0
- package/dist/dev-warnings.js +15 -0
- package/dist/dev-warnings.js.map +1 -0
- package/dist/devtools.d.ts +144 -0
- package/dist/devtools.js +390 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.d.ts +12 -368
- package/dist/index.js +88 -191
- package/dist/index.js.map +1 -1
- package/dist/observability.d.ts +170 -0
- package/dist/observability.js +123 -0
- package/dist/observability.js.map +1 -0
- package/dist/persist.d.ts +1 -1
- package/dist/persist.js +1 -1
- package/dist/push.d.ts +169 -0
- package/dist/push.js +996 -0
- package/dist/push.js.map +1 -0
- package/dist/{query-DhH3lLFa.d.ts → query-DUmVAfsF.d.ts} +11 -6
- package/dist/updates.d.ts +8 -1
- package/dist/updates.js +44 -69
- package/dist/updates.js.map +1 -1
- package/package.json +222 -5
- package/src/app.tsx +491 -0
- package/src/auth.tsx +176 -0
- package/src/blocks.tsx +144 -0
- package/src/boot.tsx +188 -0
- package/src/dev-warnings.ts +43 -0
- package/src/devtools/block.tsx +488 -0
- package/src/devtools/gestures.ts +107 -0
- package/src/devtools/index.ts +22 -0
- package/src/devtools/info.ts +45 -0
- package/src/devtools/slots.ts +21 -0
- package/src/errors.tsx +195 -0
- package/src/held-link.ts +90 -0
- package/src/hydration-gate.ts +88 -0
- package/src/index.ts +83 -0
- package/src/links.ts +140 -0
- package/src/nativewind.d.ts +41 -0
- package/src/observability/block.tsx +300 -0
- package/src/observability/events.ts +89 -0
- package/src/observability/index.ts +44 -0
- package/src/observability/scrub.ts +166 -0
- package/src/observability/sentry.ts +222 -0
- package/src/observability/sink.ts +147 -0
- package/src/persist.ts +83 -0
- package/src/push/backend.ts +129 -0
- package/src/push/block.tsx +545 -0
- package/src/push/index.ts +102 -0
- package/src/push/modules.ts +111 -0
- package/src/push/permission.ts +191 -0
- package/src/push/preferences.ts +136 -0
- package/src/push/quiet-hours.ts +71 -0
- package/src/push/registration.ts +189 -0
- package/src/push/routing.ts +27 -0
- package/src/push/screens.tsx +46 -0
- package/src/push/settings.tsx +378 -0
- package/src/query.ts +100 -0
- package/src/updates/block.tsx +442 -0
- package/src/updates/channels.ts +131 -0
- package/src/updates/checker.ts +190 -0
- package/src/updates/index.ts +87 -0
- package/src/updates/modules.ts +64 -0
- package/src/updates/policy.ts +314 -0
- package/src/updates/screens.tsx +159 -0
- package/src/updates/signature.ts +80 -0
- package/src/updates/store.ts +33 -0
- package/src/updates/version.ts +39 -0
- package/src/updates/whats-new.ts +122 -0
- package/dist/block-Dh5ny06W.d.ts +0 -535
- package/dist/chunk-MMTPWUWI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import { createNativeQueryClient, connectQueryLifecycle } from './chunk-
|
|
2
|
-
export { DAY_MS, connectQueryLifecycle, createNativeQueryClient } from './chunk-
|
|
1
|
+
import { createNativeQueryClient, connectQueryLifecycle } from './chunk-VTJDIKFC.js';
|
|
2
|
+
export { DAY_MS, connectQueryLifecycle, createNativeQueryClient } from './chunk-VTJDIKFC.js';
|
|
3
|
+
export { normalizeLink } from './chunk-VKTIHNAV.js';
|
|
4
|
+
import { reportToBlocks, BlocksProvider } from './chunk-65P7RU4V.js';
|
|
5
|
+
export { mergeBlockSettings, useBlockSettings, useBlocks } from './chunk-65P7RU4V.js';
|
|
6
|
+
import { createSessionStore, SessionProvider } from './chunk-NGYNYJPL.js';
|
|
7
|
+
export { AuthGate, SessionProvider, createSessionStore, useSession } from './chunk-NGYNYJPL.js';
|
|
3
8
|
import { createContext, useContext, useSyncExternalStore, Component, useRef, useEffect, useState } from 'react';
|
|
4
9
|
import { View, Text, Pressable } from 'react-native';
|
|
5
10
|
import { GenesisThemeProvider } from '@marktiderman/genesis-design-system/theme';
|
|
6
11
|
import { createSupabaseProvider, GenesisProvider } from '@marktiderman/genesis-core/provider';
|
|
7
12
|
import { renderSlot } from '@marktiderman/genesis-core/slots';
|
|
8
13
|
import { KeyboardScrollProvider } from '@marktiderman/genesis-ui-native/layouts';
|
|
14
|
+
import { NativeEmptyState, NativeButton, NativeToastProvider, GenesisModulesProvider, NativeSheetProvider } from '@marktiderman/genesis-ui-native/components';
|
|
9
15
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
|
-
import {
|
|
16
|
+
import { resolveBlocks } from '@marktiderman/genesis-features/blocks';
|
|
11
17
|
|
|
12
18
|
var BootTimeoutError = class extends Error {
|
|
13
19
|
step;
|
|
@@ -194,88 +200,6 @@ function installRejectionTracker({ enable, onError }) {
|
|
|
194
200
|
}
|
|
195
201
|
});
|
|
196
202
|
}
|
|
197
|
-
var DEFAULT_WAIT_MS = 5e3;
|
|
198
|
-
function defaultUserId(session) {
|
|
199
|
-
const id = session?.user?.id;
|
|
200
|
-
return typeof id === "string" ? id : null;
|
|
201
|
-
}
|
|
202
|
-
function isNetworkError(error) {
|
|
203
|
-
const e = error;
|
|
204
|
-
return e?.name === "AuthRetryableFetchError" || e?.status === 0;
|
|
205
|
-
}
|
|
206
|
-
function storedSessionReader(client) {
|
|
207
|
-
return async () => {
|
|
208
|
-
const auth = client.auth;
|
|
209
|
-
if (!auth.storage || typeof auth.storageKey !== "string") return null;
|
|
210
|
-
const raw = await auth.storage.getItem(auth.storageKey);
|
|
211
|
-
if (typeof raw !== "string") return raw ?? null;
|
|
212
|
-
try {
|
|
213
|
-
return JSON.parse(raw);
|
|
214
|
-
} catch {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
function createSessionStore(client, options = {}) {
|
|
220
|
-
const { onUserChange, waitMs = DEFAULT_WAIT_MS, userIdOf = defaultUserId } = options;
|
|
221
|
-
const readStored = options.readStoredSession ?? storedSessionReader(client);
|
|
222
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
223
|
-
let state = { status: "loading", session: null, userId: null, offline: false };
|
|
224
|
-
let version = 0;
|
|
225
|
-
const set = (session, offline = false) => {
|
|
226
|
-
const userId = session ? userIdOf(session) : null;
|
|
227
|
-
const previous = state;
|
|
228
|
-
if (previous.status !== "loading" && previous.session === session && previous.offline === offline) return;
|
|
229
|
-
state = { status: session ? "signedIn" : "signedOut", session, userId, offline };
|
|
230
|
-
for (const listener of listeners) listener();
|
|
231
|
-
if (previous.status !== "loading" && previous.userId !== userId) onUserChange?.(previous.userId, userId);
|
|
232
|
-
};
|
|
233
|
-
return {
|
|
234
|
-
getState: () => state,
|
|
235
|
-
subscribe(listener) {
|
|
236
|
-
listeners.add(listener);
|
|
237
|
-
return () => listeners.delete(listener);
|
|
238
|
-
},
|
|
239
|
-
async load() {
|
|
240
|
-
const startedAt = version;
|
|
241
|
-
let timer;
|
|
242
|
-
const timedOut = new Promise((resolve) => {
|
|
243
|
-
timer = setTimeout(() => resolve("timeout"), waitMs);
|
|
244
|
-
});
|
|
245
|
-
const result = await Promise.race([client.auth.getSession(), timedOut]).finally(() => clearTimeout(timer));
|
|
246
|
-
if (version !== startedAt) return;
|
|
247
|
-
if (result !== "timeout" && !result.error) return set(result.data.session);
|
|
248
|
-
if (result !== "timeout" && !isNetworkError(result.error)) {
|
|
249
|
-
const error = result.error;
|
|
250
|
-
throw error instanceof Error ? error : new Error("Could not read the session.", { cause: error });
|
|
251
|
-
}
|
|
252
|
-
const stored = await readStored().catch(() => null);
|
|
253
|
-
if (version !== startedAt) return;
|
|
254
|
-
set(stored, stored !== null);
|
|
255
|
-
},
|
|
256
|
-
start() {
|
|
257
|
-
const { data } = client.auth.onAuthStateChange((_event, session) => {
|
|
258
|
-
version++;
|
|
259
|
-
set(session);
|
|
260
|
-
});
|
|
261
|
-
return () => data.subscription.unsubscribe();
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
var SessionContext = createContext(null);
|
|
266
|
-
function SessionProvider({ store, children }) {
|
|
267
|
-
return /* @__PURE__ */ jsx(SessionContext.Provider, { value: store, children });
|
|
268
|
-
}
|
|
269
|
-
function useSession() {
|
|
270
|
-
const store = useContext(SessionContext);
|
|
271
|
-
if (!store) throw new Error("useSession must be used inside <GenesisNativeApp> or <SessionProvider>.");
|
|
272
|
-
return useSyncExternalStore(store.subscribe, store.getState, store.getState);
|
|
273
|
-
}
|
|
274
|
-
function AuthGate({ signedIn, signedOut, loading = null }) {
|
|
275
|
-
const { status } = useSession();
|
|
276
|
-
if (status === "loading") return /* @__PURE__ */ jsx(Fragment, { children: loading });
|
|
277
|
-
return /* @__PURE__ */ jsx(Fragment, { children: status === "signedIn" ? signedIn : signedOut });
|
|
278
|
-
}
|
|
279
203
|
function createLinkHold() {
|
|
280
204
|
let held = null;
|
|
281
205
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -327,31 +251,71 @@ function GenesisNativeApp(props) {
|
|
|
327
251
|
const [rootKey, setRootKey] = useState(0);
|
|
328
252
|
const onErrorRef = useRef(props.onError);
|
|
329
253
|
onErrorRef.current = props.onError;
|
|
254
|
+
const blocksRef = useRef(givenBlocks(props));
|
|
255
|
+
const splashRef = useRef(props.splash);
|
|
256
|
+
splashRef.current = props.splash;
|
|
330
257
|
return /* @__PURE__ */ jsx(
|
|
331
258
|
GenesisErrorBoundary,
|
|
332
259
|
{
|
|
333
260
|
fallback: PlainFatalScreen,
|
|
334
|
-
onError: (e, r) =>
|
|
261
|
+
onError: (e, r) => {
|
|
262
|
+
reportToBlocks(blocksRef.current, e, r);
|
|
263
|
+
try {
|
|
264
|
+
onErrorRef.current?.(e, r);
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
splashRef.current?.hideAsync().catch(() => {
|
|
268
|
+
});
|
|
269
|
+
},
|
|
335
270
|
onRetry: () => setRootKey((key) => key + 1),
|
|
336
271
|
children: /* @__PURE__ */ jsx(ThemedShell, { ...props })
|
|
337
272
|
},
|
|
338
273
|
rootKey
|
|
339
274
|
);
|
|
340
275
|
}
|
|
276
|
+
var MISSING_KEYBOARD_PROVIDER = 'GenesisNativeApp: `keyboardScroll` was given without `keyboardProvider`, so form screens won\'t move above the keyboard. Pass `keyboardProvider={KeyboardProvider}` from "react-native-keyboard-controller" too.';
|
|
277
|
+
var TOOLBAR_WITHOUT_SCROLL = 'GenesisNativeApp: `keyboardToolbar` was given without `keyboardScroll`, so it is ignored. Pass `keyboardScroll={KeyboardAwareScrollView}` and `keyboardProvider={KeyboardProvider}` from "react-native-keyboard-controller" too.';
|
|
341
278
|
function ThemedShell(props) {
|
|
342
|
-
const { brand, keyboardScroll } = props;
|
|
343
|
-
const
|
|
344
|
-
|
|
279
|
+
const { brand, keyboardScroll, keyboardToolbar } = props;
|
|
280
|
+
const [Keyboard] = useState(() => props.keyboardProvider);
|
|
281
|
+
const [modules] = useState(() => props.modules);
|
|
282
|
+
const [toast] = useState(() => props.toast === null ? null : { position: "top", ...props.toast });
|
|
283
|
+
const missingProvider = Boolean(keyboardScroll) && !Keyboard;
|
|
284
|
+
const toolbarWithoutScroll = Boolean(keyboardToolbar) && !keyboardScroll;
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
287
|
+
if (missingProvider) console.error(MISSING_KEYBOARD_PROVIDER);
|
|
288
|
+
if (toolbarWithoutScroll) console.error(TOOLBAR_WITHOUT_SCROLL);
|
|
289
|
+
}, [missingProvider, toolbarWithoutScroll]);
|
|
290
|
+
let body = /* @__PURE__ */ jsx(Shell, { ...props });
|
|
291
|
+
if (toast) body = /* @__PURE__ */ jsx(NativeToastProvider, { ...toast, children: body });
|
|
292
|
+
if (modules) body = /* @__PURE__ */ jsx(GenesisModulesProvider, { modules, children: body });
|
|
293
|
+
if (keyboardScroll) body = /* @__PURE__ */ jsx(KeyboardScrollProvider, { component: keyboardScroll, toolbar: keyboardToolbar, children: body });
|
|
294
|
+
if (Keyboard) body = /* @__PURE__ */ jsx(Keyboard, { children: body });
|
|
295
|
+
return /* @__PURE__ */ jsx(ThemeProvider, { brand, syncNativeAppearance: true, children: body });
|
|
345
296
|
}
|
|
346
297
|
var SESSION_STEP_MARGIN_MS = 15e3;
|
|
347
298
|
function Shell(props) {
|
|
348
299
|
const { supabase, provider, queryClient: queryOptions, persist, splash, links, steps = [], bootOptions, sessionWaitMs = 5e3 } = props;
|
|
349
|
-
const { errorUtils, rejectionTracker, lifecycle,
|
|
300
|
+
const { errorUtils, rejectionTracker, lifecycle, slotProps, children } = props;
|
|
350
301
|
const latest = useRef(props);
|
|
351
302
|
latest.current = props;
|
|
352
|
-
const
|
|
303
|
+
const [blockList] = useState(() => givenBlocks(props));
|
|
304
|
+
const [bottomSheet] = useState(() => props.bottomSheet);
|
|
305
|
+
const onError = (error, report) => {
|
|
306
|
+
reportToBlocks(blockList, error, report);
|
|
307
|
+
try {
|
|
308
|
+
latest.current.onError?.(error, report);
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
};
|
|
353
312
|
const fatalShown = useRef(0);
|
|
354
313
|
const [parts] = useState(() => {
|
|
314
|
+
const list = blockList;
|
|
315
|
+
const blocks = resolveBlocks(list, props.appFeatures ? [...list.map((b) => b.id), ...props.appFeatures] : void 0, {
|
|
316
|
+
platform: "native",
|
|
317
|
+
slots: NATIVE_APP_SLOT_NAMES
|
|
318
|
+
});
|
|
355
319
|
const client2 = queryOptions?.client ?? createNativeQueryClient(queryOptions?.config);
|
|
356
320
|
const persistence2 = persist ? persist(client2) : null;
|
|
357
321
|
const session2 = createSessionStore(supabase, {
|
|
@@ -366,8 +330,8 @@ function Shell(props) {
|
|
|
366
330
|
const boot2 = createBoot(
|
|
367
331
|
[
|
|
368
332
|
...steps,
|
|
369
|
-
//
|
|
370
|
-
...
|
|
333
|
+
// Blocks' steps, in dependency order (the updates block's settles within its own timeout and never throws).
|
|
334
|
+
...blocks.blocks.flatMap((block) => block.bootSteps ?? []),
|
|
371
335
|
{ id: "genesis:session", run: () => session2.load(), timeoutMs: sessionWaitMs + SESSION_STEP_MARGIN_MS },
|
|
372
336
|
// After the session, so only the cache saved for this user is restored.
|
|
373
337
|
...persistence2 ? [{ id: "genesis:query-cache", run: () => persistence2.restore(session2.getState().userId ?? "") }] : []
|
|
@@ -381,15 +345,17 @@ function Shell(props) {
|
|
|
381
345
|
fatalShown.current--;
|
|
382
346
|
};
|
|
383
347
|
}, []);
|
|
384
|
-
const {
|
|
348
|
+
const { slotProps: sp } = latest.current;
|
|
349
|
+
const s = withBlockSlots(blocks.slots, latest.current.slots);
|
|
385
350
|
return (
|
|
386
351
|
// A `fatal` slot that throws falls back to the plain screen.
|
|
387
352
|
/* @__PURE__ */ jsx(GenesisErrorBoundary, { fallback: PlainFatalScreen, onError, onRetry: fallbackProps.retry, children: renderSlot(FatalScreen, s?.fatal, fallbackProps, sp?.fatal) })
|
|
388
353
|
);
|
|
389
354
|
}
|
|
390
|
-
return { client: client2, persistence: persistence2, session: session2, boot: boot2, FatalFallback: FatalFallback2,
|
|
355
|
+
return { client: client2, persistence: persistence2, session: session2, boot: boot2, FatalFallback: FatalFallback2, blocks, provider: provider ?? createSupabaseProvider(supabase) };
|
|
391
356
|
});
|
|
392
357
|
const { boot, session, persistence, client, FatalFallback } = parts;
|
|
358
|
+
const slots = withBlockSlots(parts.blocks.slots, props.slots);
|
|
393
359
|
const bootState = useSyncExternalStore(boot.subscribe, boot.getState, boot.getState);
|
|
394
360
|
const sessionState = useSyncExternalStore(session.subscribe, session.getState, session.getState);
|
|
395
361
|
const [fatal, setFatal] = useState(null);
|
|
@@ -444,10 +410,33 @@ function Shell(props) {
|
|
|
444
410
|
{ fallback: FatalFallback, onError, resetKey, onRetry: retry, children: screens },
|
|
445
411
|
slotProps?.errorBoundary
|
|
446
412
|
);
|
|
447
|
-
|
|
448
|
-
|
|
413
|
+
content = parts.blocks.blocks.reduceRight((inner, block) => {
|
|
414
|
+
const Root = block.Root;
|
|
415
|
+
return Root ? /* @__PURE__ */ jsx(Root, { slots, slotProps, children: inner }, block.id) : inner;
|
|
416
|
+
}, guarded);
|
|
449
417
|
}
|
|
450
|
-
return /* @__PURE__ */ jsx(GenesisProvider, { provider: parts.provider, queryClient: client, children: /* @__PURE__ */ jsx(SessionProvider, { store: session, children: /* @__PURE__ */ jsx(BootProvider, { boot, children: /* @__PURE__ */ jsx(GenesisErrorBoundary, { fallback: FatalFallback, onError, onRetry: retry, children: content }, resetKey) }) }) });
|
|
418
|
+
return /* @__PURE__ */ jsx(GenesisProvider, { provider: parts.provider, queryClient: client, children: /* @__PURE__ */ jsx(SessionProvider, { store: session, children: /* @__PURE__ */ jsx(BootProvider, { boot, children: /* @__PURE__ */ jsx(BlocksProvider, { value: parts.blocks, children: /* @__PURE__ */ jsx(GenesisErrorBoundary, { fallback: FatalFallback, onError, onRetry: retry, children: bottomSheet ? /* @__PURE__ */ jsx(NativeSheetProvider, { bottomSheet, children: content }) : content }, resetKey) }) }) }) });
|
|
419
|
+
}
|
|
420
|
+
var SLOT_NAMES = {
|
|
421
|
+
boot: true,
|
|
422
|
+
fatal: true,
|
|
423
|
+
errorBoundary: true,
|
|
424
|
+
signedOut: true,
|
|
425
|
+
forceUpgrade: true,
|
|
426
|
+
maintenance: true,
|
|
427
|
+
updateBanner: true,
|
|
428
|
+
whatsNew: true,
|
|
429
|
+
devtoolsFeedback: true,
|
|
430
|
+
pushPrompt: true
|
|
431
|
+
};
|
|
432
|
+
var NATIVE_APP_SLOT_NAMES = Object.keys(SLOT_NAMES);
|
|
433
|
+
function withBlockSlots(fills, own) {
|
|
434
|
+
if (Object.keys(fills).length === 0) return own;
|
|
435
|
+
return { ...fills, ...own };
|
|
436
|
+
}
|
|
437
|
+
function givenBlocks(props) {
|
|
438
|
+
const given = props.blocks ?? [];
|
|
439
|
+
return props.updates && !given.includes(props.updates) ? [...given, props.updates] : given;
|
|
451
440
|
}
|
|
452
441
|
var NO_HOLD = {
|
|
453
442
|
hold: () => {
|
|
@@ -503,98 +492,6 @@ function createHydrationGate({ onDroppedWrite } = {}) {
|
|
|
503
492
|
return gate;
|
|
504
493
|
}
|
|
505
494
|
|
|
506
|
-
|
|
507
|
-
var URL_PATTERN = /^([a-z][a-z0-9+.-]*):\/\/([^/?#]*)([^?#]*)(\?[^#]*)?(#.*)?$/i;
|
|
508
|
-
function parse(url, schemes, hosts) {
|
|
509
|
-
const trimmed = url.trim();
|
|
510
|
-
const match = URL_PATTERN.exec(trimmed);
|
|
511
|
-
if (!match) {
|
|
512
|
-
if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) return null;
|
|
513
|
-
const noHash = trimmed.split("#")[0] ?? "";
|
|
514
|
-
const at = noHash.indexOf("?");
|
|
515
|
-
return at < 0 ? { path: noHash, query: "" } : { path: noHash.slice(0, at), query: noHash.slice(at) };
|
|
516
|
-
}
|
|
517
|
-
const [, scheme = "", host = "", path = "", query = ""] = match;
|
|
518
|
-
const lowerScheme = scheme.toLowerCase();
|
|
519
|
-
if (lowerScheme === "http" || lowerScheme === "https") {
|
|
520
|
-
const bareHost = host.toLowerCase().replace(/:\d+$/, "");
|
|
521
|
-
if (!hosts.some((h) => h.toLowerCase() === bareHost)) return null;
|
|
522
|
-
return { path, query };
|
|
523
|
-
}
|
|
524
|
-
if (!schemes.some((s) => s.toLowerCase() === lowerScheme)) return null;
|
|
525
|
-
return { path: `/${host}${path}`, query };
|
|
526
|
-
}
|
|
527
|
-
function clean(path) {
|
|
528
|
-
const joined = `/${path}`.replace(/\/{2,}/g, "/");
|
|
529
|
-
return joined.length > 1 ? joined.replace(/\/$/, "") : joined;
|
|
530
|
-
}
|
|
531
|
-
function matchPattern(pattern, path) {
|
|
532
|
-
const want = clean(pattern).split("/").slice(1);
|
|
533
|
-
const have = path.split("/").slice(1);
|
|
534
|
-
const params = {};
|
|
535
|
-
for (let i = 0; i < want.length; i++) {
|
|
536
|
-
const segment = want[i];
|
|
537
|
-
if (segment === "*" && i === want.length - 1) {
|
|
538
|
-
params["*"] = have.slice(i).join("/");
|
|
539
|
-
return params;
|
|
540
|
-
}
|
|
541
|
-
const value = have[i];
|
|
542
|
-
if (value === void 0) return null;
|
|
543
|
-
if (segment.startsWith(":")) params[segment.slice(1)] = value;
|
|
544
|
-
else if (segment !== value) return null;
|
|
545
|
-
}
|
|
546
|
-
return want.length === have.length ? params : null;
|
|
547
|
-
}
|
|
548
|
-
function fill(target, params) {
|
|
549
|
-
if (typeof target === "function") return target(params);
|
|
550
|
-
return target.replace(
|
|
551
|
-
/:([A-Za-z_][A-Za-z0-9_]*)|\*$/g,
|
|
552
|
-
(token, name) => name === void 0 ? params["*"] ?? "" : params[name] ?? token
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
function isDotSegment(segment) {
|
|
556
|
-
let decoded = segment;
|
|
557
|
-
try {
|
|
558
|
-
decoded = decodeURIComponent(segment);
|
|
559
|
-
} catch {
|
|
560
|
-
}
|
|
561
|
-
return decoded === "." || decoded === "..";
|
|
562
|
-
}
|
|
563
|
-
function withQuery(path, query) {
|
|
564
|
-
if (!query || query === "?") return path;
|
|
565
|
-
return path.includes("?") ? `${path}&${query.slice(1)}` : `${path}${query}`;
|
|
566
|
-
}
|
|
567
|
-
function normalizeLink(url, options) {
|
|
568
|
-
const { schemes = [], hosts = [], map, ignore = [], onUnknown } = options;
|
|
569
|
-
const unknown = () => {
|
|
570
|
-
onUnknown?.(url);
|
|
571
|
-
return "/";
|
|
572
|
-
};
|
|
573
|
-
let parsed;
|
|
574
|
-
try {
|
|
575
|
-
parsed = parse(url, schemes, hosts);
|
|
576
|
-
} catch {
|
|
577
|
-
parsed = null;
|
|
578
|
-
}
|
|
579
|
-
if (!parsed) return unknown();
|
|
580
|
-
const path = clean(parsed.path);
|
|
581
|
-
if (path.split("/").some(isDotSegment)) {
|
|
582
|
-
return unknown();
|
|
583
|
-
}
|
|
584
|
-
if (ignore.some((pattern) => matchPattern(pattern, path))) return null;
|
|
585
|
-
if (path === "/") return withQuery("/", parsed.query);
|
|
586
|
-
for (const [pattern, target] of Object.entries(map)) {
|
|
587
|
-
const params = matchPattern(pattern, path);
|
|
588
|
-
if (!params) continue;
|
|
589
|
-
try {
|
|
590
|
-
return withQuery(fill(target, params), parsed.query);
|
|
591
|
-
} catch {
|
|
592
|
-
return unknown();
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
return unknown();
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
export { AuthGate, BootProvider, BootScreen, BootTimeoutError, FatalScreen, GenesisErrorBoundary, GenesisNativeApp, PlainFatalScreen, SessionProvider, createBoot, createHydrationGate, createLinkHold, createSessionStore, defaultErrorUtils, defaultRejectionTracker, installGlobalErrorHandler, installRejectionTracker, normalizeLink, useBoot, useHeldLink, useSession };
|
|
495
|
+
export { BootProvider, BootScreen, BootTimeoutError, FatalScreen, GenesisErrorBoundary, GenesisNativeApp, PlainFatalScreen, createBoot, createHydrationGate, createLinkHold, defaultErrorUtils, defaultRejectionTracker, installGlobalErrorHandler, installRejectionTracker, useBoot, useHeldLink };
|
|
599
496
|
//# sourceMappingURL=index.js.map
|
|
600
497
|
//# sourceMappingURL=index.js.map
|