@ai-matrx/media 0.8.0 → 0.9.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/CHANGELOG.md +15 -0
- package/dist/files/engine/api/assets.d.ts +223 -0
- package/dist/files/engine/api/assets.js +124 -0
- package/dist/files/engine/api/assets.js.map +1 -0
- package/dist/files/engine/api/direct.d.ts +43 -0
- package/dist/files/engine/api/direct.js +73 -0
- package/dist/files/engine/api/direct.js.map +1 -0
- package/dist/files/engine/api/fileOrganization.d.ts +34 -0
- package/dist/files/engine/api/fileOrganization.js +38 -0
- package/dist/files/engine/api/fileOrganization.js.map +1 -0
- package/dist/files/engine/api/files.d.ts +249 -0
- package/dist/files/engine/api/files.js +231 -0
- package/dist/files/engine/api/files.js.map +1 -0
- package/dist/files/engine/api/folders.d.ts +63 -0
- package/dist/files/engine/api/folders.js +56 -0
- package/dist/files/engine/api/folders.js.map +1 -0
- package/dist/files/engine/api/office.d.ts +27 -0
- package/dist/files/engine/api/office.js +19 -0
- package/dist/files/engine/api/office.js.map +1 -0
- package/dist/files/engine/api/permissions.d.ts +37 -0
- package/dist/files/engine/api/permissions.js +109 -0
- package/dist/files/engine/api/permissions.js.map +1 -0
- package/dist/files/engine/api/versions.d.ts +26 -0
- package/dist/files/engine/api/versions.js +37 -0
- package/dist/files/engine/api/versions.js.map +1 -0
- package/dist/files/engine/cache/idb-store.d.ts +95 -0
- package/dist/files/engine/cache/idb-store.js +172 -0
- package/dist/files/engine/cache/idb-store.js.map +1 -0
- package/dist/files/engine/cache/policy.d.ts +14 -0
- package/dist/files/engine/cache/policy.js +40 -0
- package/dist/files/engine/cache/policy.js.map +1 -0
- package/dist/files/engine/cache/register-service-worker.d.ts +57 -0
- package/dist/files/engine/cache/register-service-worker.js +80 -0
- package/dist/files/engine/cache/register-service-worker.js.map +1 -0
- package/dist/files/engine/db-types.d.ts +48865 -0
- package/dist/files/engine/db-types.js +1 -0
- package/dist/files/engine/db-types.js.map +1 -0
- package/dist/files/engine/filesDb.d.ts +1913 -0
- package/dist/files/engine/filesDb.js +28 -0
- package/dist/files/engine/filesDb.js.map +1 -0
- package/dist/files/engine/handler/handler.d.ts +82 -0
- package/dist/files/engine/handler/handler.js +124 -0
- package/dist/files/engine/handler/handler.js.map +1 -0
- package/dist/files/engine/handler/hooks/useFile.d.ts +17 -0
- package/dist/files/engine/handler/hooks/useFile.js +75 -0
- package/dist/files/engine/handler/hooks/useFile.js.map +1 -0
- package/dist/files/engine/handler/hooks/useFileUpload.d.ts +76 -0
- package/dist/files/engine/handler/hooks/useFileUpload.js +67 -0
- package/dist/files/engine/handler/hooks/useFileUpload.js.map +1 -0
- package/dist/files/engine/handler/input/normalize.d.ts +14 -0
- package/dist/files/engine/handler/input/normalize.js +365 -0
- package/dist/files/engine/handler/input/normalize.js.map +1 -0
- package/dist/files/engine/handler/intelligence/access.d.ts +35 -0
- package/dist/files/engine/handler/intelligence/access.js +85 -0
- package/dist/files/engine/handler/intelligence/access.js.map +1 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.d.ts +21 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.js +67 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.js.map +1 -0
- package/dist/files/engine/handler/output/target.d.ts +19 -0
- package/dist/files/engine/handler/output/target.js +222 -0
- package/dist/files/engine/handler/output/target.js.map +1 -0
- package/dist/files/engine/handler/resolver.d.ts +34 -0
- package/dist/files/engine/handler/resolver.js +133 -0
- package/dist/files/engine/handler/resolver.js.map +1 -0
- package/dist/files/engine/handler/types.d.ts +367 -0
- package/dist/files/engine/handler/types.js +1 -0
- package/dist/files/engine/handler/types.js.map +1 -0
- package/dist/files/engine/handler/upload.d.ts +32 -0
- package/dist/files/engine/handler/upload.js +295 -0
- package/dist/files/engine/handler/upload.js.map +1 -0
- package/dist/files/engine/handler/utils/classify.d.ts +14 -0
- package/dist/files/engine/handler/utils/classify.js +27 -0
- package/dist/files/engine/handler/utils/classify.js.map +1 -0
- package/dist/files/engine/handler/utils/prefer-locator.d.ts +32 -0
- package/dist/files/engine/handler/utils/prefer-locator.js +34 -0
- package/dist/files/engine/handler/utils/prefer-locator.js.map +1 -0
- package/dist/files/engine/handler/utils/python-base.d.ts +129 -0
- package/dist/files/engine/handler/utils/python-base.js +60 -0
- package/dist/files/engine/handler/utils/python-base.js.map +1 -0
- package/dist/files/engine/hooks/blob-cache.d.ts +130 -0
- package/dist/files/engine/hooks/blob-cache.js +162 -0
- package/dist/files/engine/hooks/blob-cache.js.map +1 -0
- package/dist/files/engine/hooks/office-extraction-cache.d.ts +28 -0
- package/dist/files/engine/hooks/office-extraction-cache.js +67 -0
- package/dist/files/engine/hooks/office-extraction-cache.js.map +1 -0
- package/dist/files/engine/host/configure.d.ts +166 -0
- package/dist/files/engine/host/configure.js +30 -0
- package/dist/files/engine/host/configure.js.map +1 -0
- package/dist/files/engine/host/org.d.ts +8 -0
- package/dist/files/engine/host/org.js +16 -0
- package/dist/files/engine/host/org.js.map +1 -0
- package/dist/files/engine/host/python-client.d.ts +60 -0
- package/dist/files/engine/host/python-client.js +46 -0
- package/dist/files/engine/host/python-client.js.map +1 -0
- package/dist/files/engine/host/share-links.d.ts +20 -0
- package/dist/files/engine/host/share-links.js +32 -0
- package/dist/files/engine/host/share-links.js.map +1 -0
- package/dist/files/engine/host/store.d.ts +24 -0
- package/dist/files/engine/host/store.js +23 -0
- package/dist/files/engine/host/store.js.map +1 -0
- package/dist/files/engine/host/supabase.d.ts +3808 -0
- package/dist/files/engine/host/supabase.js +33 -0
- package/dist/files/engine/host/supabase.js.map +1 -0
- package/dist/files/engine/host/toast.d.ts +14 -0
- package/dist/files/engine/host/toast.js +20 -0
- package/dist/files/engine/host/toast.js.map +1 -0
- package/dist/files/engine/host/typed-client.d.ts +136 -0
- package/dist/files/engine/host/typed-client.js +66 -0
- package/dist/files/engine/host/typed-client.js.map +1 -0
- package/dist/files/engine/index.d.ts +22 -0
- package/dist/files/engine/index.js +49 -0
- package/dist/files/engine/index.js.map +1 -0
- package/dist/files/engine/media/our-file-sources.d.ts +39 -0
- package/dist/files/engine/media/our-file-sources.js +83 -0
- package/dist/files/engine/media/our-file-sources.js.map +1 -0
- package/dist/files/engine/media/signed-url.d.ts +1 -0
- package/dist/files/engine/media/signed-url.js +6 -0
- package/dist/files/engine/media/signed-url.js.map +1 -0
- package/dist/files/engine/redux/converters.d.ts +73 -0
- package/dist/files/engine/redux/converters.js +286 -0
- package/dist/files/engine/redux/converters.js.map +1 -0
- package/dist/files/engine/redux/file-hydration.d.ts +15 -0
- package/dist/files/engine/redux/file-hydration.js +46 -0
- package/dist/files/engine/redux/file-hydration.js.map +1 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.d.ts +5 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.js +28 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.js.map +1 -0
- package/dist/files/engine/redux/file-tree-timeout.d.ts +32 -0
- package/dist/files/engine/redux/file-tree-timeout.js +73 -0
- package/dist/files/engine/redux/file-tree-timeout.js.map +1 -0
- package/dist/files/engine/redux/mutation-toast-middleware.d.ts +27 -0
- package/dist/files/engine/redux/mutation-toast-middleware.js +103 -0
- package/dist/files/engine/redux/mutation-toast-middleware.js.map +1 -0
- package/dist/files/engine/redux/realtime-middleware.d.ts +61 -0
- package/dist/files/engine/redux/realtime-middleware.js +431 -0
- package/dist/files/engine/redux/realtime-middleware.js.map +1 -0
- package/dist/files/engine/redux/request-ledger.d.ts +68 -0
- package/dist/files/engine/redux/request-ledger.js +66 -0
- package/dist/files/engine/redux/request-ledger.js.map +1 -0
- package/dist/files/engine/redux/selectors.d.ts +3254 -0
- package/dist/files/engine/redux/selectors.js +428 -0
- package/dist/files/engine/redux/selectors.js.map +1 -0
- package/dist/files/engine/redux/slice.d.ts +157 -0
- package/dist/files/engine/redux/slice.js +836 -0
- package/dist/files/engine/redux/slice.js.map +1 -0
- package/dist/files/engine/redux/thunks.d.ts +449 -0
- package/dist/files/engine/redux/thunks.js +1684 -0
- package/dist/files/engine/redux/thunks.js.map +1 -0
- package/dist/files/engine/redux/tree-utils.d.ts +90 -0
- package/dist/files/engine/redux/tree-utils.js +200 -0
- package/dist/files/engine/redux/tree-utils.js.map +1 -0
- package/dist/files/engine/support/claimsUser.d.ts +77 -0
- package/dist/files/engine/support/claimsUser.js +48 -0
- package/dist/files/engine/support/claimsUser.js.map +1 -0
- package/dist/files/engine/support/datetime.d.ts +6 -0
- package/dist/files/engine/support/datetime.js +15 -0
- package/dist/files/engine/support/datetime.js.map +1 -0
- package/dist/files/engine/support/document-visibility.d.ts +14 -0
- package/dist/files/engine/support/document-visibility.js +28 -0
- package/dist/files/engine/support/document-visibility.js.map +1 -0
- package/dist/files/engine/support/logger.d.ts +28 -0
- package/dist/files/engine/support/logger.js +36 -0
- package/dist/files/engine/support/logger.js.map +1 -0
- package/dist/files/engine/types.d.ts +1081 -0
- package/dist/files/engine/types.js +30 -0
- package/dist/files/engine/types.js.map +1 -0
- package/dist/files/engine/upload/cloudUpload.d.ts +182 -0
- package/dist/files/engine/upload/cloudUpload.js +418 -0
- package/dist/files/engine/upload/cloudUpload.js.map +1 -0
- package/dist/files/engine/upload/tusUpload.d.ts +100 -0
- package/dist/files/engine/upload/tusUpload.js +258 -0
- package/dist/files/engine/upload/tusUpload.js.map +1 -0
- package/dist/files/engine/upload/uploadDedupGuard.d.ts +58 -0
- package/dist/files/engine/upload/uploadDedupGuard.js +41 -0
- package/dist/files/engine/upload/uploadDedupGuard.js.map +1 -0
- package/dist/files/engine/upload/uploadGuardOpeners.d.ts +70 -0
- package/dist/files/engine/upload/uploadGuardOpeners.js +54 -0
- package/dist/files/engine/upload/uploadGuardOpeners.js.map +1 -0
- package/dist/files/engine/utils/file-types.d.ts +243 -0
- package/dist/files/engine/utils/file-types.js +1722 -0
- package/dist/files/engine/utils/file-types.js.map +1 -0
- package/dist/files/engine/utils/user-visible.d.ts +133 -0
- package/dist/files/engine/utils/user-visible.js +115 -0
- package/dist/files/engine/utils/user-visible.js.map +1 -0
- package/package.json +393 -9
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { AuthError, type JWK, type JwtPayload, type SupabaseClient, type UserAppMetadata, type UserMetadata } from "@supabase/supabase-js";
|
|
2
|
+
/**
|
|
3
|
+
* The verified caller, built from the access token's claims.
|
|
4
|
+
*
|
|
5
|
+
* Every field here IS a JWT claim. `id` is `sub`, restated under the name every
|
|
6
|
+
* call site already reads. `app_metadata` / `user_metadata` are defaulted to
|
|
7
|
+
* `{}` so a reader never has to null-check what `getUser()` always handed it.
|
|
8
|
+
*/
|
|
9
|
+
export interface ApiClaimsUser extends JwtPayload {
|
|
10
|
+
/** The `sub` claim — the user id. */
|
|
11
|
+
id: string;
|
|
12
|
+
app_metadata: UserAppMetadata;
|
|
13
|
+
user_metadata: UserMetadata;
|
|
14
|
+
}
|
|
15
|
+
/** Any Supabase client — the only thing this door needs is `auth.getClaims`. */
|
|
16
|
+
export type ClaimsCapableClient = {
|
|
17
|
+
auth: Pick<SupabaseClient["auth"], "getClaims">;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* `client.auth.getUser()` without the auth-server round trip.
|
|
21
|
+
*
|
|
22
|
+
* Returns the SAME envelope `getUser()` does — `{ data: { user }, error }` —
|
|
23
|
+
* with ONE deliberate distinction the round trip never drew:
|
|
24
|
+
*
|
|
25
|
+
* - CONFIRMED SIGNED OUT (no session, no token) → `{ user: null, error: null }`.
|
|
26
|
+
* That is a settled fact, and a route is entitled to say "sign in again"
|
|
27
|
+
* rather than "try again later".
|
|
28
|
+
* - COULD NOT VERIFY (auth server / JWKS unreachable, a malformed or
|
|
29
|
+
* badly-signed token, any other verification failure, or a token that
|
|
30
|
+
* verified but carries no `sub`) → the `error` is kept, so a retry branch
|
|
31
|
+
* still fires and a screen never paints "signed out" over an outage.
|
|
32
|
+
*
|
|
33
|
+
* Pass `jwt` when the token is in hand (a Bearer header) rather than a cookie.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getClaimsUser(client: ClaimsCapableClient, jwt?: string,
|
|
36
|
+
/**
|
|
37
|
+
* Known public signing keys to verify against BEFORE any JWKS fetch
|
|
38
|
+
* (`projectSigningKeys.ts`). A key not listed falls through to the fetched
|
|
39
|
+
* JWKS, so this can only remove a network call, never change a verdict.
|
|
40
|
+
*/
|
|
41
|
+
options?: {
|
|
42
|
+
jwks: {
|
|
43
|
+
keys: JWK[];
|
|
44
|
+
};
|
|
45
|
+
}): Promise<{
|
|
46
|
+
data: {
|
|
47
|
+
user: ApiClaimsUser | null;
|
|
48
|
+
};
|
|
49
|
+
error: AuthError | null;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Is this `getClaims()` error an authority we could not REACH, as opposed to a
|
|
53
|
+
* token we could reach a verdict on?
|
|
54
|
+
*
|
|
55
|
+
* 🚨 THIS IS THE LINE `authUnavailable` DRAWS, and getting it wrong is costly in
|
|
56
|
+
* BOTH directions:
|
|
57
|
+
*
|
|
58
|
+
* - Too NARROW (only transport, but missing a case) → a real outage reads as a
|
|
59
|
+
* logout: signed-in people bounced to /login by a network blink.
|
|
60
|
+
* - Too WIDE (any error at all) → a genuinely BAD token — forged, corrupted,
|
|
61
|
+
* signed by a rotated key — reads as an outage. The person is then held on
|
|
62
|
+
* "reload in a moment" forever and NEVER told to sign in again, while the
|
|
63
|
+
* proxy, which draws the line correctly, bounces the same request to /login.
|
|
64
|
+
* Two halves of the app disagreeing about one request is worse than either
|
|
65
|
+
* answer alone. Verified live on 2026-09-21 by presenting a badly-signed
|
|
66
|
+
* token: the proxy redirected to /login while `getServerAuth` was calling it
|
|
67
|
+
* `authUnavailable`.
|
|
68
|
+
*
|
|
69
|
+
* This MIRRORS `isAuthTransportFailure` in `@ai-matrx/data/next`, which the
|
|
70
|
+
* package does not currently export (0.18.0). Keep the two in step; if they
|
|
71
|
+
* ever disagree, the package is right — it is what the proxy runs on.
|
|
72
|
+
*/
|
|
73
|
+
export declare function isAuthTransportFailure(error: AuthError): boolean;
|
|
74
|
+
/** Is this `getClaims()` error the settled fact "nobody is signed in"? */
|
|
75
|
+
export declare function isSignedOutError(error: AuthError): boolean;
|
|
76
|
+
/** The verified claims as a `{ id, ... }` user, or `null` when there are none. */
|
|
77
|
+
export declare function userFromClaims(claims: JwtPayload | undefined): ApiClaimsUser | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthError,
|
|
3
|
+
AuthSessionMissingError
|
|
4
|
+
} from "@supabase/supabase-js";
|
|
5
|
+
async function getClaimsUser(client, jwt, options) {
|
|
6
|
+
const { data, error } = options ? await client.auth.getClaims(jwt, options) : await client.auth.getClaims(jwt);
|
|
7
|
+
if (error) {
|
|
8
|
+
if (isSignedOutError(error)) return { data: { user: null }, error: null };
|
|
9
|
+
return { data: { user: null }, error };
|
|
10
|
+
}
|
|
11
|
+
if (!data?.claims) return { data: { user: null }, error: null };
|
|
12
|
+
const user = userFromClaims(data.claims);
|
|
13
|
+
if (!user) {
|
|
14
|
+
return {
|
|
15
|
+
data: { user: null },
|
|
16
|
+
error: new AuthError("Verified token carries no `sub` claim", 401, "bad_jwt")
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return { data: { user }, error: null };
|
|
20
|
+
}
|
|
21
|
+
function isAuthTransportFailure(error) {
|
|
22
|
+
const name = String(error.name ?? "");
|
|
23
|
+
if (name === "AuthRetryableFetchError" || name === "TimeoutError" || name === "AbortError") {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
const status = error.status;
|
|
27
|
+
if (status === 0 || status === 504 || status === 503) return true;
|
|
28
|
+
return status === 408 || error.code === "auth_budget_exhausted";
|
|
29
|
+
}
|
|
30
|
+
function isSignedOutError(error) {
|
|
31
|
+
return error instanceof AuthSessionMissingError || error.name === "AuthSessionMissingError" || error.code === "session_not_found";
|
|
32
|
+
}
|
|
33
|
+
function userFromClaims(claims) {
|
|
34
|
+
if (!claims || typeof claims.sub !== "string") return null;
|
|
35
|
+
return {
|
|
36
|
+
...claims,
|
|
37
|
+
id: claims.sub,
|
|
38
|
+
app_metadata: claims.app_metadata ?? {},
|
|
39
|
+
user_metadata: claims.user_metadata ?? {}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
getClaimsUser,
|
|
44
|
+
isAuthTransportFailure,
|
|
45
|
+
isSignedOutError,
|
|
46
|
+
userFromClaims
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=claimsUser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/files/engine/support/claimsUser.ts"],"sourcesContent":["// utils/supabase/claimsUser.ts — THE identity read every server and client\n// path shares. Verifies the access token LOCALLY (WebCrypto against the\n// project's cached JWKS; the main project signs ES256) and never asks the\n// auth server who the caller is.\n//\n// This file imports nothing from `next/headers`, so a Client Component, a\n// hook, a Server Action, a layout and an API route can all use it. The\n// server-only conveniences (`getServerAuth`, `resolveUser`) are thin wrappers\n// over this one function.\n//\n// 🚨 `getClaims()`, NEVER `getUser()`. `getUser()` is one network round trip\n// to the auth server PER CALL, with auth-js retrying a stalled refresh for up\n// to 30s. On 2026-09-20 that put `504 MIDDLEWARE_INVOCATION_TIMEOUT` in front\n// of real people; on 2026-09-21 a locked database did it again to every page\n// whose layout still called it. The whole class is closed by construction:\n// `.auth.getUser(` may exist in exactly the files `check:proxy-auth-hot-path`\n// allow-lists, and those wrap it as `fetchAuthUserRecord` — the ONE door for\n// the fields the JWT does not carry (`created_at`, `identities`,\n// `last_sign_in_at`, `*_confirmed_at`, `factors`) and for a read-after-write of\n// `auth.updateUser`.\n//\n// Contract: `common-docs/systems/platform/auth/proxy-identity/FEATURE.md`.\n\nimport {\n AuthError,\n AuthSessionMissingError,\n type JWK,\n type JwtPayload,\n type SupabaseClient,\n type UserAppMetadata,\n type UserMetadata,\n} from \"@supabase/supabase-js\";\n\n/**\n * The verified caller, built from the access token's claims.\n *\n * Every field here IS a JWT claim. `id` is `sub`, restated under the name every\n * call site already reads. `app_metadata` / `user_metadata` are defaulted to\n * `{}` so a reader never has to null-check what `getUser()` always handed it.\n */\nexport interface ApiClaimsUser extends JwtPayload {\n /** The `sub` claim — the user id. */\n id: string;\n app_metadata: UserAppMetadata;\n user_metadata: UserMetadata;\n}\n\n/** Any Supabase client — the only thing this door needs is `auth.getClaims`. */\nexport type ClaimsCapableClient = {\n auth: Pick<SupabaseClient[\"auth\"], \"getClaims\">;\n};\n\n/**\n * `client.auth.getUser()` without the auth-server round trip.\n *\n * Returns the SAME envelope `getUser()` does — `{ data: { user }, error }` —\n * with ONE deliberate distinction the round trip never drew:\n *\n * - CONFIRMED SIGNED OUT (no session, no token) → `{ user: null, error: null }`.\n * That is a settled fact, and a route is entitled to say \"sign in again\"\n * rather than \"try again later\".\n * - COULD NOT VERIFY (auth server / JWKS unreachable, a malformed or\n * badly-signed token, any other verification failure, or a token that\n * verified but carries no `sub`) → the `error` is kept, so a retry branch\n * still fires and a screen never paints \"signed out\" over an outage.\n *\n * Pass `jwt` when the token is in hand (a Bearer header) rather than a cookie.\n */\nexport async function getClaimsUser(\n client: ClaimsCapableClient,\n jwt?: string,\n /**\n * Known public signing keys to verify against BEFORE any JWKS fetch\n * (`projectSigningKeys.ts`). A key not listed falls through to the fetched\n * JWKS, so this can only remove a network call, never change a verdict.\n */\n options?: { jwks: { keys: JWK[] } },\n): Promise<{ data: { user: ApiClaimsUser | null }; error: AuthError | null }> {\n const { data, error } = options\n ? await client.auth.getClaims(jwt, options)\n : await client.auth.getClaims(jwt);\n // supabase-js reports \"there is no session here\" as AuthSessionMissingError.\n // That is an answer, not an outage.\n if (error) {\n if (isSignedOutError(error)) return { data: { user: null }, error: null };\n return { data: { user: null }, error };\n }\n\n // No claims at all: getClaims() ran, found no token to verify, and said so.\n if (!data?.claims) return { data: { user: null }, error: null };\n\n const user = userFromClaims(data.claims);\n // A token that VERIFIED but carries no subject is malformed, not signed out.\n if (!user) {\n return {\n data: { user: null },\n error: new AuthError(\"Verified token carries no `sub` claim\", 401, \"bad_jwt\"),\n };\n }\n return { data: { user }, error: null };\n}\n\n/**\n * Is this `getClaims()` error an authority we could not REACH, as opposed to a\n * token we could reach a verdict on?\n *\n * 🚨 THIS IS THE LINE `authUnavailable` DRAWS, and getting it wrong is costly in\n * BOTH directions:\n *\n * - Too NARROW (only transport, but missing a case) → a real outage reads as a\n * logout: signed-in people bounced to /login by a network blink.\n * - Too WIDE (any error at all) → a genuinely BAD token — forged, corrupted,\n * signed by a rotated key — reads as an outage. The person is then held on\n * \"reload in a moment\" forever and NEVER told to sign in again, while the\n * proxy, which draws the line correctly, bounces the same request to /login.\n * Two halves of the app disagreeing about one request is worse than either\n * answer alone. Verified live on 2026-09-21 by presenting a badly-signed\n * token: the proxy redirected to /login while `getServerAuth` was calling it\n * `authUnavailable`.\n *\n * This MIRRORS `isAuthTransportFailure` in `@ai-matrx/data/next`, which the\n * package does not currently export (0.18.0). Keep the two in step; if they\n * ever disagree, the package is right — it is what the proxy runs on.\n */\nexport function isAuthTransportFailure(error: AuthError): boolean {\n const name = String(error.name ?? \"\");\n if (name === \"AuthRetryableFetchError\" || name === \"TimeoutError\" || name === \"AbortError\") {\n return true;\n }\n const status = error.status;\n if (status === 0 || status === 504 || status === 503) return true;\n // 408 / `auth_budget_exhausted` is the synthetic answer `createAuthBudget`\n // returns once the request's 2.5s identity budget is spent.\n return status === 408 || error.code === \"auth_budget_exhausted\";\n}\n\n/** Is this `getClaims()` error the settled fact \"nobody is signed in\"? */\nexport function isSignedOutError(error: AuthError): boolean {\n return (\n error instanceof AuthSessionMissingError ||\n error.name === \"AuthSessionMissingError\" ||\n error.code === \"session_not_found\"\n );\n}\n\n/** The verified claims as a `{ id, ... }` user, or `null` when there are none. */\nexport function userFromClaims(claims: JwtPayload | undefined): ApiClaimsUser | null {\n if (!claims || typeof claims.sub !== \"string\") return null;\n return {\n ...claims,\n id: claims.sub,\n app_metadata: claims.app_metadata ?? {},\n user_metadata: claims.user_metadata ?? {},\n };\n}\n"],"mappings":"AAuBA;AAAA,EACE;AAAA,EACA;AAAA,OAMK;AAqCP,eAAsB,cACpB,QACA,KAMA,SAC4E;AAC5E,QAAM,EAAE,MAAM,MAAM,IAAI,UACpB,MAAM,OAAO,KAAK,UAAU,KAAK,OAAO,IACxC,MAAM,OAAO,KAAK,UAAU,GAAG;AAGnC,MAAI,OAAO;AACT,QAAI,iBAAiB,KAAK,EAAG,QAAO,EAAE,MAAM,EAAE,MAAM,KAAK,GAAG,OAAO,KAAK;AACxE,WAAO,EAAE,MAAM,EAAE,MAAM,KAAK,GAAG,MAAM;AAAA,EACvC;AAGA,MAAI,CAAC,MAAM,OAAQ,QAAO,EAAE,MAAM,EAAE,MAAM,KAAK,GAAG,OAAO,KAAK;AAE9D,QAAM,OAAO,eAAe,KAAK,MAAM;AAEvC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,MACL,MAAM,EAAE,MAAM,KAAK;AAAA,MACnB,OAAO,IAAI,UAAU,yCAAyC,KAAK,SAAS;AAAA,IAC9E;AAAA,EACF;AACA,SAAO,EAAE,MAAM,EAAE,KAAK,GAAG,OAAO,KAAK;AACvC;AAwBO,SAAS,uBAAuB,OAA2B;AAChE,QAAM,OAAO,OAAO,MAAM,QAAQ,EAAE;AACpC,MAAI,SAAS,6BAA6B,SAAS,kBAAkB,SAAS,cAAc;AAC1F,WAAO;AAAA,EACT;AACA,QAAM,SAAS,MAAM;AACrB,MAAI,WAAW,KAAK,WAAW,OAAO,WAAW,IAAK,QAAO;AAG7D,SAAO,WAAW,OAAO,MAAM,SAAS;AAC1C;AAGO,SAAS,iBAAiB,OAA2B;AAC1D,SACE,iBAAiB,2BACjB,MAAM,SAAS,6BACf,MAAM,SAAS;AAEnB;AAGO,SAAS,eAAe,QAAsD;AACnF,MAAI,CAAC,UAAU,OAAO,OAAO,QAAQ,SAAU,QAAO;AACtD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,IAAI,OAAO;AAAA,IACX,cAAc,OAAO,gBAAgB,CAAC;AAAA,IACtC,eAAe,OAAO,iBAAiB,CAAC;AAAA,EAC1C;AACF;","names":[]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type TimestampInput } from "@ai-matrx/kit/format";
|
|
2
|
+
/**
|
|
3
|
+
* Numeric comparison helper for sorting by timestamp. Unparseable values
|
|
4
|
+
* sort LAST regardless of direction (caller multiplies the sign).
|
|
5
|
+
*/
|
|
6
|
+
export declare function compareTimestamps(a: TimestampInput, b: TimestampInput): number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { parseTimestamp } from "@ai-matrx/kit/format";
|
|
2
|
+
function compareTimestamps(a, b) {
|
|
3
|
+
const ta = parseTimestamp(a)?.getTime() ?? NaN;
|
|
4
|
+
const tb = parseTimestamp(b)?.getTime() ?? NaN;
|
|
5
|
+
const aNan = Number.isNaN(ta);
|
|
6
|
+
const bNan = Number.isNaN(tb);
|
|
7
|
+
if (aNan && bNan) return 0;
|
|
8
|
+
if (aNan) return 1;
|
|
9
|
+
if (bNan) return -1;
|
|
10
|
+
return ta - tb;
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
compareTimestamps
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=datetime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/files/engine/support/datetime.ts"],"sourcesContent":["import { parseTimestamp, type TimestampInput } from \"@ai-matrx/kit/format\";\n\n/**\n * Numeric comparison helper for sorting by timestamp. Unparseable values\n * sort LAST regardless of direction (caller multiplies the sign).\n */\nexport function compareTimestamps(a: TimestampInput, b: TimestampInput): number {\n const ta = parseTimestamp(a)?.getTime() ?? NaN;\n const tb = parseTimestamp(b)?.getTime() ?? NaN;\n const aNan = Number.isNaN(ta);\n const bNan = Number.isNaN(tb);\n if (aNan && bNan) return 0;\n if (aNan) return 1;\n if (bNan) return -1;\n return ta - tb;\n}\n"],"mappings":"AAAA,SAAS,sBAA2C;AAM7C,SAAS,kBAAkB,GAAmB,GAA2B;AAC9E,QAAM,KAAK,eAAe,CAAC,GAAG,QAAQ,KAAK;AAC3C,QAAM,KAAK,eAAe,CAAC,GAAG,QAAQ,KAAK;AAC3C,QAAM,OAAO,OAAO,MAAM,EAAE;AAC5B,QAAM,OAAO,OAAO,MAAM,EAAE;AAC5B,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,KAAM,QAAO;AACjB,MAAI,KAAM,QAAO;AACjB,SAAO,KAAK;AACd;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document visibility primitives. A hidden tab's network is frozen or
|
|
3
|
+
* throttled by the browser, so background work that runs (or times out) while
|
|
4
|
+
* hidden is work nobody watched: defer it to the moment the person returns.
|
|
5
|
+
* SSR-safe: with no `document`, the page counts as visible.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isDocumentHidden(): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Run `run` once, the next time the tab is visible (immediately if it already
|
|
10
|
+
* is). Returns a cancel that drops a run still waiting.
|
|
11
|
+
*/
|
|
12
|
+
export declare function onceDocumentVisible(run: () => void): () => void;
|
|
13
|
+
/** Resolves the next time the tab is visible (now, if it already is). */
|
|
14
|
+
export declare function whenDocumentVisible(): Promise<void>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function isDocumentHidden() {
|
|
2
|
+
return typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
3
|
+
}
|
|
4
|
+
function onceDocumentVisible(run) {
|
|
5
|
+
if (!isDocumentHidden()) {
|
|
6
|
+
run();
|
|
7
|
+
return () => {
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
if (isDocumentHidden()) return;
|
|
12
|
+
document.removeEventListener("visibilitychange", onChange);
|
|
13
|
+
run();
|
|
14
|
+
};
|
|
15
|
+
document.addEventListener("visibilitychange", onChange);
|
|
16
|
+
return () => document.removeEventListener("visibilitychange", onChange);
|
|
17
|
+
}
|
|
18
|
+
function whenDocumentVisible() {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
onceDocumentVisible(resolve);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
isDocumentHidden,
|
|
25
|
+
onceDocumentVisible,
|
|
26
|
+
whenDocumentVisible
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=document-visibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/files/engine/support/document-visibility.ts"],"sourcesContent":["/**\n * Document visibility primitives. A hidden tab's network is frozen or\n * throttled by the browser, so background work that runs (or times out) while\n * hidden is work nobody watched: defer it to the moment the person returns.\n * SSR-safe: with no `document`, the page counts as visible.\n */\n\nexport function isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\n/**\n * Run `run` once, the next time the tab is visible (immediately if it already\n * is). Returns a cancel that drops a run still waiting.\n */\nexport function onceDocumentVisible(run: () => void): () => void {\n if (!isDocumentHidden()) {\n run();\n return () => {};\n }\n const onChange = () => {\n if (isDocumentHidden()) return;\n document.removeEventListener(\"visibilitychange\", onChange);\n run();\n };\n document.addEventListener(\"visibilitychange\", onChange);\n return () => document.removeEventListener(\"visibilitychange\", onChange);\n}\n\n/** Resolves the next time the tab is visible (now, if it already is). */\nexport function whenDocumentVisible(): Promise<void> {\n return new Promise((resolve) => {\n onceDocumentVisible(resolve);\n });\n}\n"],"mappings":"AAOO,SAAS,mBAA4B;AAC1C,SACE,OAAO,aAAa,eAAe,SAAS,oBAAoB;AAEpE;AAMO,SAAS,oBAAoB,KAA6B;AAC/D,MAAI,CAAC,iBAAiB,GAAG;AACvB,QAAI;AACJ,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACA,QAAM,WAAW,MAAM;AACrB,QAAI,iBAAiB,EAAG;AACxB,aAAS,oBAAoB,oBAAoB,QAAQ;AACzD,QAAI;AAAA,EACN;AACA,WAAS,iBAAiB,oBAAoB,QAAQ;AACtD,SAAO,MAAM,SAAS,oBAAoB,oBAAoB,QAAQ;AACxE;AAGO,SAAS,sBAAqC;AACnD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,wBAAoB,OAAO;AAAA,EAC7B,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/sync/logger.ts
|
|
3
|
+
*
|
|
4
|
+
* Single prefixed logger for the sync engine. Silent in production unless
|
|
5
|
+
* `?sync-debug=1` is in the URL or `localStorage.getItem('matrx:sync:debug') === '1'`.
|
|
6
|
+
*
|
|
7
|
+
* Phase 12 expands this into the debug-panel feed + observability hooks (Q8).
|
|
8
|
+
*/
|
|
9
|
+
interface LogEvent {
|
|
10
|
+
event: string;
|
|
11
|
+
sliceName?: string;
|
|
12
|
+
ms?: number;
|
|
13
|
+
meta?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
declare function isDebugEnabled(): boolean;
|
|
16
|
+
export declare const logger: {
|
|
17
|
+
debug: (event: string, meta?: Omit<LogEvent, "event">) => void;
|
|
18
|
+
info: (event: string, meta?: Omit<LogEvent, "event">) => void;
|
|
19
|
+
warn: (event: string, meta?: Omit<LogEvent, "event">) => void;
|
|
20
|
+
error: (event: string, meta?: Omit<LogEvent, "event">) => void;
|
|
21
|
+
};
|
|
22
|
+
/** Visible for tests only. */
|
|
23
|
+
export declare const __internal: {
|
|
24
|
+
DEBUG_FLAG_KEY: string;
|
|
25
|
+
DEBUG_URL_PARAM: string;
|
|
26
|
+
isDebugEnabled: typeof isDebugEnabled;
|
|
27
|
+
};
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const DEBUG_FLAG_KEY = "matrx:sync:debug";
|
|
2
|
+
const DEBUG_URL_PARAM = "sync-debug";
|
|
3
|
+
function isDebugEnabled() {
|
|
4
|
+
if (true) return true;
|
|
5
|
+
if (typeof window === "undefined") return false;
|
|
6
|
+
try {
|
|
7
|
+
if (window.localStorage?.getItem(DEBUG_FLAG_KEY) === "1") return true;
|
|
8
|
+
const params = new URLSearchParams(window.location.search);
|
|
9
|
+
return params.get(DEBUG_URL_PARAM) === "1";
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function emit(level, evt) {
|
|
15
|
+
if (!isDebugEnabled() && level !== "error") return;
|
|
16
|
+
const line = `[sync] ${evt.event}`;
|
|
17
|
+
const payload = {};
|
|
18
|
+
if (evt.sliceName) payload.sliceName = evt.sliceName;
|
|
19
|
+
if (evt.ms != null) payload.ms = evt.ms;
|
|
20
|
+
if (evt.meta) Object.assign(payload, evt.meta);
|
|
21
|
+
const hasPayload = Object.keys(payload).length > 0;
|
|
22
|
+
const fn = console[level] ?? console.log;
|
|
23
|
+
hasPayload ? fn(line, payload) : fn(line);
|
|
24
|
+
}
|
|
25
|
+
const logger = {
|
|
26
|
+
debug: (event, meta) => emit("debug", { event, ...meta }),
|
|
27
|
+
info: (event, meta) => emit("info", { event, ...meta }),
|
|
28
|
+
warn: (event, meta) => emit("warn", { event, ...meta }),
|
|
29
|
+
error: (event, meta) => emit("error", { event, ...meta })
|
|
30
|
+
};
|
|
31
|
+
const __internal = { DEBUG_FLAG_KEY, DEBUG_URL_PARAM, isDebugEnabled };
|
|
32
|
+
export {
|
|
33
|
+
__internal,
|
|
34
|
+
logger
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/files/engine/support/logger.ts"],"sourcesContent":["/**\n * lib/sync/logger.ts\n *\n * Single prefixed logger for the sync engine. Silent in production unless\n * `?sync-debug=1` is in the URL or `localStorage.getItem('matrx:sync:debug') === '1'`.\n *\n * Phase 12 expands this into the debug-panel feed + observability hooks (Q8).\n */\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\ninterface LogEvent {\n event: string;\n sliceName?: string;\n ms?: number;\n meta?: Record<string, unknown>;\n}\n\nconst DEBUG_FLAG_KEY = \"matrx:sync:debug\";\nconst DEBUG_URL_PARAM = \"sync-debug\";\n\nfunction isDebugEnabled(): boolean {\n if (process.env.NODE_ENV !== \"production\") return true;\n if (typeof window === \"undefined\") return false;\n try {\n if (window.localStorage?.getItem(DEBUG_FLAG_KEY) === \"1\") return true;\n const params = new URLSearchParams(window.location.search);\n return params.get(DEBUG_URL_PARAM) === \"1\";\n } catch {\n return false;\n }\n}\n\nfunction emit(level: LogLevel, evt: LogEvent): void {\n if (!isDebugEnabled() && level !== \"error\") return;\n const line = `[sync] ${evt.event}`;\n const payload: Record<string, unknown> = {};\n if (evt.sliceName) payload.sliceName = evt.sliceName;\n if (evt.ms != null) payload.ms = evt.ms;\n if (evt.meta) Object.assign(payload, evt.meta);\n const hasPayload = Object.keys(payload).length > 0;\n // eslint-disable-next-line no-console\n const fn = console[level] ?? console.log;\n hasPayload ? fn(line, payload) : fn(line);\n}\n\nexport const logger = {\n debug: (event: string, meta?: Omit<LogEvent, \"event\">) => emit(\"debug\", { event, ...meta }),\n info: (event: string, meta?: Omit<LogEvent, \"event\">) => emit(\"info\", { event, ...meta }),\n warn: (event: string, meta?: Omit<LogEvent, \"event\">) => emit(\"warn\", { event, ...meta }),\n error: (event: string, meta?: Omit<LogEvent, \"event\">) => emit(\"error\", { event, ...meta }),\n};\n\n/** Visible for tests only. */\nexport const __internal = { DEBUG_FLAG_KEY, DEBUG_URL_PARAM, isDebugEnabled };\n"],"mappings":"AAkBA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAExB,SAAS,iBAA0B;AAC/B,MAAI,KAAuC,QAAO;AAClD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACA,QAAI,OAAO,cAAc,QAAQ,cAAc,MAAM,IAAK,QAAO;AACjE,UAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,WAAO,OAAO,IAAI,eAAe,MAAM;AAAA,EAC3C,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,SAAS,KAAK,OAAiB,KAAqB;AAChD,MAAI,CAAC,eAAe,KAAK,UAAU,QAAS;AAC5C,QAAM,OAAO,UAAU,IAAI,KAAK;AAChC,QAAM,UAAmC,CAAC;AAC1C,MAAI,IAAI,UAAW,SAAQ,YAAY,IAAI;AAC3C,MAAI,IAAI,MAAM,KAAM,SAAQ,KAAK,IAAI;AACrC,MAAI,IAAI,KAAM,QAAO,OAAO,SAAS,IAAI,IAAI;AAC7C,QAAM,aAAa,OAAO,KAAK,OAAO,EAAE,SAAS;AAEjD,QAAM,KAAK,QAAQ,KAAK,KAAK,QAAQ;AACrC,eAAa,GAAG,MAAM,OAAO,IAAI,GAAG,IAAI;AAC5C;AAEO,MAAM,SAAS;AAAA,EAClB,OAAO,CAAC,OAAe,SAAmC,KAAK,SAAS,EAAE,OAAO,GAAG,KAAK,CAAC;AAAA,EAC1F,MAAM,CAAC,OAAe,SAAmC,KAAK,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC;AAAA,EACxF,MAAM,CAAC,OAAe,SAAmC,KAAK,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC;AAAA,EACxF,OAAO,CAAC,OAAe,SAAmC,KAAK,SAAS,EAAE,OAAO,GAAG,KAAK,CAAC;AAC9F;AAGO,MAAM,aAAa,EAAE,gBAAgB,iBAAiB,eAAe;","names":[]}
|