@magicvr/schema-ui-shell 0.1.0 → 0.1.1
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/account/AuthContext.d.ts +32 -0
- package/account/auth-client.d.ts +106 -0
- package/account/tokens.d.ts +14 -0
- package/app/App.d.ts +2 -2
- package/app/AuthGate.d.ts +1 -1
- package/app/navigation.d.ts +2 -2
- package/components/ui/breadcrumbs.d.ts +1 -1
- package/host/boot.d.ts +1 -1
- package/package.json +12 -7
- package/protocol/app-manifest.d.ts +117 -0
- package/protocol/conformance/component-format.d.ts +13 -0
- package/protocol/conformance/query-serialize.d.ts +24 -0
- package/protocol/conformance/request-construction.d.ts +42 -0
- package/protocol/conformance/runtime-schema-validate.d.ts +23 -0
- package/protocol/conformance/upload-orchestration.d.ts +102 -0
- package/protocol/load-page.d.ts +45 -0
- package/renderer/custom-components.d.ts +1 -1
- package/renderer/form-controls.d.ts +3 -3
- package/renderer/permissions.d.ts +1 -1
- package/renderer/render.d.ts +4 -4
- package/renderer/render.types.d.ts +2 -2
- package/renderer/resource.d.ts +1 -1
- package/renderer/schema-table.d.ts +2 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type AuthSession } from "@/account/auth-client";
|
|
3
|
+
import type { SessionAdapterState } from "@/host/boot";
|
|
4
|
+
export type AuthStatus = SessionAdapterState;
|
|
5
|
+
export interface AuthContextValue {
|
|
6
|
+
status: AuthStatus;
|
|
7
|
+
session: AuthSession | null;
|
|
8
|
+
user: AuthSession["user"] | null;
|
|
9
|
+
/** Authenticates, restores a captured return intent, and transitions to the
|
|
10
|
+
* shell. When the account requires a second factor (S-10 · GOAL-017 D-002
|
|
11
|
+
* §3) resolveMFA is invoked with the one-time proof and must return the
|
|
12
|
+
* TOTP code (or recovery code) the user entered. */
|
|
13
|
+
login: (username: string, password: string, captcha?: import("@/account/auth-client").LoginCaptcha, resolveMFA?: (proof: string) => Promise<{
|
|
14
|
+
code: string;
|
|
15
|
+
recoveryCode?: string;
|
|
16
|
+
}>) => Promise<void>;
|
|
17
|
+
/** Revokes the session and transitions to the login page. */
|
|
18
|
+
logout: () => Promise<void>;
|
|
19
|
+
/** Auth-aware fetch: attaches Bearer and refreshes once on 401. */
|
|
20
|
+
authFetch: typeof fetch;
|
|
21
|
+
/**
|
|
22
|
+
* Re-resolves /me into the session (best-effort; keeps the current session
|
|
23
|
+
* on failure). W13 T-05 follow-up: the account profile save publishes the
|
|
24
|
+
* account.profile config-change event and the provider refreshes itself so
|
|
25
|
+
* the shell header (avatar / display name) updates without a reload.
|
|
26
|
+
*/
|
|
27
|
+
refreshSession: () => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare function AuthProvider({ children }: {
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
32
|
+
export declare function useAuth(): AuthContextValue;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export interface AuthUser {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
roles: string[];
|
|
5
|
+
/** Self-service avatar asset URL (W13 T-05); absent = no avatar. */
|
|
6
|
+
avatarUrl?: string;
|
|
7
|
+
/** True when the account must change its initial/reset password before using
|
|
8
|
+
* business APIs (W16-F01). */
|
|
9
|
+
mustChangePassword?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Permission keys resolved from persisted RBAC at identity load (`/me`).
|
|
12
|
+
* Required by Schema expressions (`$context.user.permissions contains "…"`).
|
|
13
|
+
* Optional on the type only because login token payloads may omit it until
|
|
14
|
+
* `/me` completes; the session returned by login/restore always carries it
|
|
15
|
+
* when the API provides it.
|
|
16
|
+
*/
|
|
17
|
+
permissions?: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface AuthSession {
|
|
20
|
+
user: AuthUser;
|
|
21
|
+
features: Record<string, boolean>;
|
|
22
|
+
}
|
|
23
|
+
/** Error with a stable code so the UI can branch (e.g. INVALID_CREDENTIALS). */
|
|
24
|
+
export declare class AuthError extends Error {
|
|
25
|
+
code: string;
|
|
26
|
+
/** Optional HTTP status carried for errors that localize a {status} param. */
|
|
27
|
+
status?: number;
|
|
28
|
+
constructor(code: string, message: string, status?: number);
|
|
29
|
+
}
|
|
30
|
+
export declare function setAuthLostListener(listener: (() => void) | null): void;
|
|
31
|
+
/**
|
|
32
|
+
* Auth-aware fetch: attaches the Bearer access token, and on a 401 (not an auth
|
|
33
|
+
* endpoint) attempts one silent refresh then retries once. If the refresh fails,
|
|
34
|
+
* or the retry is still 401 after a successful refresh, the session is cleared
|
|
35
|
+
* and the auth-lost listener fires (UI → login page).
|
|
36
|
+
*/
|
|
37
|
+
export declare function authFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
38
|
+
/** Captcha challenge submitted with login (S-11 · GOAL-011 D-002 §5). */
|
|
39
|
+
export interface LoginCaptcha {
|
|
40
|
+
id: string;
|
|
41
|
+
answer: string;
|
|
42
|
+
}
|
|
43
|
+
/** Second factor required by the login (S-10 · GOAL-017 D-002 §3): the
|
|
44
|
+
* password factor succeeded; no tokens are issued until /api/auth/mfa/verify
|
|
45
|
+
* completes with the one-time proof. */
|
|
46
|
+
export interface LoginMFARequired {
|
|
47
|
+
mfaRequired: true;
|
|
48
|
+
mfaProof: string;
|
|
49
|
+
}
|
|
50
|
+
export declare function isLoginMFARequired(value: AuthSession | LoginMFARequired): value is LoginMFARequired;
|
|
51
|
+
/** Completes a two-step login with the second factor (S-10 · GOAL-017 D-002
|
|
52
|
+
* §3): proof + TOTP code (or one-time recovery code) → real token pair. */
|
|
53
|
+
export declare function mfaVerify(proof: string, code: string, recoveryCode?: string): Promise<AuthSession>;
|
|
54
|
+
/** Authenticates with username/password and persists the token pair. When the
|
|
55
|
+
* account requires a second factor the response carries mfaRequired + a
|
|
56
|
+
* one-time proof instead of tokens — complete it via mfaVerify. */
|
|
57
|
+
export declare function login(username: string, password: string, captcha?: LoginCaptcha): Promise<AuthSession | LoginMFARequired>;
|
|
58
|
+
/** Revokes the refresh token (best-effort, idempotent) and clears local state. */
|
|
59
|
+
export declare function logout(): Promise<void>;
|
|
60
|
+
/** Requests a reset code for the account (username or its verified email).
|
|
61
|
+
* The server answers 202 dispatched even when no recovery path exists, so a
|
|
62
|
+
* resolved promise NEVER means an email was actually sent — only that the
|
|
63
|
+
* request was accepted. */
|
|
64
|
+
export declare function recoveryStart(account: string): Promise<void>;
|
|
65
|
+
export interface RecoveryCompleteInput {
|
|
66
|
+
account: string;
|
|
67
|
+
code: string;
|
|
68
|
+
newPassword: string;
|
|
69
|
+
/** TOTP code — required when the account has MFA enrolled. */
|
|
70
|
+
secondFactorCode?: string;
|
|
71
|
+
/** One-time recovery code as the TOTP alternative. */
|
|
72
|
+
recoveryCode?: string;
|
|
73
|
+
}
|
|
74
|
+
/** Completes self-recovery: verifies the emailed code (+ second factor for
|
|
75
|
+
* MFA accounts), swaps the password and revokes every live session. Success
|
|
76
|
+
* returns WITHOUT tokens — the user signs in with the new password. */
|
|
77
|
+
export declare function recoveryComplete(input: RecoveryCompleteInput): Promise<void>;
|
|
78
|
+
/** Restore outcomes (ADR-0035 D4 normalized adapter input, GOAL-004 S4-2). */
|
|
79
|
+
export type RestoreSessionResult = {
|
|
80
|
+
kind: "none";
|
|
81
|
+
} | {
|
|
82
|
+
kind: "reauth";
|
|
83
|
+
} | {
|
|
84
|
+
kind: "session";
|
|
85
|
+
session: AuthSession;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Restores a session on boot: if a refresh token exists, rotate it for a fresh
|
|
89
|
+
* access/refresh pair, then resolve the identity via /me. No refresh token →
|
|
90
|
+
* `none` (anonymous); rotation or identity resolution failure with an existing
|
|
91
|
+
* refresh token → `reauth` (the adapter reports reauth-required, ADR-0035 D4).
|
|
92
|
+
*/
|
|
93
|
+
export declare function restoreSession(): Promise<RestoreSessionResult>;
|
|
94
|
+
/** Fetches the current session from /me using the active access token. */
|
|
95
|
+
export declare function fetchMe(): Promise<AuthSession>;
|
|
96
|
+
export interface InviteAcceptInput {
|
|
97
|
+
token: string;
|
|
98
|
+
username: string;
|
|
99
|
+
name: string;
|
|
100
|
+
password: string;
|
|
101
|
+
}
|
|
102
|
+
/** Redeems an invitation into a real account. Success returns WITHOUT tokens
|
|
103
|
+
* (GOAL-002 D-001 §4 projection): the invitee signs in with their new
|
|
104
|
+
* credentials. Errors carry the cataloged server codes (INVITE_INVALID /
|
|
105
|
+
* USERNAME_TAKEN / INVALID_PASSWORD). */
|
|
106
|
+
export declare function inviteAccept(input: InviteAcceptInput): Promise<void>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* R2 token storage (GOAL-005 D-002): the short-lived JWT access token lives in
|
|
3
|
+
* memory only; the opaque refresh token persists in localStorage so the session
|
|
4
|
+
* survives a page reload and is rotated on every refresh. localStorage is the
|
|
5
|
+
* user-accepted XSS trade-off (D-002), mitigated by short access TTL, server-side
|
|
6
|
+
* revocation and HTTPS.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getAccessToken(): string | null;
|
|
9
|
+
export declare function setAccessToken(token: string | null): void;
|
|
10
|
+
export declare function getRefreshToken(): string | null;
|
|
11
|
+
export declare function setRefreshToken(token: string | null): void;
|
|
12
|
+
export declare function clearTokens(): void;
|
|
13
|
+
/** Whether a session may exist (an access token in memory or a refresh token). */
|
|
14
|
+
export declare function hasSession(): boolean;
|
package/app/App.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Branding } from "
|
|
2
|
-
import { type AppManifest, type NavigationContext } from "
|
|
1
|
+
import { type Branding } from "@schema-ui/shell/app/branding";
|
|
2
|
+
import { type AppManifest, type NavigationContext } from "@schema-ui/protocol/app-manifest";
|
|
3
3
|
export interface AppProps {
|
|
4
4
|
manifest: AppManifest;
|
|
5
5
|
navigationContext?: NavigationContext;
|
package/app/AuthGate.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* ("无法显示此页面"). The production-wiring regression lock lives in
|
|
12
12
|
* auth-gate.wiring.test.tsx.
|
|
13
13
|
*/
|
|
14
|
-
import type { AppManifest } from "
|
|
14
|
+
import type { AppManifest } from "@schema-ui/protocol/app-manifest";
|
|
15
15
|
/** Boot placeholder while the session adapter resolves (ADR-0035 D4). */
|
|
16
16
|
export declare function BootScreen(): import("react").JSX.Element;
|
|
17
17
|
/** Renders the login page when unauthenticated, the shell when authenticated. */
|
package/app/navigation.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type AppManifest, type NavigationContext } from "
|
|
2
|
-
import { type MessageParams } from "
|
|
1
|
+
import { type AppManifest, type NavigationContext } from "@schema-ui/protocol/app-manifest";
|
|
2
|
+
import { type MessageParams } from "@schema-ui/lib/i18n/catalog";
|
|
3
3
|
export interface ProjectedLink {
|
|
4
4
|
type: "link";
|
|
5
5
|
href?: string;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* "/" separators, brighter non-clickable current item, and an optional
|
|
16
16
|
* small circular ghost back button (semantic parent) at the far left.
|
|
17
17
|
*/
|
|
18
|
-
import { type MessageParams } from "
|
|
18
|
+
import { type MessageParams } from "@schema-ui/lib/i18n/catalog";
|
|
19
19
|
export interface BreadcrumbEntry {
|
|
20
20
|
/** Page id (manifest pageId); group labels use the label text as key. */
|
|
21
21
|
pageId: string;
|
package/host/boot.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* reuses the fixture-pinned `evaluateBootstrap`, so the vendored upstream
|
|
7
7
|
* host-bootstrap suite covers every stage this orchestrator executes.
|
|
8
8
|
*/
|
|
9
|
-
import type { AppManifest } from "
|
|
9
|
+
import type { AppManifest } from "@schema-ui/protocol/app-manifest";
|
|
10
10
|
import { discoverBootstrapDocument, type BootstrapAuth, type BootstrapEvaluation } from "@/host/bootstrap";
|
|
11
11
|
import { type HostFailure } from "@/host/failure";
|
|
12
12
|
/** Session adapter state (ADR-0035 D4): normalized by AuthContext. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magicvr/schema-ui-shell",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "schema-ui-core 包面(VP-023 R3 六包化)",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,21 +9,26 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./app/index.d.ts",
|
|
11
11
|
"import": "./index.js"
|
|
12
|
-
}
|
|
12
|
+
},
|
|
13
|
+
"./*": "./*"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"index.js",
|
|
16
|
-
"
|
|
17
|
-
"theme",
|
|
18
|
-
"components",
|
|
17
|
+
"account",
|
|
19
18
|
"app",
|
|
19
|
+
"components",
|
|
20
20
|
"host",
|
|
21
21
|
"i18n",
|
|
22
|
-
"
|
|
22
|
+
"lib",
|
|
23
|
+
"protocol",
|
|
23
24
|
"renderer",
|
|
24
|
-
"
|
|
25
|
+
"theme"
|
|
25
26
|
],
|
|
26
27
|
"license": "UNLICENSED",
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": "^19.0.0",
|
|
30
|
+
"react-dom": "^19.0.0"
|
|
31
|
+
},
|
|
27
32
|
"publishConfig": {
|
|
28
33
|
"access": "public"
|
|
29
34
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export declare const DEFAULT_MANIFEST_PATH = "/.well-known/schema-ui/app-manifest.json";
|
|
2
|
+
/**
|
|
3
|
+
* Host manifest-version support set (strict negotiation, ADR-0009): 2.7 for
|
|
4
|
+
* existing production manifests, 2.8 for Host/App interoperability manifests
|
|
5
|
+
* (returnIntentQueryKeys etc.), 2.9 for ADR-0039/ADR-0040 (data.route-binding /
|
|
6
|
+
* form.controls.readonly). Kept additive — older manifests stay accepted.
|
|
7
|
+
*/
|
|
8
|
+
export declare const APP_MANIFEST_SUPPORTED_PROTOCOL_VERSIONS: readonly ["2.7", "2.8", "2.9"];
|
|
9
|
+
export declare const APP_MANIFEST_PROTOCOL_VERSION: "2.9";
|
|
10
|
+
export declare const MANIFEST_SOURCE_HEADER = "X-Schema-UI-Manifest-Source";
|
|
11
|
+
export declare const APP_MANIFEST_SOURCE = "https://github.com/magicvr/schema-ui-docs/tree/81aa1d8";
|
|
12
|
+
/** True when `expression` matches the frozen $context expression grammar. */
|
|
13
|
+
export declare function isValidExpression(expression: string): boolean;
|
|
14
|
+
export type ManifestErrorCode = "MANIFEST_LOAD_FAILED" | "MISSING_PROTOCOL_VERSION" | "INVALID_PROTOCOL_VERSION" | "UNSUPPORTED_PROTOCOL_VERSION" | "PROTOCOL_VERSION_TOO_LOW" | "MISSING_REQUIRED_CAPABILITY" | "CAPABILITY_REQUIRED" | "UNKNOWN_MANIFEST_FIELD" | "INVALID_MANIFEST" | "INVALID_PATH" | "MANIFEST_HOME_PAGE_UNKNOWN" | "MANIFEST_HOME_ROUTE_PARAMETRIC" | "PAGE_REF_WITH_EMPTY_PAGES" | "MISSING_PATH_BINDING" | "MANIFEST_PAGE_ID_MISMATCH" | "INVALID_LOGO_URL" | "UNKNOWN_NAV_SLOT" | "NAV_LINK_MUTEX" | "NAV_GROUP_NESTED" | "NAV_PAGE_REF_UNKNOWN" | "INVALID_RETURN_INTENT_QUERY_KEYS" | "FORBIDDEN_VARIABLE" | "SYNTAX";
|
|
15
|
+
export declare class ManifestError extends Error {
|
|
16
|
+
readonly code: ManifestErrorCode;
|
|
17
|
+
readonly path: string;
|
|
18
|
+
/** Optional machine detail (e.g. the missing capability id). */
|
|
19
|
+
readonly detail?: string;
|
|
20
|
+
constructor(code: ManifestErrorCode, path: string, message: string, detail?: string);
|
|
21
|
+
}
|
|
22
|
+
export interface AppInfo {
|
|
23
|
+
appId: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
nameKey?: string;
|
|
26
|
+
homePageRef?: string;
|
|
27
|
+
logo?: {
|
|
28
|
+
light: string;
|
|
29
|
+
dark?: string;
|
|
30
|
+
};
|
|
31
|
+
description?: string;
|
|
32
|
+
descriptionKey?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface PageEntry {
|
|
35
|
+
pageId: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
titleKey?: string;
|
|
38
|
+
schemaUrl: string;
|
|
39
|
+
route: string;
|
|
40
|
+
/** v2.8+ (ADR-0036): auth-return intent allowlist extension for this page. */
|
|
41
|
+
returnIntentQueryKeys?: string[];
|
|
42
|
+
}
|
|
43
|
+
export interface VisibleWhen {
|
|
44
|
+
when: string;
|
|
45
|
+
}
|
|
46
|
+
export interface NavPermissions {
|
|
47
|
+
view?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface NavLink {
|
|
50
|
+
pageRef?: string;
|
|
51
|
+
url?: string;
|
|
52
|
+
label?: string;
|
|
53
|
+
labelKey?: string;
|
|
54
|
+
icon?: string;
|
|
55
|
+
visibleWhen?: VisibleWhen;
|
|
56
|
+
permissions?: NavPermissions;
|
|
57
|
+
}
|
|
58
|
+
export interface NavGroup {
|
|
59
|
+
label?: string;
|
|
60
|
+
labelKey?: string;
|
|
61
|
+
icon?: string;
|
|
62
|
+
items: NavLink[];
|
|
63
|
+
visibleWhen?: VisibleWhen;
|
|
64
|
+
permissions?: NavPermissions;
|
|
65
|
+
}
|
|
66
|
+
export type NavItem = NavLink | NavGroup;
|
|
67
|
+
export interface Navigation {
|
|
68
|
+
top?: NavItem[];
|
|
69
|
+
sidebar?: NavItem[];
|
|
70
|
+
user?: NavItem[];
|
|
71
|
+
}
|
|
72
|
+
export interface AppManifest {
|
|
73
|
+
protocolVersion: string;
|
|
74
|
+
requiredCapabilities: string[];
|
|
75
|
+
app: AppInfo;
|
|
76
|
+
pages: PageEntry[];
|
|
77
|
+
navigation?: Navigation;
|
|
78
|
+
}
|
|
79
|
+
export interface RouteMatch {
|
|
80
|
+
page: PageEntry;
|
|
81
|
+
index: number;
|
|
82
|
+
params: Record<string, string>;
|
|
83
|
+
}
|
|
84
|
+
export interface ResolvedRoute extends RouteMatch {
|
|
85
|
+
path: string;
|
|
86
|
+
query: Record<string, string>;
|
|
87
|
+
source: "home" | "deepLink";
|
|
88
|
+
}
|
|
89
|
+
export interface NavigationContext {
|
|
90
|
+
user?: Record<string, unknown>;
|
|
91
|
+
features?: Record<string, unknown>;
|
|
92
|
+
}
|
|
93
|
+
export declare function validateAppManifest(value: unknown): AppManifest;
|
|
94
|
+
export declare function matchRoute(pages: PageEntry[], path: string): RouteMatch | undefined;
|
|
95
|
+
export declare function stripPathQuery(path: string): string;
|
|
96
|
+
export declare function resolveInitialRoute(manifest: AppManifest, requestedPath: string): ResolvedRoute | undefined;
|
|
97
|
+
export declare function resolveSchemaUrl(baseURL: string, schemaUrl: string, params: Record<string, string>): string;
|
|
98
|
+
export declare function resolveRoutePath(route: string, params: Record<string, string>): string;
|
|
99
|
+
export declare function resolveLogoUrl(baseURL: string, logoUrl: string): string;
|
|
100
|
+
export declare function pageIdMatches(page: PageEntry, schemaPageId: string): boolean;
|
|
101
|
+
export declare function loadAppManifest(options?: {
|
|
102
|
+
url?: string;
|
|
103
|
+
fetcher?: typeof fetch;
|
|
104
|
+
}): Promise<AppManifest>;
|
|
105
|
+
/** Loads the manifest with its raw 200 bytes (bootstrap integrity, ADR-0035 D6). */
|
|
106
|
+
export declare function loadAppManifestBytes(options?: {
|
|
107
|
+
url?: string;
|
|
108
|
+
fetcher?: typeof fetch;
|
|
109
|
+
}): Promise<{
|
|
110
|
+
manifest: AppManifest;
|
|
111
|
+
bytes: Uint8Array;
|
|
112
|
+
}>;
|
|
113
|
+
export declare function evaluateExpression(expression: string, context: NavigationContext): boolean;
|
|
114
|
+
export declare function isNavigationItemVisible(item: NavLink | NavGroup, context: NavigationContext): boolean;
|
|
115
|
+
/** Normalizes a page identifier for contribution-key matching (trim + lowercase).
|
|
116
|
+
* Added in the R4 zero-conflict upgrade drill as a protocol additive sample. */
|
|
117
|
+
export declare function normalizePageID(id: string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D-COMP component-format fixture adapter (schema-ui-docs v2.7.0).
|
|
3
|
+
* Validates format wire types without coercion.
|
|
4
|
+
*/
|
|
5
|
+
export type ComponentFormat = "currency" | "percent" | "datetime" | string;
|
|
6
|
+
export type FormatResult = {
|
|
7
|
+
ok: true;
|
|
8
|
+
value: unknown;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
code: "COMPONENT_DATA_TYPE_MISMATCH";
|
|
12
|
+
};
|
|
13
|
+
export declare function applyComponentFormat(format: ComponentFormat, value: unknown): FormatResult;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR-0010 query-serialization adapter (schema-ui-docs v2.7.0 fixtures).
|
|
3
|
+
*
|
|
4
|
+
* Merges base URL query with ordered source layers; encodes keys/values per
|
|
5
|
+
* RFC 3986 (unreserved A-Za-z0-9-._~); sorts final keys by Unicode code point.
|
|
6
|
+
*/
|
|
7
|
+
export type QueryScalar = string | number | boolean | null;
|
|
8
|
+
export type QueryPair = [string, unknown];
|
|
9
|
+
export type QuerySource = QueryPair[];
|
|
10
|
+
export type QuerySerializeResult = {
|
|
11
|
+
ok: true;
|
|
12
|
+
url: string;
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
code: "INVALID_BASE_URL_QUERY" | "INVALID_QUERY_KEY" | "INVALID_QUERY_VALUE";
|
|
16
|
+
};
|
|
17
|
+
/** RFC3986 encode: percent-encode everything except unreserved. */
|
|
18
|
+
export declare function encodeRFC3986(value: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* JCS-style number serialization used by query-serialization fixtures.
|
|
21
|
+
* Matches JSON number text for typical finite values (1e+21, 1e-7, 0.000001).
|
|
22
|
+
*/
|
|
23
|
+
export declare function serializeQueryNumber(value: number): string;
|
|
24
|
+
export declare function serializeQuery(baseUrl: string, sources: QuerySource[]): QuerySerializeResult;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* request-construction fixture adapter (schema-ui-docs v2.7.0).
|
|
3
|
+
*
|
|
4
|
+
* Builds HTTP request / navigation / modal outcomes from declarative mappings.
|
|
5
|
+
* Batch kinds are out of scope for MVP callers (Q1); this module still implements
|
|
6
|
+
* non-batch kinds used by stage3 execution.
|
|
7
|
+
*/
|
|
8
|
+
export type RequestConstructionResult = {
|
|
9
|
+
ok: true;
|
|
10
|
+
request?: {
|
|
11
|
+
method: string;
|
|
12
|
+
url: string;
|
|
13
|
+
body: unknown;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
};
|
|
16
|
+
navigation?: {
|
|
17
|
+
url: string;
|
|
18
|
+
};
|
|
19
|
+
modalOpen?: {
|
|
20
|
+
modalId: string;
|
|
21
|
+
};
|
|
22
|
+
resolvedBase?: string;
|
|
23
|
+
/** Batch triggers: selection snapshot after a successful reload (ADR-0022 D2). */
|
|
24
|
+
selectionAfterSuccessReload?: {
|
|
25
|
+
keys: unknown[];
|
|
26
|
+
count: number;
|
|
27
|
+
};
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
code: string;
|
|
31
|
+
path: string;
|
|
32
|
+
};
|
|
33
|
+
/** D3 invariants: scalar keys only, dedupe preserving order, count = keys.length. */
|
|
34
|
+
export declare function normalizeSelection(keys: unknown[]): {
|
|
35
|
+
keys: unknown[];
|
|
36
|
+
count: number;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Run one request-construction fixture case.
|
|
40
|
+
* Batch kinds return a structured error; stage3 excludes them via Q1.
|
|
41
|
+
*/
|
|
42
|
+
export declare function constructRequest(input: Record<string, unknown>): RequestConstructionResult;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe structural validation for vendored page/node/action/reaction
|
|
3
|
+
* schemas. Mirrors `protocol/conformance/schema-validate.ts` but imports the
|
|
4
|
+
* pinned `docs/schemas/*.json` at build time (Vite `@schemas` alias) instead of
|
|
5
|
+
* reading them from disk, so the runtime loader can enforce D-VAL in the
|
|
6
|
+
* browser. The schema set is identical, so runtime and test-time validators
|
|
7
|
+
* stay aligned and neither redefines upstream node/page semantics.
|
|
8
|
+
*/
|
|
9
|
+
export type RuntimeSchemaKind = "node" | "page" | "action" | "reaction";
|
|
10
|
+
export interface RuntimeSchemaValidationResult {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
errors: Array<{
|
|
13
|
+
path: string;
|
|
14
|
+
message: string;
|
|
15
|
+
keyword?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Structural validation of a fetched page document against the pinned page/node
|
|
20
|
+
* schemas. `ok: false` means the document must fail closed and never reach the
|
|
21
|
+
* renderer.
|
|
22
|
+
*/
|
|
23
|
+
export declare function validatePageDocument(document: unknown): RuntimeSchemaValidationResult;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upload orchestration (schema-ui-docs@2.7.0 · docs/07-actions-contract.md §7
|
|
3
|
+
* + ADR-0012). I-PROTO-FULL-001 D-UPLOAD include.
|
|
4
|
+
*
|
|
5
|
+
* Client contract:
|
|
6
|
+
* - one multipart request per file (fieldName default "file"; method POST
|
|
7
|
+
* default, PUT allowed)
|
|
8
|
+
* - constraints (multiple / maxSize / accept) are checked BEFORE any
|
|
9
|
+
* request; any violation rejects the whole batch atomically
|
|
10
|
+
* - accept tokens: ".ext" matches the file extension case-insensitively,
|
|
11
|
+
* "type/*" matches the MIME main type, other tokens match MIME exactly
|
|
12
|
+
* (case-insensitive)
|
|
13
|
+
* - serial uploads in selection order; any failure stops the batch and no
|
|
14
|
+
* partial field value is committed
|
|
15
|
+
* - field value: url (priority) or id; multiple → array in selection order
|
|
16
|
+
* - retryPolicy idempotent → Idempotency-Key header "{invocationId}:{index}"
|
|
17
|
+
*/
|
|
18
|
+
export interface UploadFile {
|
|
19
|
+
name: string;
|
|
20
|
+
type: string;
|
|
21
|
+
size: number;
|
|
22
|
+
contentId: string;
|
|
23
|
+
}
|
|
24
|
+
/** Real-transport file: an UploadFile plus the bytes to send (browser File). */
|
|
25
|
+
export interface UploadableFile extends UploadFile {
|
|
26
|
+
blob: Blob;
|
|
27
|
+
}
|
|
28
|
+
export interface UploadActionResult {
|
|
29
|
+
url?: string;
|
|
30
|
+
id?: string;
|
|
31
|
+
method?: string;
|
|
32
|
+
fieldName?: string;
|
|
33
|
+
accept?: string;
|
|
34
|
+
maxSize?: number;
|
|
35
|
+
multiple?: boolean;
|
|
36
|
+
retryPolicy?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface UploadRequestPart {
|
|
39
|
+
name: string;
|
|
40
|
+
fileName: string;
|
|
41
|
+
contentId: string;
|
|
42
|
+
}
|
|
43
|
+
export interface UploadRequest {
|
|
44
|
+
method: string;
|
|
45
|
+
url: string;
|
|
46
|
+
part: UploadRequestPart;
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
export type UploadResult = {
|
|
50
|
+
type: "success";
|
|
51
|
+
response: {
|
|
52
|
+
url?: string;
|
|
53
|
+
id?: string;
|
|
54
|
+
};
|
|
55
|
+
} | {
|
|
56
|
+
type: "failure";
|
|
57
|
+
status?: number;
|
|
58
|
+
};
|
|
59
|
+
export interface UploadBatchResultOk {
|
|
60
|
+
ok: true;
|
|
61
|
+
requests: UploadRequest[];
|
|
62
|
+
fieldValue: string | string[] | null;
|
|
63
|
+
}
|
|
64
|
+
export interface UploadBatchResultError {
|
|
65
|
+
ok: false;
|
|
66
|
+
code: string;
|
|
67
|
+
fileIndex: number;
|
|
68
|
+
requests: UploadRequest[];
|
|
69
|
+
fieldValue: null;
|
|
70
|
+
}
|
|
71
|
+
export type UploadBatchResult = UploadBatchResultOk | UploadBatchResultError;
|
|
72
|
+
/** accept token matching (ADR-0012 D2): ".ext", "type/*", exact MIME. */
|
|
73
|
+
export declare function acceptTokenMatches(token: string, file: UploadFile): boolean;
|
|
74
|
+
/** Client-side constraints (ADR-0012 D2); returns the first violation. */
|
|
75
|
+
export declare function validateUploadSelection(action: UploadActionResult, files: UploadFile[]): {
|
|
76
|
+
ok: true;
|
|
77
|
+
} | {
|
|
78
|
+
ok: false;
|
|
79
|
+
code: string;
|
|
80
|
+
fileIndex: number;
|
|
81
|
+
};
|
|
82
|
+
/** Builds the per-file multipart request descriptors (pre-request). */
|
|
83
|
+
export declare function buildUploadRequests(action: UploadActionResult, files: UploadFile[], invocationId?: string): UploadBatchResultError | {
|
|
84
|
+
ok: true;
|
|
85
|
+
requests: UploadRequest[];
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Fixture-driven orchestration: replays pre-recorded transport results
|
|
89
|
+
* (`conformance/fixtures/uploads/cases.json` contract).
|
|
90
|
+
*/
|
|
91
|
+
export declare function runUploadBatch(input: {
|
|
92
|
+
action: UploadActionResult;
|
|
93
|
+
files: UploadFile[];
|
|
94
|
+
results?: UploadResult[];
|
|
95
|
+
invocationId?: string;
|
|
96
|
+
}): UploadBatchResult;
|
|
97
|
+
/**
|
|
98
|
+
* Real transport orchestration used by the Renderer's upload control: the
|
|
99
|
+
* same constraints and request shape, executed against a live fetch with
|
|
100
|
+
* multipart FormData carrying the actual file bytes (ADR-0012 D4).
|
|
101
|
+
*/
|
|
102
|
+
export declare function uploadFilesWithFetch(action: UploadActionResult, files: UploadableFile[], fetcher: typeof fetch, invocationId?: string): Promise<UploadBatchResult>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime page-schema loader for the schema-driven render path (R1 · GOAL-002).
|
|
3
|
+
*
|
|
4
|
+
* Resolves a manifest `PageEntry`'s schemaUrl (with route-parameter expansion),
|
|
5
|
+
* fetches the page document, forces structural validation (D-VAL) against the
|
|
6
|
+
* pinned page/node schemas, and returns the parsed document or throws a unified
|
|
7
|
+
* `PageSchemaError`. The fetcher is injectable so tests exercise network,
|
|
8
|
+
* parse, and validation failure paths without a server. This module does NOT
|
|
9
|
+
* switch the app's default render branch (that is GOAL-003).
|
|
10
|
+
*/
|
|
11
|
+
import type { PageEntry } from "@schema-ui/protocol/app-manifest";
|
|
12
|
+
export type PageSchemaErrorCode = "PAGE_LOAD_FAILED" | "PAGE_NOT_FOUND" | "PAGE_PARSE_FAILED" | "PAGE_SCHEMA_INVALID" | "PAGE_ID_MISMATCH";
|
|
13
|
+
export interface PageSchemaValidationIssue {
|
|
14
|
+
path: string;
|
|
15
|
+
message: string;
|
|
16
|
+
keyword?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Unified, observable error for the schema loading + validation pipeline. */
|
|
19
|
+
export declare class PageSchemaError extends Error {
|
|
20
|
+
readonly code: PageSchemaErrorCode;
|
|
21
|
+
readonly url: string;
|
|
22
|
+
readonly issues?: PageSchemaValidationIssue[];
|
|
23
|
+
constructor(code: PageSchemaErrorCode, url: string, message: string, issues?: PageSchemaValidationIssue[]);
|
|
24
|
+
}
|
|
25
|
+
export interface LoadPageOptions {
|
|
26
|
+
/** Origin/base used to resolve relative schemaUrl values. Defaults to `location.origin`. */
|
|
27
|
+
baseURL?: string;
|
|
28
|
+
/** Injectable fetch; defaults to `globalThis.fetch`. */
|
|
29
|
+
fetcher?: typeof fetch;
|
|
30
|
+
/**
|
|
31
|
+
* Optional in-memory document cache keyed by resolved schemaUrl (owned by
|
|
32
|
+
* the App shell). A hit skips the network fetch AND the D-VAL structural
|
|
33
|
+
* validation (the stored document was already validated at load time, and
|
|
34
|
+
* its meta.pageId was verified against the manifest page). Intentionally
|
|
35
|
+
* opt-in: tests and callers that must observe every load pass none.
|
|
36
|
+
*/
|
|
37
|
+
cache?: Map<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Load and validate a page document for the given manifest page and route
|
|
41
|
+
* params. Resolves the schemaUrl (expanding `{param}` placeholders), fetches
|
|
42
|
+
* it, enforces structural validation, and verifies the document's `meta.pageId`
|
|
43
|
+
* matches the manifest page. Returns the parsed page document on success.
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadPageDocument(page: PageEntry, params: Record<string, string>, options?: LoadPageOptions): Promise<unknown>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
|
-
import type { RenderCustomNode, RenderNode } from "
|
|
2
|
+
import type { RenderCustomNode, RenderNode } from "@schema-ui/renderer/renderer/render.types";
|
|
3
3
|
export interface CustomComponentProps {
|
|
4
4
|
node: RenderCustomNode;
|
|
5
5
|
context: Record<string, unknown>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type MessageParams } from "
|
|
3
|
-
import type { UploadableFile } from "
|
|
4
|
-
import { type FormControlField } from "
|
|
2
|
+
import { type MessageParams } from "@schema-ui/lib/i18n/catalog";
|
|
3
|
+
import type { UploadableFile } from "@schema-ui/protocol/conformance/upload-orchestration";
|
|
4
|
+
import { type FormControlField } from "@schema-ui/renderer/renderer/form-controls.types";
|
|
5
5
|
export interface FormControlsProps {
|
|
6
6
|
fields: FormControlField[];
|
|
7
7
|
values: Record<string, unknown>;
|
package/renderer/render.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type ComponentType, type ReactNode } from "react";
|
|
2
|
-
import { type UploadableFile } from "
|
|
3
|
-
import { type FormControlField } from "
|
|
4
|
-
import { type ResourceList, type ResourceQuery } from "
|
|
5
|
-
import { type RenderActionButtonNode, type RenderChartNode, type RenderFormNode, type RenderPageDocument, type RenderStatCardNode, type RenderTableNode } from "
|
|
2
|
+
import { type UploadableFile } from "@schema-ui/protocol/conformance/upload-orchestration";
|
|
3
|
+
import { type FormControlField } from "@schema-ui/renderer/renderer/form-controls.types";
|
|
4
|
+
import { type ResourceList, type ResourceQuery } from "@schema-ui/renderer/renderer/resource";
|
|
5
|
+
import { type RenderActionButtonNode, type RenderChartNode, type RenderFormNode, type RenderPageDocument, type RenderStatCardNode, type RenderTableNode } from "@schema-ui/renderer/renderer/render.types";
|
|
6
6
|
/**
|
|
7
7
|
* R5 D-COMP minimal Renderer (resolve R4 F-002) + S4 Schema CRUD (GOAL-007).
|
|
8
8
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type FormControlField, type FormControlGateError } from "
|
|
2
|
-
import { type FormControlStateMap, type ReactionError } from "
|
|
1
|
+
import { type FormControlField, type FormControlGateError } from "@schema-ui/renderer/renderer/form-controls.types";
|
|
2
|
+
import { type FormControlStateMap, type ReactionError } from "@schema-ui/renderer/renderer/reactions";
|
|
3
3
|
/**
|
|
4
4
|
* D-COMP page renderer (frozen §5 whitelist + I-PROTO-FULL-001 full registry
|
|
5
5
|
* surface; resolve R4 F-002).
|
package/renderer/resource.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SortOrder } from "
|
|
1
|
+
import type { SortOrder } from "@schema-ui/renderer/components/data-table";
|
|
2
2
|
export declare const DEFAULT_PAGE_SIZE = 10;
|
|
3
3
|
/**
|
|
4
4
|
* Frozen list-endpoint rule (I-010-001 v0.2.0 · A-001 F-001): `table.props.dataSource`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type ResourceItem } from "
|
|
2
|
-
import type { RenderTableNode } from "
|
|
1
|
+
import { type ResourceItem } from "@schema-ui/renderer/renderer/resource";
|
|
2
|
+
import type { RenderTableNode } from "@schema-ui/renderer/renderer/render.types";
|
|
3
3
|
/**
|
|
4
4
|
* Default schema-driven table surface (R1 · GOAL-004 / D-004) + S4 CRUD wiring
|
|
5
5
|
* + A-002 generic adapter (GOAL-010 S3).
|