@runlot/auth 0.1.0-rc.17 → 0.1.0-rc.170
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/dist/client.d.ts +123 -16
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +126 -27
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +154 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +31 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -11
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +14 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +37 -0
- package/dist/react.js.map +1 -0
- package/dist/ui.d.ts +25 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +144 -0
- package/dist/ui.js.map +1 -0
- package/package.json +27 -4
package/dist/client.d.ts
CHANGED
|
@@ -1,37 +1,144 @@
|
|
|
1
1
|
import { type User } from "./index.js";
|
|
2
|
+
import { type AuthApiErrorCode } from "./errors.js";
|
|
3
|
+
export { AUTH_API_ERROR_CODES, AUTH_ERROR_CODES, authErrorMessage, isAuthApiErrorCode, isAuthErrorCode, parseAuthError, stripAuthError, withoutAuthError, } from "./errors.js";
|
|
4
|
+
export type { AuthApiErrorCode, AuthErrorCode, AuthLang, ParsedAuthError } from "./errors.js";
|
|
2
5
|
export interface NextOpts {
|
|
3
6
|
next?: string;
|
|
4
7
|
}
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* A provider id: `"github"`, `"google"`, `"runlot"`, or the id of one of the
|
|
10
|
+
* project's OIDC providers (docs/auth-access-unify.md §5).
|
|
11
|
+
*/
|
|
12
|
+
export type AuthProvider = "github" | "google" | "runlot" | (string & {});
|
|
13
|
+
/** A Runlot ID method the app can deep-link to (docs/custom-sign-in.md §3.1). */
|
|
14
|
+
export type RunlotMethod = "password" | "google" | "github" | "passkey";
|
|
15
|
+
/** Options only `signInUrl("runlot", …)` takes (§12.1). */
|
|
16
|
+
export interface RunlotSignInOpts extends NextOpts {
|
|
17
|
+
/** Skip the chooser and go straight to this method. */
|
|
18
|
+
idp?: RunlotMethod;
|
|
19
|
+
/** Prefills the email on the Runlot form; never trusted for anything. */
|
|
20
|
+
loginHint?: string;
|
|
21
|
+
/** Where errors return to instead of `next`. Same-origin path. */
|
|
22
|
+
errorNext?: string;
|
|
23
|
+
/** `"none"`: sign in with no page if already consented, else `login_required`. */
|
|
24
|
+
prompt?: "none";
|
|
25
|
+
}
|
|
26
|
+
/** The sign-in page. Given a provider, it goes straight to that provider's
|
|
27
|
+
* authorize. `idp`, `loginHint`, `errorNext` and `prompt` exist only for
|
|
28
|
+
* `"runlot"`. */
|
|
29
|
+
export declare function signInUrl(provider: "runlot", opts?: RunlotSignInOpts): string;
|
|
8
30
|
export declare function signInUrl(provider?: AuthProvider, opts?: NextOpts): string;
|
|
9
31
|
/**
|
|
10
|
-
*
|
|
32
|
+
* The sign-up page.
|
|
11
33
|
*
|
|
12
|
-
*
|
|
13
|
-
* (docs/auth.md §5 — `GET sign-in/{provider}
|
|
14
|
-
*
|
|
15
|
-
*
|
|
34
|
+
* **There is no provider argument.** For social logins, signing up and signing
|
|
35
|
+
* in are one path (docs/auth.md §5 — there is only `GET sign-in/{provider}`,
|
|
36
|
+
* and the identity upsert rides the linking rules of §6), so a URL like
|
|
37
|
+
* `signUpUrl("github")` does not exist on the server. To sign somebody up with
|
|
38
|
+
* a social provider, use `signInUrl(provider)`.
|
|
16
39
|
*/
|
|
17
40
|
export declare function signUpUrl(opts?: NextOpts): string;
|
|
18
41
|
/**
|
|
19
|
-
*
|
|
42
|
+
* Sign-out.
|
|
20
43
|
*
|
|
21
|
-
*
|
|
22
|
-
* action={signOutUrl()}>`
|
|
23
|
-
* `<img>`
|
|
44
|
+
* **It is a POST target, not a link** (docs/auth.md §5). Use it as `<form
|
|
45
|
+
* method="post" action={signOutUrl()}>` — if a GET could sign somebody out, a
|
|
46
|
+
* single `<img>` embedded in somebody else's page could throw the user out.
|
|
24
47
|
*/
|
|
25
48
|
export declare function signOutUrl(opts?: NextOpts): string;
|
|
26
49
|
/**
|
|
27
|
-
*
|
|
50
|
+
* One sign-in provider the project has turned on. `kind` is `builtin` for the
|
|
51
|
+
* project's own GitHub or Google app, `oidc` for a generic OpenID Connect
|
|
52
|
+
* provider, `runlot` for "Continue with Runlot". `name` is the button label and
|
|
53
|
+
* `icon` an icon URL (empty when there is none).
|
|
54
|
+
*/
|
|
55
|
+
export interface AuthProviderInfo {
|
|
56
|
+
id: string;
|
|
57
|
+
kind: "builtin" | "oidc" | "runlot";
|
|
58
|
+
name: string;
|
|
59
|
+
icon: string;
|
|
60
|
+
/** For the `runlot` row only: the Runlot ID methods the chooser offers,
|
|
61
|
+
* each usable as `signInUrl("runlot", { idp })` (§12.3). */
|
|
62
|
+
methods?: RunlotMethod[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* What the sign-in page needs to know about the project, from
|
|
66
|
+
* `GET /__runlot/auth/config`. No secrets: what is here is what the hosted
|
|
67
|
+
* page already shows.
|
|
68
|
+
*/
|
|
69
|
+
export interface AuthConfig {
|
|
70
|
+
brand: {
|
|
71
|
+
name: string;
|
|
72
|
+
logo: string;
|
|
73
|
+
color: string;
|
|
74
|
+
};
|
|
75
|
+
/** Only the providers that are turned on — draw a button for each and no other. */
|
|
76
|
+
providers: AuthProviderInfo[];
|
|
77
|
+
password: boolean;
|
|
78
|
+
magicLink: boolean;
|
|
79
|
+
/** Off means only existing users can sign in, by any method including social. */
|
|
80
|
+
allowSignup: boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The project's sign-in configuration, for a custom login page.
|
|
84
|
+
*
|
|
85
|
+
* **It throws on anything but a 200** — unlike `session()`, there is no answer
|
|
86
|
+
* that means "nothing": a 404 is a project whose login is off, and a page that
|
|
87
|
+
* drew zero buttons from it would hide that.
|
|
88
|
+
*/
|
|
89
|
+
export declare function config(fetchImpl?: typeof fetch): Promise<AuthConfig>;
|
|
90
|
+
/** The turned-on providers, so a custom page never draws a button that answers `provider_not_configured`. */
|
|
91
|
+
export declare function providers(fetchImpl?: typeof fetch): Promise<AuthProviderInfo[]>;
|
|
92
|
+
/**
|
|
93
|
+
* Who is signed in right now. This is what a client-rendered app calls while
|
|
94
|
+
* booting.
|
|
28
95
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
96
|
+
* Anything other than a 200 gives `{ user: null }` — being signed out is an
|
|
97
|
+
* answer, not an exception (a 401 is an answer too), and throwing would turn
|
|
98
|
+
* every page into a try/catch.
|
|
31
99
|
*
|
|
32
|
-
* `fetchImpl`
|
|
100
|
+
* `fetchImpl` is the hole through which the tests inject a fake.
|
|
33
101
|
*/
|
|
34
102
|
export declare function session(fetchImpl?: typeof fetch): Promise<{
|
|
35
103
|
user: User | null;
|
|
36
104
|
}>;
|
|
105
|
+
export type AuthApiResult<T> = ({
|
|
106
|
+
ok: true;
|
|
107
|
+
} & T) | {
|
|
108
|
+
ok: false;
|
|
109
|
+
error: AuthApiErrorCode;
|
|
110
|
+
status: number;
|
|
111
|
+
retryAfter: number | null;
|
|
112
|
+
};
|
|
113
|
+
/** Project-local password sign-in from the app's own form. Sets the session cookie on success. */
|
|
114
|
+
export declare function signInWithPassword(input: {
|
|
115
|
+
email: string;
|
|
116
|
+
password: string;
|
|
117
|
+
}, fetchImpl?: typeof fetch): Promise<AuthApiResult<{
|
|
118
|
+
user: User;
|
|
119
|
+
}>>;
|
|
120
|
+
/** Project-local sign-up. `check_email` means a verification mail was (or would have been) sent. */
|
|
121
|
+
export declare function signUp(input: {
|
|
122
|
+
email: string;
|
|
123
|
+
password: string;
|
|
124
|
+
name?: string;
|
|
125
|
+
}, fetchImpl?: typeof fetch): Promise<AuthApiResult<{
|
|
126
|
+
user?: User;
|
|
127
|
+
}>>;
|
|
128
|
+
/** Sends a reset mail. Always succeeds (202) for any address — no enumeration. */
|
|
129
|
+
export declare function requestReset(input: {
|
|
130
|
+
email: string;
|
|
131
|
+
}, fetchImpl?: typeof fetch): Promise<AuthApiResult<object>>;
|
|
132
|
+
/** Sets a new password with the token from the reset mail. */
|
|
133
|
+
export declare function confirmReset(input: {
|
|
134
|
+
token: string;
|
|
135
|
+
password: string;
|
|
136
|
+
}, fetchImpl?: typeof fetch): Promise<AuthApiResult<{
|
|
137
|
+
user?: User;
|
|
138
|
+
}>>;
|
|
139
|
+
/** Sends a magic sign-in link. Always 202. `next` is filtered like every other `next`. */
|
|
140
|
+
export declare function sendMagicLink(input: {
|
|
141
|
+
email: string;
|
|
142
|
+
next?: string;
|
|
143
|
+
}, fetchImpl?: typeof fetch): Promise<AuthApiResult<object>>;
|
|
37
144
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Browser helpers (docs/auth.md §10). Building links and one `GET /session` is
|
|
2
|
+
// all there is, so it has zero dependencies, and it does not claim to know
|
|
3
|
+
// again what the server already knows.
|
|
3
4
|
import { AUTH_BASE } from "./index.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
//
|
|
5
|
+
import { isAuthApiErrorCode } from "./errors.js";
|
|
6
|
+
export { AUTH_API_ERROR_CODES, AUTH_ERROR_CODES, authErrorMessage, isAuthApiErrorCode, isAuthErrorCode, parseAuthError, stripAuthError, withoutAuthError, } from "./errors.js";
|
|
7
|
+
// `next` lets through only a same-origin relative path — the same rule as
|
|
8
|
+
// `safeNext` in docs/auth.md §5. It has to start with `/` and must not start
|
|
9
|
+
// with `//`: the browser reads `//evil.example` as an absolute URL, which makes
|
|
10
|
+
// it an open redirect.
|
|
7
11
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
12
|
+
// Filtering here is a convenience, not a security boundary. A link can also be
|
|
13
|
+
// made by hand, so the server runs the same check again.
|
|
10
14
|
function safeNext(next) {
|
|
11
15
|
if (next === undefined || next === "")
|
|
12
16
|
return null;
|
|
@@ -16,43 +20,84 @@ function safeNext(next) {
|
|
|
16
20
|
return null;
|
|
17
21
|
return next;
|
|
18
22
|
}
|
|
19
|
-
function authUrl(path, provider,
|
|
23
|
+
function authUrl(path, provider, params) {
|
|
20
24
|
const base = `${AUTH_BASE}/${path}${provider ? `/${provider}` : ""}`;
|
|
21
|
-
const
|
|
22
|
-
|
|
25
|
+
const q = Object.entries(params ?? {})
|
|
26
|
+
.filter((e) => e[1] !== null)
|
|
27
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
|
28
|
+
.join("&");
|
|
29
|
+
return q === "" ? base : `${base}?${q}`;
|
|
30
|
+
}
|
|
31
|
+
const METHODS = ["password", "google", "github", "passkey"];
|
|
32
|
+
// Same shape as the worker's `normEmail` filter — anything else is dropped,
|
|
33
|
+
// which is what the server would do anyway.
|
|
34
|
+
function safeHint(v) {
|
|
35
|
+
if (v === undefined)
|
|
36
|
+
return null;
|
|
37
|
+
const s = v.trim();
|
|
38
|
+
if (s === "" || s.length > 320 || !/^[^\s@]+@[^\s@]+$/.test(s))
|
|
39
|
+
return null;
|
|
40
|
+
return s;
|
|
23
41
|
}
|
|
24
|
-
/** 로그인 페이지. 프로바이더를 주면 그 프로바이더의 authorize 로 바로 간다. */
|
|
25
42
|
export function signInUrl(provider, opts) {
|
|
26
|
-
|
|
43
|
+
const params = { next: safeNext(opts?.next) };
|
|
44
|
+
if (provider === "runlot" && opts) {
|
|
45
|
+
params.idp = opts.idp !== undefined && METHODS.includes(opts.idp) ? opts.idp : null;
|
|
46
|
+
params.login_hint = safeHint(opts.loginHint);
|
|
47
|
+
params.error_next = safeNext(opts.errorNext);
|
|
48
|
+
params.prompt = opts.prompt === "none" ? "none" : null;
|
|
49
|
+
}
|
|
50
|
+
return authUrl("sign-in", provider, params);
|
|
27
51
|
}
|
|
28
52
|
/**
|
|
29
|
-
*
|
|
53
|
+
* The sign-up page.
|
|
30
54
|
*
|
|
31
|
-
*
|
|
32
|
-
* (docs/auth.md §5 — `GET sign-in/{provider}
|
|
33
|
-
*
|
|
34
|
-
*
|
|
55
|
+
* **There is no provider argument.** For social logins, signing up and signing
|
|
56
|
+
* in are one path (docs/auth.md §5 — there is only `GET sign-in/{provider}`,
|
|
57
|
+
* and the identity upsert rides the linking rules of §6), so a URL like
|
|
58
|
+
* `signUpUrl("github")` does not exist on the server. To sign somebody up with
|
|
59
|
+
* a social provider, use `signInUrl(provider)`.
|
|
35
60
|
*/
|
|
36
61
|
export function signUpUrl(opts) {
|
|
37
|
-
return authUrl("sign-up", undefined, opts);
|
|
62
|
+
return authUrl("sign-up", undefined, { next: safeNext(opts?.next) });
|
|
38
63
|
}
|
|
39
64
|
/**
|
|
40
|
-
*
|
|
65
|
+
* Sign-out.
|
|
41
66
|
*
|
|
42
|
-
*
|
|
43
|
-
* action={signOutUrl()}>`
|
|
44
|
-
* `<img>`
|
|
67
|
+
* **It is a POST target, not a link** (docs/auth.md §5). Use it as `<form
|
|
68
|
+
* method="post" action={signOutUrl()}>` — if a GET could sign somebody out, a
|
|
69
|
+
* single `<img>` embedded in somebody else's page could throw the user out.
|
|
45
70
|
*/
|
|
46
71
|
export function signOutUrl(opts) {
|
|
47
|
-
return authUrl("sign-out", undefined, opts);
|
|
72
|
+
return authUrl("sign-out", undefined, { next: safeNext(opts?.next) });
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The project's sign-in configuration, for a custom login page.
|
|
76
|
+
*
|
|
77
|
+
* **It throws on anything but a 200** — unlike `session()`, there is no answer
|
|
78
|
+
* that means "nothing": a 404 is a project whose login is off, and a page that
|
|
79
|
+
* drew zero buttons from it would hide that.
|
|
80
|
+
*/
|
|
81
|
+
export async function config(fetchImpl) {
|
|
82
|
+
const f = fetchImpl ?? fetch;
|
|
83
|
+
const res = await f(`${AUTH_BASE}/config`, { credentials: "same-origin" });
|
|
84
|
+
if (!res.ok)
|
|
85
|
+
throw new Error(`GET ${AUTH_BASE}/config → ${res.status}`);
|
|
86
|
+
return (await res.json());
|
|
87
|
+
}
|
|
88
|
+
/** The turned-on providers, so a custom page never draws a button that answers `provider_not_configured`. */
|
|
89
|
+
export async function providers(fetchImpl) {
|
|
90
|
+
return (await config(fetchImpl)).providers;
|
|
48
91
|
}
|
|
49
92
|
/**
|
|
50
|
-
*
|
|
93
|
+
* Who is signed in right now. This is what a client-rendered app calls while
|
|
94
|
+
* booting.
|
|
51
95
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
96
|
+
* Anything other than a 200 gives `{ user: null }` — being signed out is an
|
|
97
|
+
* answer, not an exception (a 401 is an answer too), and throwing would turn
|
|
98
|
+
* every page into a try/catch.
|
|
54
99
|
*
|
|
55
|
-
* `fetchImpl`
|
|
100
|
+
* `fetchImpl` is the hole through which the tests inject a fake.
|
|
56
101
|
*/
|
|
57
102
|
export async function session(fetchImpl) {
|
|
58
103
|
const f = fetchImpl ?? fetch;
|
|
@@ -61,4 +106,58 @@ export async function session(fetchImpl) {
|
|
|
61
106
|
return { user: null };
|
|
62
107
|
return (await res.json());
|
|
63
108
|
}
|
|
109
|
+
async function api(path, body, fetchImpl) {
|
|
110
|
+
const f = fetchImpl ?? fetch;
|
|
111
|
+
let res;
|
|
112
|
+
try {
|
|
113
|
+
res = await f(`${AUTH_BASE}/api/${path}`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
credentials: "same-origin",
|
|
116
|
+
headers: { "content-type": "application/json" },
|
|
117
|
+
body: JSON.stringify(body),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return { ok: false, error: "server_error", status: 0, retryAfter: null };
|
|
122
|
+
}
|
|
123
|
+
let json = {};
|
|
124
|
+
try {
|
|
125
|
+
const text = await res.text();
|
|
126
|
+
if (text !== "")
|
|
127
|
+
json = JSON.parse(text);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
json = {};
|
|
131
|
+
}
|
|
132
|
+
if (res.ok)
|
|
133
|
+
return { ok: true, ...json };
|
|
134
|
+
const ra = Number(res.headers.get("retry-after"));
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
error: isAuthApiErrorCode(json.error) ? json.error : res.status === 429 ? "rate_limited" : "server_error",
|
|
138
|
+
status: res.status,
|
|
139
|
+
retryAfter: Number.isFinite(ra) && ra > 0 ? ra : null,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** Project-local password sign-in from the app's own form. Sets the session cookie on success. */
|
|
143
|
+
export function signInWithPassword(input, fetchImpl) {
|
|
144
|
+
return api("password/sign-in", input, fetchImpl);
|
|
145
|
+
}
|
|
146
|
+
/** Project-local sign-up. `check_email` means a verification mail was (or would have been) sent. */
|
|
147
|
+
export function signUp(input, fetchImpl) {
|
|
148
|
+
return api("password/sign-up", input, fetchImpl);
|
|
149
|
+
}
|
|
150
|
+
/** Sends a reset mail. Always succeeds (202) for any address — no enumeration. */
|
|
151
|
+
export function requestReset(input, fetchImpl) {
|
|
152
|
+
return api("password/reset", input, fetchImpl);
|
|
153
|
+
}
|
|
154
|
+
/** Sets a new password with the token from the reset mail. */
|
|
155
|
+
export function confirmReset(input, fetchImpl) {
|
|
156
|
+
return api("password/reset/confirm", input, fetchImpl);
|
|
157
|
+
}
|
|
158
|
+
/** Sends a magic sign-in link. Always 202. `next` is filtered like every other `next`. */
|
|
159
|
+
export function sendMagicLink(input, fetchImpl) {
|
|
160
|
+
const next = safeNext(input.next);
|
|
161
|
+
return api("magic-link", next === null ? { email: input.email } : { email: input.email, next }, fetchImpl);
|
|
162
|
+
}
|
|
64
163
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Codes the broker or the auth worker returns to the app on a redirect. */
|
|
2
|
+
export declare const AUTH_ERROR_CODES: readonly ["cancelled", "signup_closed", "email_unverified", "provider_unavailable", "rate_limited", "expired", "consent_denied", "login_required", "account_exists", "server_error"];
|
|
3
|
+
/** Extra codes only the project-local JSON API answers with (§12.6). */
|
|
4
|
+
export declare const AUTH_API_ERROR_CODES: readonly ["invalid_credentials", "weak_password", "check_email", "csrf"];
|
|
5
|
+
export type AuthErrorCode = (typeof AUTH_ERROR_CODES)[number];
|
|
6
|
+
export type AuthApiErrorCode = AuthErrorCode | (typeof AUTH_API_ERROR_CODES)[number];
|
|
7
|
+
export type AuthLang = "ko" | "en" | "ja";
|
|
8
|
+
export declare function isAuthErrorCode(v: unknown): v is AuthErrorCode;
|
|
9
|
+
export declare function isAuthApiErrorCode(v: unknown): v is AuthApiErrorCode;
|
|
10
|
+
/** The default message for a code. Korean when `lang` is omitted or unknown. */
|
|
11
|
+
export declare function authErrorMessage(code: AuthApiErrorCode, lang?: AuthLang | (string & {})): string;
|
|
12
|
+
export interface ParsedAuthError {
|
|
13
|
+
code: AuthErrorCode;
|
|
14
|
+
/** The start id support looks up; `null` when absent. */
|
|
15
|
+
request: string | null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Reads `auth_error` / `auth_request` from a URL (or `location`). `null` when
|
|
19
|
+
* there is no error. An unknown code is reported as `server_error`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseAuthError(url: string | URL | {
|
|
22
|
+
href: string;
|
|
23
|
+
}): ParsedAuthError | null;
|
|
24
|
+
/** `url` without `auth_error` and `auth_request`, as a path+query+hash string. */
|
|
25
|
+
export declare function withoutAuthError(url: string | URL | {
|
|
26
|
+
href: string;
|
|
27
|
+
}): string;
|
|
28
|
+
/**
|
|
29
|
+
* Removes `auth_error` / `auth_request` from the address bar with
|
|
30
|
+
* `history.replaceState`, so a reload does not show the error again. A no-op
|
|
31
|
+
* outside a browser or when there is nothing to strip.
|
|
32
|
+
*/
|
|
33
|
+
export declare function stripAuthError(): void;
|
|
34
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAQA,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,sLAWnB,CAAC;AAEX,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,0EAA2E,CAAC;AAE7G,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC9D,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AACrF,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AA2E1C,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAE9D;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,gBAAgB,CAEpE;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,CAGhG;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,yDAAyD;IACzD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAYD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,eAAe,GAAG,IAAI,CAM3F;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAO7E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAUrC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Structured sign-in errors (docs/custom-sign-in.md §3.3, §12.2, §12.6).
|
|
2
|
+
//
|
|
3
|
+
// Every non-success return of `/__runlot/auth/sign-in/runlot` comes back to the
|
|
4
|
+
// app's `next` (or `error_next`) as `?auth_error=<code>&auth_request=<start id>`.
|
|
5
|
+
// The codes are a closed enum: an unknown value is reported as `server_error`
|
|
6
|
+
// rather than passed through, so the app never renders a string it did not
|
|
7
|
+
// expect. The messages are the product surface (ko/en/ja, Korean-primary).
|
|
8
|
+
/** Codes the broker or the auth worker returns to the app on a redirect. */
|
|
9
|
+
export const AUTH_ERROR_CODES = [
|
|
10
|
+
"cancelled",
|
|
11
|
+
"signup_closed",
|
|
12
|
+
"email_unverified",
|
|
13
|
+
"provider_unavailable",
|
|
14
|
+
"rate_limited",
|
|
15
|
+
"expired",
|
|
16
|
+
"consent_denied",
|
|
17
|
+
"login_required",
|
|
18
|
+
"account_exists",
|
|
19
|
+
"server_error",
|
|
20
|
+
];
|
|
21
|
+
/** Extra codes only the project-local JSON API answers with (§12.6). */
|
|
22
|
+
export const AUTH_API_ERROR_CODES = ["invalid_credentials", "weak_password", "check_email", "csrf"];
|
|
23
|
+
const MESSAGES = {
|
|
24
|
+
cancelled: {
|
|
25
|
+
ko: "로그인이 취소되었습니다.",
|
|
26
|
+
en: "Sign-in was cancelled.",
|
|
27
|
+
ja: "ログインがキャンセルされました。",
|
|
28
|
+
},
|
|
29
|
+
signup_closed: {
|
|
30
|
+
ko: "지금은 새 계정을 만들 수 없습니다. 기존 계정으로 로그인해 주세요.",
|
|
31
|
+
en: "New sign-ups are closed. Please sign in with an existing account.",
|
|
32
|
+
ja: "現在、新規登録は受け付けていません。既存のアカウントでログインしてください。",
|
|
33
|
+
},
|
|
34
|
+
email_unverified: {
|
|
35
|
+
ko: "이메일 인증이 필요합니다. 인증된 이메일로 다시 시도해 주세요.",
|
|
36
|
+
en: "Your email address is not verified. Verify it and try again.",
|
|
37
|
+
ja: "メールアドレスが未確認です。確認してから再度お試しください。",
|
|
38
|
+
},
|
|
39
|
+
provider_unavailable: {
|
|
40
|
+
ko: "이 로그인 방법은 지금 사용할 수 없습니다. 다른 방법을 선택해 주세요.",
|
|
41
|
+
en: "This sign-in method is not available right now. Please choose another.",
|
|
42
|
+
ja: "このログイン方法は現在利用できません。別の方法を選んでください。",
|
|
43
|
+
},
|
|
44
|
+
rate_limited: {
|
|
45
|
+
ko: "시도가 너무 많습니다. 잠시 후 다시 시도해 주세요.",
|
|
46
|
+
en: "Too many attempts. Please try again in a moment.",
|
|
47
|
+
ja: "試行回数が多すぎます。しばらくしてから再度お試しください。",
|
|
48
|
+
},
|
|
49
|
+
expired: {
|
|
50
|
+
ko: "로그인 요청이 만료되었습니다. 처음부터 다시 시도해 주세요.",
|
|
51
|
+
en: "The sign-in request expired. Please start again.",
|
|
52
|
+
ja: "ログインリクエストの有効期限が切れました。最初からやり直してください。",
|
|
53
|
+
},
|
|
54
|
+
consent_denied: {
|
|
55
|
+
ko: "동의하지 않아 로그인하지 않았습니다.",
|
|
56
|
+
en: "You declined, so you were not signed in.",
|
|
57
|
+
ja: "同意されなかったため、ログインしませんでした。",
|
|
58
|
+
},
|
|
59
|
+
login_required: {
|
|
60
|
+
ko: "로그인이 필요합니다.",
|
|
61
|
+
en: "Please sign in to continue.",
|
|
62
|
+
ja: "ログインが必要です。",
|
|
63
|
+
},
|
|
64
|
+
account_exists: {
|
|
65
|
+
ko: "같은 이메일로 만든 계정이 이미 있습니다. 원래 쓰던 방법으로 로그인해 주세요.",
|
|
66
|
+
en: "An account with this email already exists. Sign in with the method you used before.",
|
|
67
|
+
ja: "このメールアドレスのアカウントは既に存在します。以前の方法でログインしてください。",
|
|
68
|
+
},
|
|
69
|
+
server_error: {
|
|
70
|
+
ko: "로그인 중 문제가 생겼습니다. 잠시 후 다시 시도해 주세요.",
|
|
71
|
+
en: "Something went wrong while signing in. Please try again.",
|
|
72
|
+
ja: "ログイン中に問題が発生しました。しばらくしてから再度お試しください。",
|
|
73
|
+
},
|
|
74
|
+
invalid_credentials: {
|
|
75
|
+
ko: "이메일 또는 비밀번호가 올바르지 않습니다.",
|
|
76
|
+
en: "The email or password is incorrect.",
|
|
77
|
+
ja: "メールアドレスまたはパスワードが正しくありません。",
|
|
78
|
+
},
|
|
79
|
+
weak_password: {
|
|
80
|
+
ko: "비밀번호가 너무 약합니다. 더 길고 복잡하게 만들어 주세요.",
|
|
81
|
+
en: "That password is too weak. Use a longer, less guessable one.",
|
|
82
|
+
ja: "パスワードが弱すぎます。より長く推測されにくいものにしてください。",
|
|
83
|
+
},
|
|
84
|
+
check_email: {
|
|
85
|
+
ko: "이메일을 확인해 주세요.",
|
|
86
|
+
en: "Check your email.",
|
|
87
|
+
ja: "メールをご確認ください。",
|
|
88
|
+
},
|
|
89
|
+
csrf: {
|
|
90
|
+
ko: "요청을 확인할 수 없습니다. 페이지를 새로고침한 뒤 다시 시도해 주세요.",
|
|
91
|
+
en: "The request could not be verified. Reload the page and try again.",
|
|
92
|
+
ja: "リクエストを確認できませんでした。ページを再読み込みしてからお試しください。",
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
export function isAuthErrorCode(v) {
|
|
96
|
+
return typeof v === "string" && AUTH_ERROR_CODES.includes(v);
|
|
97
|
+
}
|
|
98
|
+
export function isAuthApiErrorCode(v) {
|
|
99
|
+
return isAuthErrorCode(v) || (typeof v === "string" && AUTH_API_ERROR_CODES.includes(v));
|
|
100
|
+
}
|
|
101
|
+
/** The default message for a code. Korean when `lang` is omitted or unknown. */
|
|
102
|
+
export function authErrorMessage(code, lang) {
|
|
103
|
+
const l = lang === "en" ? "en" : lang === "ja" ? "ja" : "ko";
|
|
104
|
+
return (MESSAGES[code] ?? MESSAGES.server_error)[l];
|
|
105
|
+
}
|
|
106
|
+
function toURL(url) {
|
|
107
|
+
try {
|
|
108
|
+
if (url instanceof URL)
|
|
109
|
+
return url;
|
|
110
|
+
const s = typeof url === "string" ? url : url.href;
|
|
111
|
+
return new URL(s, "http://x.invalid");
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Reads `auth_error` / `auth_request` from a URL (or `location`). `null` when
|
|
119
|
+
* there is no error. An unknown code is reported as `server_error`.
|
|
120
|
+
*/
|
|
121
|
+
export function parseAuthError(url) {
|
|
122
|
+
const u = toURL(url);
|
|
123
|
+
const raw = u?.searchParams.get("auth_error");
|
|
124
|
+
if (!u || raw === null || raw === undefined || raw === "")
|
|
125
|
+
return null;
|
|
126
|
+
const request = u.searchParams.get("auth_request");
|
|
127
|
+
return { code: isAuthErrorCode(raw) ? raw : "server_error", request: request || null };
|
|
128
|
+
}
|
|
129
|
+
/** `url` without `auth_error` and `auth_request`, as a path+query+hash string. */
|
|
130
|
+
export function withoutAuthError(url) {
|
|
131
|
+
const u = toURL(url);
|
|
132
|
+
if (!u)
|
|
133
|
+
return typeof url === "string" ? url : String(url);
|
|
134
|
+
u.searchParams.delete("auth_error");
|
|
135
|
+
u.searchParams.delete("auth_request");
|
|
136
|
+
const q = u.searchParams.toString();
|
|
137
|
+
return `${u.pathname}${q ? `?${q}` : ""}${u.hash}`;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Removes `auth_error` / `auth_request` from the address bar with
|
|
141
|
+
* `history.replaceState`, so a reload does not show the error again. A no-op
|
|
142
|
+
* outside a browser or when there is nothing to strip.
|
|
143
|
+
*/
|
|
144
|
+
export function stripAuthError() {
|
|
145
|
+
// Read through globalThis so the module also typechecks without the DOM lib
|
|
146
|
+
// (the CLI and workers import the codes from here).
|
|
147
|
+
const g = globalThis;
|
|
148
|
+
if (!g.location || !g.history)
|
|
149
|
+
return;
|
|
150
|
+
if (!parseAuthError(g.location.href))
|
|
151
|
+
return;
|
|
152
|
+
g.history.replaceState(g.history.state, "", withoutAuthError(g.location.href));
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** The path prefix the system worker terminates (docs/auth.md §5). */
|
|
2
2
|
export declare const AUTH_BASE = "/__runlot/auth";
|
|
3
3
|
export interface User {
|
|
4
|
-
/** uuid — `runlot_auth.users`
|
|
4
|
+
/** uuid — the PK of `runlot_auth.users` and the value RLS looks at. */
|
|
5
5
|
id: string;
|
|
6
6
|
email: string | null;
|
|
7
7
|
emailVerified: boolean;
|
|
8
8
|
name: string | null;
|
|
9
9
|
avatarUrl: string | null;
|
|
10
|
-
/**
|
|
10
|
+
/** The sign-in methods linked to this user. For example:
|
|
11
|
+
* `["password", "github"]`. */
|
|
11
12
|
providers: string[];
|
|
12
13
|
/** ISO 8601. */
|
|
13
14
|
createdAt: string;
|
|
14
|
-
/** ISO 8601.
|
|
15
|
+
/** ISO 8601. `null` if they have never signed in. */
|
|
15
16
|
lastSignInAt: string | null;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
|
-
* `signOut`
|
|
19
|
+
* The one deletion-cookie line `signOut` returns. Used as a `HeadersInit` as
|
|
20
|
+
* is.
|
|
19
21
|
*/
|
|
20
22
|
export interface SignOutHeaders {
|
|
21
23
|
"set-cookie": string;
|
|
22
24
|
}
|
|
23
|
-
/** `users.list
|
|
25
|
+
/** One page of `users.list`. A `null` `cursor` means it is the last page. */
|
|
24
26
|
export interface UserPage {
|
|
25
27
|
users: User[];
|
|
26
28
|
cursor: string | null;
|
|
@@ -28,10 +30,11 @@ export interface UserPage {
|
|
|
28
30
|
export interface UserUpdate {
|
|
29
31
|
name?: string | null;
|
|
30
32
|
avatarUrl?: string | null;
|
|
31
|
-
/** `true`
|
|
33
|
+
/** `true` also revokes every session of that user (docs/auth.md §4). */
|
|
32
34
|
banned?: boolean;
|
|
33
35
|
}
|
|
34
|
-
/**
|
|
36
|
+
/** Administrative reads and writes. They do not go through the gateway; they
|
|
37
|
+
* read directly through the `DB` binding. */
|
|
35
38
|
export interface AuthUsers {
|
|
36
39
|
get(id: string): Promise<User | null>;
|
|
37
40
|
getByEmail(email: string): Promise<User | null>;
|
|
@@ -40,29 +43,37 @@ export interface AuthUsers {
|
|
|
40
43
|
limit?: number;
|
|
41
44
|
}): Promise<UserPage>;
|
|
42
45
|
update(id: string, patch: UserUpdate): Promise<User>;
|
|
43
|
-
/**
|
|
46
|
+
/** The user, the identities, the sessions and the tokens. The app's own
|
|
47
|
+
* tables are joined up by the app with `ON DELETE`. */
|
|
44
48
|
delete(id: string): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Moves every identity of `from` onto `into`, deletes `from`'s sessions and
|
|
51
|
+
* then `from` itself, in one transaction (docs/custom-sign-in.md §12.6). The
|
|
52
|
+
* app's own rows that reference `from` are the app's job.
|
|
53
|
+
*/
|
|
54
|
+
merge(from: string, into: string): Promise<void>;
|
|
45
55
|
}
|
|
46
56
|
export interface AuthSessions {
|
|
47
|
-
/**
|
|
57
|
+
/** The idiom after a password change. */
|
|
48
58
|
revokeAll(userId: string): Promise<void>;
|
|
49
59
|
}
|
|
50
60
|
export interface AuthBinding {
|
|
51
61
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
62
|
+
* Cookie → session → user. `null` when there is none.
|
|
63
|
+
* **It does not throw** — not being signed in is not an error.
|
|
54
64
|
*/
|
|
55
65
|
user(request: Request): Promise<User | null>;
|
|
56
66
|
/**
|
|
57
|
-
*
|
|
67
|
+
* Erases that session and returns the deletion cookie. Putting it on the
|
|
68
|
+
* response is the caller's job.
|
|
58
69
|
*
|
|
59
70
|
* ```ts
|
|
60
71
|
* return new Response(null, { headers: await env.auth.signOut(request) });
|
|
61
72
|
* ```
|
|
62
73
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
74
|
+
* It is a plain object rather than a `Headers` because of a measurement —
|
|
75
|
+
* `Headers` cannot cross the RPC boundary (docs/auth.md §4). It is still a
|
|
76
|
+
* `HeadersInit`, so it goes on exactly as above.
|
|
66
77
|
*/
|
|
67
78
|
signOut(request: Request): Promise<SignOutHeaders>;
|
|
68
79
|
users: AuthUsers;
|
|
@@ -75,10 +86,11 @@ export interface AuthBinding {
|
|
|
75
86
|
}): Promise<UserPage>;
|
|
76
87
|
updateUser?(id: string, patch: UserUpdate): Promise<User>;
|
|
77
88
|
deleteUser?(id: string): Promise<void>;
|
|
89
|
+
mergeUsers?(from: string, into: string): Promise<void>;
|
|
78
90
|
revokeSessions?(userId: string): Promise<void>;
|
|
79
91
|
}
|
|
80
92
|
/**
|
|
81
|
-
*
|
|
93
|
+
* The fragment to attach to your own `Env`.
|
|
82
94
|
*
|
|
83
95
|
* ```ts
|
|
84
96
|
* interface Env extends AuthEnv {
|
|
@@ -89,4 +101,6 @@ export interface AuthBinding {
|
|
|
89
101
|
export interface AuthEnv {
|
|
90
102
|
auth: AuthBinding;
|
|
91
103
|
}
|
|
104
|
+
export { AUTH_API_ERROR_CODES, AUTH_ERROR_CODES, authErrorMessage, isAuthApiErrorCode, isAuthErrorCode, parseAuthError, stripAuthError, withoutAuthError, } from "./errors.js";
|
|
105
|
+
export type { AuthApiErrorCode, AuthErrorCode, AuthLang, ParsedAuthError } from "./errors.js";
|
|
92
106
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
// `env.auth`
|
|
1
|
+
// The types for `env.auth` (docs/auth.md §4, §10).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// This package ships no runtime. The binding is injected by the auth system
|
|
4
|
+
// worker inside workerd, so all a user gets from npm is the type to attach to
|
|
5
|
+
// their own `Env` and the helpers called from the browser
|
|
6
|
+
// (`@runlot/auth/client`). That is also why it has zero dependencies — it must
|
|
7
|
+
// not add a single byte to the worker bundle.
|
|
7
8
|
//
|
|
8
|
-
// `user(request)`
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
9
|
+
// `user(request)` taking a request is a constraint, not a taste (the same
|
|
10
|
+
// argument as docs/access.md §4.2): `env` is a bundle fixed for the lifetime of
|
|
11
|
+
// the worker, and the identity is a different value on every request. The
|
|
12
|
+
// argument-free shape only holds inside round 6's Next.js adapter, which hides
|
|
13
|
+
// the request in `AsyncLocalStorage`; anywhere else it is a `TypeError` — if
|
|
14
|
+
// "cannot be done" quietly became `null`, every page would look logged out.
|
|
15
|
+
/** The path prefix the system worker terminates (docs/auth.md §5). */
|
|
14
16
|
export const AUTH_BASE = "/__runlot/auth";
|
|
17
|
+
export { AUTH_API_ERROR_CODES, AUTH_ERROR_CODES, authErrorMessage, isAuthApiErrorCode, isAuthErrorCode, parseAuthError, stripAuthError, withoutAuthError, } from "./errors.js";
|
|
15
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,0DAA0D;AAC1D,+EAA+E;AAC/E,8CAA8C;AAC9C,EAAE;AACF,0EAA0E;AAC1E,gFAAgF;AAChF,0EAA0E;AAC1E,+EAA+E;AAC/E,6EAA6E;AAC7E,4EAA4E;AAE5E,sEAAsE;AACtE,MAAM,CAAC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AA+G1C,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,aAAa,CAAC"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type SignInMethod } from "./ui.js";
|
|
2
|
+
export interface RunlotSignInProps {
|
|
3
|
+
next?: string;
|
|
4
|
+
lang?: "ko" | "en" | "ja";
|
|
5
|
+
methods?: SignInMethod[] | string;
|
|
6
|
+
guest?: boolean;
|
|
7
|
+
errorNext?: string;
|
|
8
|
+
loginHint?: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Fired when the guest button is pressed (the `runlot-guest` event). */
|
|
11
|
+
onGuest?: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function RunlotSignIn(props: RunlotSignInProps): import("react").DOMElement<Record<string, unknown>, Element>;
|
|
14
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,gEAsBpD"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// A thin React wrapper around `<runlot-sign-in>` (docs/custom-sign-in.md §7.1).
|
|
2
|
+
// React is an optional peer: only this subpath imports it. Written with
|
|
3
|
+
// `createElement` so the package needs no JSX configuration.
|
|
4
|
+
import { createElement, useEffect, useRef } from "react";
|
|
5
|
+
import { defineRunlotSignIn } from "./ui.js";
|
|
6
|
+
export function RunlotSignIn(props) {
|
|
7
|
+
const ref = useRef(null);
|
|
8
|
+
const { onGuest } = props;
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
defineRunlotSignIn();
|
|
11
|
+
}, []);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const el = ref.current;
|
|
14
|
+
if (!el || !onGuest)
|
|
15
|
+
return;
|
|
16
|
+
const h = () => onGuest();
|
|
17
|
+
el.addEventListener("runlot-guest", h);
|
|
18
|
+
return () => el.removeEventListener("runlot-guest", h);
|
|
19
|
+
}, [onGuest]);
|
|
20
|
+
const attrs = { ref };
|
|
21
|
+
if (props.next)
|
|
22
|
+
attrs.next = props.next;
|
|
23
|
+
if (props.lang)
|
|
24
|
+
attrs.lang = props.lang;
|
|
25
|
+
if (props.methods)
|
|
26
|
+
attrs.methods = Array.isArray(props.methods) ? props.methods.join(" ") : props.methods;
|
|
27
|
+
if (props.guest)
|
|
28
|
+
attrs.guest = "";
|
|
29
|
+
if (props.errorNext)
|
|
30
|
+
attrs["error-next"] = props.errorNext;
|
|
31
|
+
if (props.loginHint)
|
|
32
|
+
attrs["login-hint"] = props.loginHint;
|
|
33
|
+
if (props.className)
|
|
34
|
+
attrs.class = props.className;
|
|
35
|
+
return createElement("runlot-sign-in", attrs);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wEAAwE;AACxE,6DAA6D;AAE7D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAqB,MAAM,SAAS,CAAC;AAchE,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,MAAM,GAAG,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IAC7C,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,SAAS,CAAC,GAAG,EAAE;QACb,kBAAkB,EAAE,CAAC;IACvB,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC;QACvB,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO;YAAE,OAAO;QAC5B,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1B,EAAE,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;QACvC,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,mBAAmB,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACd,MAAM,KAAK,GAA4B,EAAE,GAAG,EAAE,CAAC;IAC/C,IAAI,KAAK,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxC,IAAI,KAAK,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxC,IAAI,KAAK,CAAC,OAAO;QAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAC1G,IAAI,KAAK,CAAC,KAAK;QAAE,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;IAClC,IAAI,KAAK,CAAC,SAAS;QAAE,KAAK,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC;IAC3D,IAAI,KAAK,CAAC,SAAS;QAAE,KAAK,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC;IAC3D,IAAI,KAAK,CAAC,SAAS;QAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC;IACnD,OAAO,aAAa,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC"}
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AuthConfig, type RunlotMethod } from "./client.js";
|
|
2
|
+
import { type AuthLang } from "./errors.js";
|
|
3
|
+
/** A token of the `methods` attribute: a Runlot ID method, or `runlot` for the plain chooser button. */
|
|
4
|
+
export type SignInMethod = RunlotMethod | "runlot";
|
|
5
|
+
export interface SignInRenderInput {
|
|
6
|
+
lang?: string | null;
|
|
7
|
+
next?: string | null;
|
|
8
|
+
errorNext?: string | null;
|
|
9
|
+
loginHint?: string | null;
|
|
10
|
+
/** The `methods` attribute, space-separated; `null` = what the project offers. */
|
|
11
|
+
methods?: string | null;
|
|
12
|
+
guest?: boolean;
|
|
13
|
+
/** `null` while loading or when config failed. */
|
|
14
|
+
config?: AuthConfig | null;
|
|
15
|
+
/** The message to show, already resolved. */
|
|
16
|
+
error?: string | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveLang(lang: string | null | undefined): AuthLang;
|
|
19
|
+
/** Which buttons to draw: the asked-for tokens ∩ what the project's `runlot` provider offers. */
|
|
20
|
+
export declare function resolveMethods(cfg: AuthConfig | null | undefined, attr: string | null | undefined): SignInMethod[];
|
|
21
|
+
/** The shadow-root HTML. Pure, so it is testable without a DOM. */
|
|
22
|
+
export declare function renderSignIn(i: SignInRenderInput): string;
|
|
23
|
+
/** Defines the element (idempotent). Called on import when `customElements` exists. */
|
|
24
|
+
export declare function defineRunlotSignIn(tag?: string): void;
|
|
25
|
+
//# sourceMappingURL=ui.d.ts.map
|
package/dist/ui.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,UAAU,EAAqB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,KAAK,QAAQ,EAAoD,MAAM,aAAa,CAAC;AAE9F,wGAAwG;AACxG,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,QAAQ,CAAC;AA+BnD,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kDAAkD;IAClD,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,CAGrE;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,EAAE,CAclH;AAYD,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAgBzD;AAID,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,GAAG,SAAmB,GAAG,IAAI,CAmD/D"}
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// `<runlot-sign-in>` — a dependency-free custom element that draws a sign-in
|
|
2
|
+
// block from `config()` (docs/custom-sign-in.md §7.1, §12.5).
|
|
3
|
+
//
|
|
4
|
+
// <runlot-sign-in next="/chat" lang="ko" methods="google github password" guest></runlot-sign-in>
|
|
5
|
+
//
|
|
6
|
+
// Every element carries a `part` (`::part(button-google)`), colours, radius and
|
|
7
|
+
// font come from CSS custom properties, and the shadow styles are all
|
|
8
|
+
// overridable through those two. Buttons are plain links built by `signInUrl`,
|
|
9
|
+
// so the credential is always typed on a Runlot origin (I1). Importing this
|
|
10
|
+
// module in Node or a worker is safe: the element is defined only when
|
|
11
|
+
// `customElements` exists. Budget: ≤ 6 KiB gzip with its imports.
|
|
12
|
+
import { config, signInUrl } from "./client.js";
|
|
13
|
+
import { authErrorMessage, parseAuthError, stripAuthError } from "./errors.js";
|
|
14
|
+
const ALL = ["google", "github", "password", "passkey", "runlot"];
|
|
15
|
+
const LABELS = {
|
|
16
|
+
ko: {
|
|
17
|
+
google: "Google로 계속하기",
|
|
18
|
+
github: "GitHub로 계속하기",
|
|
19
|
+
password: "이메일로 계속하기",
|
|
20
|
+
passkey: "패스키로 계속하기",
|
|
21
|
+
runlot: "Runlot으로 계속하기",
|
|
22
|
+
guest: "게스트로 둘러보기",
|
|
23
|
+
},
|
|
24
|
+
en: {
|
|
25
|
+
google: "Continue with Google",
|
|
26
|
+
github: "Continue with GitHub",
|
|
27
|
+
password: "Continue with email",
|
|
28
|
+
passkey: "Continue with a passkey",
|
|
29
|
+
runlot: "Continue with Runlot",
|
|
30
|
+
guest: "Continue as guest",
|
|
31
|
+
},
|
|
32
|
+
ja: {
|
|
33
|
+
google: "Googleで続行",
|
|
34
|
+
github: "GitHubで続行",
|
|
35
|
+
password: "メールで続行",
|
|
36
|
+
passkey: "パスキーで続行",
|
|
37
|
+
runlot: "Runlotで続行",
|
|
38
|
+
guest: "ゲストとして続行",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export function resolveLang(lang) {
|
|
42
|
+
const l = (lang ?? "").slice(0, 2).toLowerCase();
|
|
43
|
+
return l === "en" || l === "ja" ? l : "ko";
|
|
44
|
+
}
|
|
45
|
+
/** Which buttons to draw: the asked-for tokens ∩ what the project's `runlot` provider offers. */
|
|
46
|
+
export function resolveMethods(cfg, attr) {
|
|
47
|
+
const p = cfg?.providers.find((x) => x.kind === "runlot");
|
|
48
|
+
if (!p)
|
|
49
|
+
return [];
|
|
50
|
+
const offered = p.methods && p.methods.length > 0 ? [...p.methods] : ["runlot"];
|
|
51
|
+
offered.push("runlot");
|
|
52
|
+
const want = attr == null || attr.trim() === "" ? null : attr.trim().split(/[\s,]+/);
|
|
53
|
+
const list = want ?? (p.methods && p.methods.length > 0 ? p.methods : ["runlot"]);
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const m of list) {
|
|
56
|
+
if (ALL.includes(m) && offered.includes(m) && !out.includes(m)) {
|
|
57
|
+
out.push(m);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
const esc = (s) => s.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
63
|
+
const STYLE = `:host{display:block;font:var(--runlot-font,inherit);color:var(--runlot-text,inherit)}
|
|
64
|
+
[part~=root]{display:flex;flex-direction:column;gap:var(--runlot-gap,.5rem)}
|
|
65
|
+
[part~=button]{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:100%;padding:var(--runlot-padding,.625rem 1rem);border:1px solid var(--runlot-border,#d4d4d8);border-radius:var(--runlot-radius,.5rem);background:var(--runlot-bg,#fff);color:var(--runlot-button-text,#18181b);font:inherit;text-decoration:none;cursor:pointer}
|
|
66
|
+
[part~=button]:hover{background:var(--runlot-bg-hover,#f4f4f5)}
|
|
67
|
+
[part~=button-runlot]{background:var(--runlot-brand,#18181b);color:var(--runlot-brand-text,#fff);border-color:transparent}
|
|
68
|
+
[part~=error]{padding:.5rem .75rem;border-radius:var(--runlot-radius,.5rem);background:var(--runlot-error-bg,#fef2f2);color:var(--runlot-error,#b91c1c)}`;
|
|
69
|
+
/** The shadow-root HTML. Pure, so it is testable without a DOM. */
|
|
70
|
+
export function renderSignIn(i) {
|
|
71
|
+
const lang = resolveLang(i.lang);
|
|
72
|
+
const L = LABELS[lang];
|
|
73
|
+
let h = `<style>${STYLE}</style><div part="root"><slot name="header"></slot>`;
|
|
74
|
+
if (i.error)
|
|
75
|
+
h += `<div part="error" role="alert">${esc(i.error)}</div>`;
|
|
76
|
+
for (const m of resolveMethods(i.config, i.methods)) {
|
|
77
|
+
const href = signInUrl("runlot", {
|
|
78
|
+
...(i.next ? { next: i.next } : {}),
|
|
79
|
+
...(i.errorNext ? { errorNext: i.errorNext } : {}),
|
|
80
|
+
...(i.loginHint ? { loginHint: i.loginHint } : {}),
|
|
81
|
+
...(m !== "runlot" ? { idp: m } : {}),
|
|
82
|
+
});
|
|
83
|
+
h += `<a part="button button-${m}" href="${esc(href)}">${esc(L[m])}</a>`;
|
|
84
|
+
}
|
|
85
|
+
if (i.guest)
|
|
86
|
+
h += `<button part="button button-guest" type="button">${esc(L.guest)}</button>`;
|
|
87
|
+
return `${h}<slot name="footer"></slot></div>`;
|
|
88
|
+
}
|
|
89
|
+
const ATTRS = ["next", "lang", "methods", "guest", "error-next", "login-hint"];
|
|
90
|
+
/** Defines the element (idempotent). Called on import when `customElements` exists. */
|
|
91
|
+
export function defineRunlotSignIn(tag = "runlot-sign-in") {
|
|
92
|
+
if (typeof customElements === "undefined" || customElements.get(tag))
|
|
93
|
+
return;
|
|
94
|
+
class RunlotSignIn extends HTMLElement {
|
|
95
|
+
static observedAttributes = ATTRS;
|
|
96
|
+
#cfg = null;
|
|
97
|
+
#error = null;
|
|
98
|
+
#code = null;
|
|
99
|
+
constructor() {
|
|
100
|
+
super();
|
|
101
|
+
const root = this.attachShadow({ mode: "open" });
|
|
102
|
+
root.addEventListener("click", (e) => {
|
|
103
|
+
const t = e.target;
|
|
104
|
+
if (t?.closest?.('[part~="button-guest"]')) {
|
|
105
|
+
this.dispatchEvent(new CustomEvent("runlot-guest", { bubbles: true, composed: true }));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
connectedCallback() {
|
|
110
|
+
if (typeof location !== "undefined") {
|
|
111
|
+
this.#code = parseAuthError(location.href);
|
|
112
|
+
if (this.#code)
|
|
113
|
+
stripAuthError();
|
|
114
|
+
}
|
|
115
|
+
this.#paint();
|
|
116
|
+
config().then((c) => {
|
|
117
|
+
this.#cfg = c;
|
|
118
|
+
this.#paint();
|
|
119
|
+
}, () => this.#paint());
|
|
120
|
+
}
|
|
121
|
+
attributeChangedCallback() {
|
|
122
|
+
if (this.isConnected)
|
|
123
|
+
this.#paint();
|
|
124
|
+
}
|
|
125
|
+
#paint() {
|
|
126
|
+
const a = (n) => this.getAttribute(n);
|
|
127
|
+
const lang = a("lang") ?? (typeof document !== "undefined" ? document.documentElement.lang : null);
|
|
128
|
+
this.#error = this.#code ? authErrorMessage(this.#code.code, resolveLang(lang)) : null;
|
|
129
|
+
this.shadowRoot.innerHTML = renderSignIn({
|
|
130
|
+
lang,
|
|
131
|
+
next: a("next"),
|
|
132
|
+
errorNext: a("error-next"),
|
|
133
|
+
loginHint: a("login-hint"),
|
|
134
|
+
methods: a("methods"),
|
|
135
|
+
guest: this.hasAttribute("guest"),
|
|
136
|
+
config: this.#cfg,
|
|
137
|
+
error: this.#error,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
customElements.define(tag, RunlotSignIn);
|
|
142
|
+
}
|
|
143
|
+
defineRunlotSignIn();
|
|
144
|
+
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8DAA8D;AAC9D,EAAE;AACF,oGAAoG;AACpG,EAAE;AACF,gFAAgF;AAChF,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,uEAAuE;AACvE,kEAAkE;AAElE,OAAO,EAAmB,MAAM,EAAE,SAAS,EAAqB,MAAM,aAAa,CAAC;AACpF,OAAO,EAAiB,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAK9F,MAAM,GAAG,GAA4B,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3F,MAAM,MAAM,GAA6D;IACvE,EAAE,EAAE;QACF,MAAM,EAAE,cAAc;QACtB,MAAM,EAAE,cAAc;QACtB,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,WAAW;QACpB,MAAM,EAAE,eAAe;QACvB,KAAK,EAAE,WAAW;KACnB;IACD,EAAE,EAAE;QACF,MAAM,EAAE,sBAAsB;QAC9B,MAAM,EAAE,sBAAsB;QAC9B,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,yBAAyB;QAClC,MAAM,EAAE,sBAAsB;QAC9B,KAAK,EAAE,mBAAmB;KAC3B;IACD,EAAE,EAAE;QACF,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,WAAW;QACnB,KAAK,EAAE,UAAU;KAClB;CACF,CAAC;AAgBF,MAAM,UAAU,WAAW,CAAC,IAA+B;IACzD,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,cAAc,CAAC,GAAkC,EAAE,IAA+B;IAChG,MAAM,CAAC,GAAG,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,MAAM,OAAO,GAAmB,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAChG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrF,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClF,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAiB,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAiB,CAAC,EAAE,CAAC;YAC/G,GAAG,CAAC,IAAI,CAAC,CAAiB,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CACxB,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAExD,MAAM,KAAK,GAAG;;;;;yJAK2I,CAAC;AAE1J,mEAAmE;AACnE,MAAM,UAAU,YAAY,CAAC,CAAoB;IAC/C,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,GAAG,UAAU,KAAK,sDAAsD,CAAC;IAC9E,IAAI,CAAC,CAAC,KAAK;QAAE,CAAC,IAAI,kCAAkC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;IACzE,KAAK,MAAM,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,EAAE;YAC/B,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,GAAG,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtC,CAAC,CAAC;QACH,CAAC,IAAI,0BAA0B,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,CAAC,KAAK;QAAE,CAAC,IAAI,oDAAoD,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;IAC9F,OAAO,GAAG,CAAC,mCAAmC,CAAC;AACjD,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;AAE/E,uFAAuF;AACvF,MAAM,UAAU,kBAAkB,CAAC,GAAG,GAAG,gBAAgB;IACvD,IAAI,OAAO,cAAc,KAAK,WAAW,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAC7E,MAAM,YAAa,SAAQ,WAAW;QACpC,MAAM,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAClC,IAAI,GAAsB,IAAI,CAAC;QAC/B,MAAM,GAAkB,IAAI,CAAC;QAC7B,KAAK,GAAsC,IAAI,CAAC;QAChD;YACE,KAAK,EAAE,CAAC;YACR,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gBACnC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAwB,CAAC;gBACrC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,wBAAwB,CAAC,EAAE,CAAC;oBAC3C,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACzF,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,iBAAiB;YACf,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACpC,IAAI,CAAC,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,IAAI,CAAC,KAAK;oBAAE,cAAc,EAAE,CAAC;YACnC,CAAC;YACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,MAAM,EAAE,CAAC,IAAI,CACX,CAAC,CAAC,EAAE,EAAE;gBACJ,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;gBACd,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,CAAC,EACD,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CACpB,CAAC;QACJ,CAAC;QACD,wBAAwB;YACtB,IAAI,IAAI,CAAC,WAAW;gBAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QACtC,CAAC;QACD,MAAM;YACJ,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACnG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACvF,IAAI,CAAC,UAAW,CAAC,SAAS,GAAG,YAAY,CAAC;gBACxC,IAAI;gBACJ,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBACf,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC;gBAC1B,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC;gBAC1B,OAAO,EAAE,CAAC,CAAC,SAAS,CAAC;gBACrB,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;gBACjC,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,KAAK,EAAE,IAAI,CAAC,MAAM;aACnB,CAAC,CAAC;QACL,CAAC;;IAEH,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AAC3C,CAAC;AAED,kBAAkB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runlot/auth",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0-rc.170",
|
|
4
|
+
"description": "Types and browser helpers for env.auth — Runlot provides the runtime",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -21,6 +21,14 @@
|
|
|
21
21
|
"./client": {
|
|
22
22
|
"types": "./dist/client.d.ts",
|
|
23
23
|
"default": "./dist/client.js"
|
|
24
|
+
},
|
|
25
|
+
"./ui": {
|
|
26
|
+
"types": "./dist/ui.d.ts",
|
|
27
|
+
"default": "./dist/ui.js"
|
|
28
|
+
},
|
|
29
|
+
"./react": {
|
|
30
|
+
"types": "./dist/react.d.ts",
|
|
31
|
+
"default": "./dist/react.js"
|
|
24
32
|
}
|
|
25
33
|
},
|
|
26
34
|
"files": [
|
|
@@ -30,11 +38,26 @@
|
|
|
30
38
|
"@types/node": "^22.10.2",
|
|
31
39
|
"esbuild": "^0.24.2",
|
|
32
40
|
"typescript": "^5.7.2",
|
|
33
|
-
"@
|
|
41
|
+
"@types/react": "^19.2.18",
|
|
42
|
+
"react": "^19.2.8",
|
|
43
|
+
"@runlot/config": "0.1.0-rc.170"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": ">=18"
|
|
34
47
|
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"react": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"sideEffects": [
|
|
54
|
+
"./src/ui.ts",
|
|
55
|
+
"./dist/ui.js"
|
|
56
|
+
],
|
|
35
57
|
"scripts": {
|
|
36
58
|
"test": "node build.mjs",
|
|
37
59
|
"typecheck": "tsc --noEmit",
|
|
38
|
-
"build": "tsc -p tsconfig.build.json"
|
|
60
|
+
"build": "tsc -p tsconfig.build.json",
|
|
61
|
+
"size": "node size.mjs"
|
|
39
62
|
}
|
|
40
63
|
}
|