workers-mini-admin 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.
@@ -0,0 +1,80 @@
1
+ import { type Field } from './form.js';
2
+ import { type FlashTone } from './html.js';
3
+ export type AdminRenderResult = {
4
+ hint?: string;
5
+ flash?: string;
6
+ flashTone?: FlashTone;
7
+ submitLabel?: string;
8
+ /**
9
+ Appended after shared CSS.
10
+ */
11
+ extraCss?: string;
12
+ /**
13
+ Sets `.page-shell` max-width (e.g. `1200px`).
14
+ */
15
+ shellMaxWidth?: string;
16
+ /**
17
+ When false, do not wrap `body` / form in `.panel-card` (default true).
18
+ */
19
+ wrapBody?: boolean;
20
+ /**
21
+ Attributes merged onto `<body>`.
22
+ */
23
+ bodyAttrs?: Record<string, string>;
24
+ } & ({
25
+ fields: Field[];
26
+ body?: undefined;
27
+ } | {
28
+ body: string;
29
+ fields?: undefined;
30
+ });
31
+ export type AdminSaveResult = {
32
+ flash?: string;
33
+ };
34
+ export type AdminRouteContext<Env = unknown> = {
35
+ request: Request;
36
+ env: Env;
37
+ url: URL;
38
+ secret: string;
39
+ basePath: string;
40
+ /**
41
+ Relative path under the mount (e.g. `/preview`).
42
+ */
43
+ path: string;
44
+ requireAuth: () => Promise<Response | undefined>;
45
+ };
46
+ export type CreateAdminOptions<Env = unknown> = {
47
+ /**
48
+ Mount path. `""` or `"/"` = site root; default `"/admin"`.
49
+ */
50
+ basePath?: string;
51
+ title: string;
52
+ getSecret: (env: Env) => string | undefined;
53
+ cookieName?: string;
54
+ /**
55
+ Cookie Path attribute. Defaults to `/`.
56
+ */
57
+ cookiePath?: string;
58
+ render: (context: {
59
+ request: Request;
60
+ env: Env;
61
+ url: URL;
62
+ }) => AdminRenderResult | Promise<AdminRenderResult>;
63
+ save: (context: {
64
+ request: Request;
65
+ env: Env;
66
+ url: URL;
67
+ form: FormData;
68
+ }) => AdminSaveResult | Promise<AdminSaveResult>;
69
+ /**
70
+ Extra routes relative to `basePath`, e.g. `"POST /preview"`.
71
+ Run after secret is present; use `context.requireAuth()` for cookie/Bearer checks.
72
+ */
73
+ routes?: Record<string, (context: AdminRouteContext<Env>) => Response | Promise<Response>>;
74
+ disabledMessage?: string;
75
+ };
76
+ export type AdminHandler<Env = unknown> = {
77
+ fetch: (request: Request, env: Env) => Promise<Response | undefined>;
78
+ };
79
+ export declare function createAdmin<Env = unknown>(options: CreateAdminOptions<Env>): AdminHandler<Env>;
80
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAMA,OAAO,EAAuB,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;MAEE;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;MAEE;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;MAEE;IACF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;MAEE;IACF,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,GAAG,CACF;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,CAC7E,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,GAAG,GAAG,OAAO,IAAI;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB;;MAEE;IACF,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,GAAG,GAAG,OAAO,IAAI;IAC9C;;MAEE;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,SAAS,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;MAEE;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,OAAO,EAAE;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,GAAG,EAAE,GAAG,CAAC;QACT,GAAG,EAAE,GAAG,CAAC;KACV,KAAK,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACrD,IAAI,EAAE,CAAC,OAAO,EAAE;QACd,OAAO,EAAE,OAAO,CAAC;QACjB,GAAG,EAAE,GAAG,CAAC;QACT,GAAG,EAAE,GAAG,CAAC;QACT,IAAI,EAAE,QAAQ,CAAC;KAChB,KAAK,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACjD;;;MAGE;IACF,MAAM,CAAC,EAAE,MAAM,CACb,MAAM,EACN,CAAC,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAClE,CAAC;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,GAAG,GAAG,OAAO,IAAI;IACxC,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;CACtE,CAAC;AA4BF,wBAAgB,WAAW,CAAC,GAAG,GAAG,OAAO,EACvC,OAAO,EAAE,kBAAkB,CAAC,GAAG,CAAC,GAC/B,YAAY,CAAC,GAAG,CAAC,CAwJnB"}
@@ -0,0 +1,177 @@
1
+ import { DEFAULT_COOKIE_NAME, isAuthenticated, loginResponse, logoutResponse, } from './auth.js';
2
+ import { adminForm, formText } from './form.js';
3
+ import { adminDisabledResponse, adminPage, html, loginPageHtml, } from './html.js';
4
+ import { joinBasePath, normalizeBasePath, relativeToBase } from './path.js';
5
+ function isSecureCookie(request) {
6
+ return new URL(request.url).protocol === 'https:';
7
+ }
8
+ function isRootMount(basePath) {
9
+ return normalizeBasePath(basePath) === '';
10
+ }
11
+ function knownRelativePaths(routes) {
12
+ const known = new Set(['/', '/login', '/logout']);
13
+ if (!routes) {
14
+ return known;
15
+ }
16
+ for (const key of Object.keys(routes)) {
17
+ const space = key.indexOf(' ');
18
+ if (space === -1) {
19
+ continue;
20
+ }
21
+ const relativePath = key.slice(space + 1);
22
+ known.add(relativePath.startsWith('/') ? relativePath : `/${relativePath}`);
23
+ }
24
+ return known;
25
+ }
26
+ export function createAdmin(options) {
27
+ const basePath = normalizeBasePath(options.basePath ?? '/admin');
28
+ const cookieName = options.cookieName ?? DEFAULT_COOKIE_NAME;
29
+ // Prefer Path=/ so login/logout always agree. Mount-scoped paths (e.g.
30
+ // `/admin`) left stale Path=/ cookies that sign-out could not clear.
31
+ const cookiePath = options.cookiePath ?? '/';
32
+ const homePath = joinBasePath(basePath);
33
+ const known = knownRelativePaths(options.routes);
34
+ const loginHtml = (error) => loginPageHtml({
35
+ title: options.title,
36
+ error,
37
+ basePath,
38
+ });
39
+ async function requireAuth(request, secret) {
40
+ const isSignedIn = await isAuthenticated(request, secret, cookieName);
41
+ return isSignedIn ? undefined : html(loginHtml(), 401);
42
+ }
43
+ async function loginGet(request, url, secret) {
44
+ const isSignedIn = await isAuthenticated(request, secret, cookieName);
45
+ return isSignedIn
46
+ ? Response.redirect(new URL(homePath, url).href, 302)
47
+ : html(loginHtml());
48
+ }
49
+ async function loginPost(request, secret) {
50
+ const form = await request.formData();
51
+ const password = formText(form, 'password');
52
+ return loginResponse({
53
+ secret,
54
+ password,
55
+ secureCookie: isSecureCookie(request),
56
+ basePath,
57
+ cookieName,
58
+ cookiePath,
59
+ loginHtml,
60
+ });
61
+ }
62
+ async function home(request, env, url, secret) {
63
+ const unauth = await requireAuth(request, secret);
64
+ if (unauth) {
65
+ return unauth;
66
+ }
67
+ if (request.method === 'GET') {
68
+ const page = await options.render({ request, env, url });
69
+ return html(renderAdminPage(options.title, basePath, page));
70
+ }
71
+ return request.method === 'POST'
72
+ ? saveHome(request, env, url)
73
+ : new Response('Method not allowed', { status: 405 });
74
+ }
75
+ async function saveHome(request, env, url) {
76
+ try {
77
+ const form = await request.formData();
78
+ const saved = await options.save({
79
+ request,
80
+ env,
81
+ url,
82
+ form,
83
+ });
84
+ const page = await options.render({ request, env, url });
85
+ return html(renderAdminPage(options.title, basePath, {
86
+ ...page,
87
+ flash: saved.flash ?? page.flash,
88
+ }));
89
+ }
90
+ catch (error) {
91
+ const message = error instanceof Error ? error.message : String(error);
92
+ const page = await options.render({ request, env, url });
93
+ return html(renderAdminPage(options.title, basePath, {
94
+ ...page,
95
+ flash: `Error: ${message}`,
96
+ }), 400);
97
+ }
98
+ }
99
+ return {
100
+ async fetch(request, env) {
101
+ const url = new URL(request.url);
102
+ const relativePath = relativeToBase(url.pathname, basePath);
103
+ if (relativePath === undefined) {
104
+ return undefined;
105
+ }
106
+ if (isRootMount(basePath) && !known.has(relativePath)) {
107
+ return undefined;
108
+ }
109
+ const secret = options.getSecret(env);
110
+ if (!secret) {
111
+ return adminDisabledResponse(options.disabledMessage);
112
+ }
113
+ const routeKey = `${request.method} ${relativePath}`;
114
+ const custom = options.routes?.[routeKey];
115
+ if (custom) {
116
+ return custom({
117
+ request,
118
+ env,
119
+ url,
120
+ secret,
121
+ basePath,
122
+ path: relativePath,
123
+ requireAuth: () => requireAuth(request, secret),
124
+ });
125
+ }
126
+ if (relativePath === '/login' && request.method === 'GET') {
127
+ return loginGet(request, url, secret);
128
+ }
129
+ if (relativePath === '/login' && request.method === 'POST') {
130
+ return loginPost(request, secret);
131
+ }
132
+ if (relativePath === '/logout' && request.method === 'POST') {
133
+ return logoutResponse({
134
+ secureCookie: isSecureCookie(request),
135
+ basePath,
136
+ cookieName,
137
+ cookiePath,
138
+ });
139
+ }
140
+ if (relativePath === '/') {
141
+ return home(request, env, url, secret);
142
+ }
143
+ return isRootMount(basePath)
144
+ ? undefined
145
+ : new Response('Not found', { status: 404 });
146
+ },
147
+ };
148
+ }
149
+ function renderAdminPage(title, basePath, page) {
150
+ let body;
151
+ if ('body' in page && page.body !== undefined) {
152
+ body = page.body;
153
+ }
154
+ else if ('fields' in page) {
155
+ body = adminForm({
156
+ action: joinBasePath(basePath),
157
+ fields: page.fields,
158
+ submitLabel: page.submitLabel,
159
+ });
160
+ }
161
+ else {
162
+ body = '';
163
+ }
164
+ return adminPage({
165
+ title,
166
+ body,
167
+ flash: page.flash,
168
+ flashTone: page.flashTone,
169
+ hint: page.hint,
170
+ basePath,
171
+ extraCss: page.extraCss,
172
+ shellMaxWidth: page.shellMaxWidth,
173
+ wrapBody: page.wrapBody,
174
+ bodyAttrs: page.bodyAttrs,
175
+ });
176
+ }
177
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
package/dist/html.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ export { escapeHtml } from './escape.js';
2
+ export type FlashTone = 'ok' | 'warn' | 'error';
3
+ export declare function html(body: string, status?: number): Response;
4
+ export declare function redirect(location: string, cookie?: string | readonly string[]): Response;
5
+ export declare function adminDisabledResponse(message?: string): Response;
6
+ export declare function loginPageHtml(options: {
7
+ title: string;
8
+ error?: string;
9
+ action?: string;
10
+ basePath?: string;
11
+ }): string;
12
+ /**
13
+ Allow only simple CSS lengths for shellMaxWidth (no injection).
14
+ */
15
+ export declare function isSafeCssLength(value: string): boolean;
16
+ export declare function resolveFlashClass(flash: string, tone?: FlashTone): 'ok' | 'warn' | 'flash-err';
17
+ export type AdminPageOptions = {
18
+ title: string;
19
+ body: string;
20
+ flash?: string;
21
+ flashTone?: FlashTone;
22
+ /**
23
+ Hint under the title. Not escaped — may include trusted HTML (e.g. `<code>`).
24
+ */
25
+ hint?: string;
26
+ logoutAction?: string;
27
+ basePath?: string;
28
+ /**
29
+ Appended after shared CSS (app-specific rules).
30
+ */
31
+ extraCss?: string;
32
+ /**
33
+ Sets `.page-shell { max-width }`. Must be a simple length (e.g. `1200px`).
34
+ */
35
+ shellMaxWidth?: string;
36
+ /**
37
+ When false, `body` is not wrapped in `.panel-card` (default true).
38
+ Use for multi-panel / bring-your-own-card layouts.
39
+ */
40
+ wrapBody?: boolean;
41
+ /**
42
+ Attributes merged onto `<body>` (values escaped).
43
+ */
44
+ bodyAttrs?: Record<string, string>;
45
+ };
46
+ export declare function adminPage(options: AdminPageOptions): string;
47
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,QAAQ,CAKzD;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAClC,QAAQ,CAUV;AAKD,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAKhE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CA+BT;AAED;;EAEE;AACF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,SAAS,GACf,IAAI,GAAG,MAAM,GAAG,WAAW,CAO7B;AAaD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;MAEE;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;MAEE;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;MAEE;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;MAGE;IACF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;MAEE;IACF,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,CAAC;AAEF,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAgD3D"}
package/dist/html.js ADDED
@@ -0,0 +1,116 @@
1
+ import { SHARED_CSS } from './css.js';
2
+ import { escapeHtml } from './escape.js';
3
+ import { joinBasePath, resolveBasePath } from './path.js';
4
+ export { escapeHtml } from './escape.js';
5
+ export function html(body, status = 200) {
6
+ return new Response(body, {
7
+ status,
8
+ headers: { 'content-type': 'text/html; charset=utf-8' },
9
+ });
10
+ }
11
+ export function redirect(location, cookie) {
12
+ const headers = new Headers({ Location: location });
13
+ if (typeof cookie === 'string') {
14
+ headers.append('Set-Cookie', cookie);
15
+ }
16
+ else if (cookie) {
17
+ for (const value of cookie) {
18
+ headers.append('Set-Cookie', value);
19
+ }
20
+ }
21
+ return new Response(undefined, { status: 302, headers });
22
+ }
23
+ const DEFAULT_ADMIN_DISABLED_MESSAGE = 'Admin UI is disabled. Set the ADMIN_SECRET secret (wrangler secret put ADMIN_SECRET).';
24
+ export function adminDisabledResponse(message) {
25
+ return new Response(message ?? DEFAULT_ADMIN_DISABLED_MESSAGE, {
26
+ status: 503,
27
+ headers: { 'content-type': 'text/plain; charset=utf-8' },
28
+ });
29
+ }
30
+ export function loginPageHtml(options) {
31
+ const { title, error, basePath, action = joinBasePath(resolveBasePath(basePath), 'login'), } = options;
32
+ return `<!DOCTYPE html>
33
+ <html lang="en">
34
+ <head>
35
+ <meta charset="utf-8">
36
+ <meta name="viewport" content="width=device-width, initial-scale=1">
37
+ <title>${escapeHtml(title)} — login</title>
38
+ <style>${SHARED_CSS}</style>
39
+ </head>
40
+ <body class="admin-body">
41
+ <div class="login-shell">
42
+ <div class="panel-card">
43
+ <h1>${escapeHtml(title)}</h1>
44
+ ${error ? `<p class="err">${escapeHtml(error)}</p>` : ''}
45
+ <form method="post" action="${escapeHtml(action)}">
46
+ <label for="password">Password</label>
47
+ <input id="password" name="password" type="password" autocomplete="current-password" required>
48
+ <div class="actions">
49
+ <button type="submit" class="btn-primary">Sign in</button>
50
+ </div>
51
+ </form>
52
+ </div>
53
+ </div>
54
+ </body>
55
+ </html>`;
56
+ }
57
+ /**
58
+ Allow only simple CSS lengths for shellMaxWidth (no injection).
59
+ */
60
+ export function isSafeCssLength(value) {
61
+ return /^-?\d+(\.\d+)?(px|rem|em|%|ch|vw|vh)$/.test(value.trim());
62
+ }
63
+ export function resolveFlashClass(flash, tone) {
64
+ if (tone === 'error' || (!tone && flash.startsWith('Error:'))) {
65
+ return 'flash-err';
66
+ }
67
+ return tone === 'warn' || (!tone && /\bfailed\b/i.test(flash))
68
+ ? 'warn'
69
+ : 'ok';
70
+ }
71
+ function bodyAttributesToString(attributes) {
72
+ if (!attributes) {
73
+ return '';
74
+ }
75
+ let out = '';
76
+ for (const [key, value] of Object.entries(attributes)) {
77
+ out += ` ${escapeHtml(key)}="${escapeHtml(value)}"`;
78
+ }
79
+ return out;
80
+ }
81
+ export function adminPage(options) {
82
+ const { title, body, flash, flashTone, hint, basePath, extraCss, shellMaxWidth, wrapBody = true, bodyAttrs, logoutAction = joinBasePath(resolveBasePath(basePath), 'logout'), } = options;
83
+ const flashClass = flash ? resolveFlashClass(flash, flashTone) : 'ok';
84
+ const shellRule = shellMaxWidth && isSafeCssLength(shellMaxWidth)
85
+ ? '\n.page-shell { max-width: ' + shellMaxWidth.trim() + '; }'
86
+ : '';
87
+ const styles = SHARED_CSS + shellRule + (extraCss ? '\n' + extraCss : '');
88
+ const main = wrapBody
89
+ ? `<div class="panel-card">
90
+ ${body}
91
+ </div>`
92
+ : body;
93
+ return `<!DOCTYPE html>
94
+ <html lang="en">
95
+ <head>
96
+ <meta charset="utf-8">
97
+ <meta name="viewport" content="width=device-width, initial-scale=1">
98
+ <title>${escapeHtml(title)}</title>
99
+ <style>${styles}</style>
100
+ </head>
101
+ <body class="admin-body"${bodyAttributesToString(bodyAttrs)}>
102
+ <div class="page-shell">
103
+ <header class="page-header">
104
+ <h1>${escapeHtml(title)}</h1>
105
+ ${hint ? `<p class="hint">${hint}</p>` : ''}
106
+ </header>
107
+ ${flash ? `<p class="${flashClass}">${escapeHtml(flash)}</p>` : ''}
108
+ ${main}
109
+ <form method="post" action="${escapeHtml(logoutAction)}" style="margin-top:0.75rem">
110
+ <button type="submit" class="btn-ghost">Sign out</button>
111
+ </form>
112
+ </div>
113
+ </body>
114
+ </html>`;
115
+ }
116
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { adminAuthCookie, DEFAULT_COOKIE_NAME, hexSha256, isAuthenticated, loginResponse, logoutResponse, timingSafeEqual, } from './auth.js';
2
+ export { SHARED_CSS } from './css.js';
3
+ export { adminForm, formText, parseAdminForm, type CheckboxField, type Field, type FieldOption, type FieldsetField, type HtmlField, type NumberField, type RadioField, type SelectField, type TextareaField, type TextField, } from './form.js';
4
+ export { createAdmin, type AdminHandler, type AdminRenderResult, type AdminRouteContext, type AdminSaveResult, type CreateAdminOptions, } from './handler.js';
5
+ export { adminDisabledResponse, adminPage, escapeHtml, html, isSafeCssLength, loginPageHtml, redirect, resolveFlashClass, type AdminPageOptions, type FlashTone, } from './html.js';
6
+ export { cookiePathForBase, joinBasePath, normalizeBasePath, normalizePathname, relativeToBase, resolveBasePath, } from './path.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EACL,SAAS,EACT,QAAQ,EACR,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,qBAAqB,EACrB,SAAS,EACT,UAAU,EACV,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,SAAS,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { adminAuthCookie, DEFAULT_COOKIE_NAME, hexSha256, isAuthenticated, loginResponse, logoutResponse, timingSafeEqual, } from './auth.js';
2
+ export { SHARED_CSS } from './css.js';
3
+ export { adminForm, formText, parseAdminForm, } from './form.js';
4
+ export { createAdmin, } from './handler.js';
5
+ export { adminDisabledResponse, adminPage, escapeHtml, html, isSafeCssLength, loginPageHtml, redirect, resolveFlashClass, } from './html.js';
6
+ export { cookiePathForBase, joinBasePath, normalizeBasePath, normalizePathname, relativeToBase, resolveBasePath, } from './path.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EACL,SAAS,EACT,QAAQ,EACR,cAAc,GAWf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,WAAW,GAMZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,qBAAqB,EACrB,SAAS,EACT,UAAU,EACV,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,iBAAiB,GAGlB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,WAAW,CAAC"}
package/dist/path.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ Normalize admin mount: `""` and `"/"` → root; strip trailing slashes otherwise.
3
+ */
4
+ export declare function normalizeBasePath(basePath: string): string;
5
+ /**
6
+ Resolve mount for low-level helpers: omitted → legacy `/admin`;
7
+ `""` / `"/"` → site root.
8
+ */
9
+ export declare function resolveBasePath(basePath?: string): string;
10
+ /**
11
+ Join mount + segments into an absolute path (always starts with `/` except empty join → `/`).
12
+ */
13
+ export declare function joinBasePath(basePath: string, ...segments: string[]): string;
14
+ /**
15
+ Cookie `Path` attribute for a mount (`/` when root).
16
+ */
17
+ export declare function cookiePathForBase(basePath: string): string;
18
+ /**
19
+ Strip trailing slashes; empty → `/`.
20
+ */
21
+ export declare function normalizePathname(pathname: string): string;
22
+ /**
23
+ Path relative to the admin mount, or `undefined` if outside the mount.
24
+ Root mount (`""`): relative is the full pathname (always "under" for known-route checks).
25
+ Non-root: `undefined` when pathname is not `{base}` or `{base}/…`.
26
+ */
27
+ export declare function relativeToBase(pathname: string, basePath: string): string | undefined;
28
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAqBA;;EAEE;AACF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED;;;EAGE;AACF,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;EAEE;AACF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAa5E;AAED;;EAEE;AACF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;EAEE;AACF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;EAIE;AACF,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAYpB"}
package/dist/path.js ADDED
@@ -0,0 +1,82 @@
1
+ function stripTrailingSlashes(value) {
2
+ let end = value.length;
3
+ while (end > 0 && value.startsWith('/', end - 1)) {
4
+ end -= 1;
5
+ }
6
+ return value.slice(0, end);
7
+ }
8
+ function stripEdgeSlashes(value) {
9
+ let start = 0;
10
+ const endLimit = value.length;
11
+ while (start < endLimit && value.startsWith('/', start)) {
12
+ start += 1;
13
+ }
14
+ let end = endLimit;
15
+ while (end > start && value.startsWith('/', end - 1)) {
16
+ end -= 1;
17
+ }
18
+ return value.slice(start, end);
19
+ }
20
+ /**
21
+ Normalize admin mount: `""` and `"/"` → root; strip trailing slashes otherwise.
22
+ */
23
+ export function normalizeBasePath(basePath) {
24
+ return basePath === '' || basePath === '/'
25
+ ? ''
26
+ : stripTrailingSlashes(basePath);
27
+ }
28
+ /**
29
+ Resolve mount for low-level helpers: omitted → legacy `/admin`;
30
+ `""` / `"/"` → site root.
31
+ */
32
+ export function resolveBasePath(basePath) {
33
+ return basePath === undefined ? '/admin' : normalizeBasePath(basePath);
34
+ }
35
+ /**
36
+ Join mount + segments into an absolute path (always starts with `/` except empty join → `/`).
37
+ */
38
+ export function joinBasePath(basePath, ...segments) {
39
+ const base = normalizeBasePath(basePath);
40
+ const rest = segments
41
+ .map((segment) => stripEdgeSlashes(segment))
42
+ .filter(Boolean)
43
+ .join('/');
44
+ if (!base && !rest) {
45
+ return '/';
46
+ }
47
+ if (!base) {
48
+ return `/${rest}`;
49
+ }
50
+ return rest ? `${base}/${rest}` : base;
51
+ }
52
+ /**
53
+ Cookie `Path` attribute for a mount (`/` when root).
54
+ */
55
+ export function cookiePathForBase(basePath) {
56
+ return normalizeBasePath(basePath) || '/';
57
+ }
58
+ /**
59
+ Strip trailing slashes; empty → `/`.
60
+ */
61
+ export function normalizePathname(pathname) {
62
+ return stripTrailingSlashes(pathname) || '/';
63
+ }
64
+ /**
65
+ Path relative to the admin mount, or `undefined` if outside the mount.
66
+ Root mount (`""`): relative is the full pathname (always "under" for known-route checks).
67
+ Non-root: `undefined` when pathname is not `{base}` or `{base}/…`.
68
+ */
69
+ export function relativeToBase(pathname, basePath) {
70
+ const path = normalizePathname(pathname);
71
+ const base = normalizeBasePath(basePath);
72
+ if (!base) {
73
+ return path;
74
+ }
75
+ if (path === base) {
76
+ return '/';
77
+ }
78
+ return path.startsWith(`${base}/`)
79
+ ? path.slice(base.length) || '/'
80
+ : undefined;
81
+ }
82
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"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"}