@reddoorla/maintenance 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/forms/index.d.ts +54 -10
- package/dist/forms/index.js +46 -0
- package/dist/forms/index.js.map +1 -1
- package/package.json +10 -1
package/dist/forms/index.d.ts
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import { F as FormType } from '../types-RXY-vY-5.js';
|
|
2
2
|
export { S as SUBMISSION_FORM_TYPES } from '../types-RXY-vY-5.js';
|
|
3
|
+
import { RequestEvent, ActionFailure } from '@sveltejs/kit';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The JSON a fleet site forwards to the dashboard ingest endpoint. Typed fields
|
|
6
7
|
* are optional; the index signature lets a site include its own extra fields
|
|
7
8
|
* (e.g. `company`) which the dashboard normalizer captures into `extraFields`.
|
|
9
|
+
*
|
|
10
|
+
* Each typed field allows `string | undefined` (not just `string`) so a
|
|
11
|
+
* `buildPayload` mapping can use the idiomatic `form.get("name")?.toString()`
|
|
12
|
+
* pattern under `exactOptionalPropertyTypes` without a cast — an absent field
|
|
13
|
+
* and an explicit `undefined` both serialize away in the JSON body.
|
|
8
14
|
*/
|
|
9
15
|
type SubmissionPayload = {
|
|
10
|
-
formType?: FormType | string;
|
|
11
|
-
name?: string;
|
|
12
|
-
firstName?: string;
|
|
13
|
-
lastName?: string;
|
|
14
|
-
email?: string;
|
|
15
|
-
phone?: string;
|
|
16
|
-
message?: string;
|
|
17
|
-
sourceUrl?: string;
|
|
18
|
-
utm?: string;
|
|
16
|
+
formType?: FormType | string | undefined;
|
|
17
|
+
name?: string | undefined;
|
|
18
|
+
firstName?: string | undefined;
|
|
19
|
+
lastName?: string | undefined;
|
|
20
|
+
email?: string | undefined;
|
|
21
|
+
phone?: string | undefined;
|
|
22
|
+
message?: string | undefined;
|
|
23
|
+
sourceUrl?: string | undefined;
|
|
24
|
+
utm?: string | undefined;
|
|
19
25
|
[key: string]: unknown;
|
|
20
26
|
};
|
|
21
27
|
type IngestClientResult = {
|
|
@@ -61,4 +67,42 @@ declare const MIN_FILL_MS = 2000;
|
|
|
61
67
|
*/
|
|
62
68
|
declare function screenSubmission(input: ScreenInput): ScreenResult;
|
|
63
69
|
|
|
64
|
-
|
|
70
|
+
/** Endpoint + token for the dashboard ingest, read per-request from site env. */
|
|
71
|
+
type IngestActionConfig = {
|
|
72
|
+
url?: string;
|
|
73
|
+
token?: string;
|
|
74
|
+
};
|
|
75
|
+
type CreateIngestActionOptions = {
|
|
76
|
+
/** Stamped onto every payload as `formType` (a SUBMISSION_FORM_TYPES value). */
|
|
77
|
+
formType: string;
|
|
78
|
+
/** Read at call time so SvelteKit's dynamic private env resolves per-request. */
|
|
79
|
+
getConfig: () => IngestActionConfig;
|
|
80
|
+
/**
|
|
81
|
+
* Map this form's fields to a payload. The factory's `formType` is always
|
|
82
|
+
* authoritative and cannot be overridden by `buildPayload`.
|
|
83
|
+
*/
|
|
84
|
+
buildPayload: (form: FormData, event: RequestEvent) => SubmissionPayload;
|
|
85
|
+
/** Honeypot input name. Default "bot-field". */
|
|
86
|
+
botFieldName?: string;
|
|
87
|
+
/** Hidden timestamp input name (planted in `load`). Default "ts". */
|
|
88
|
+
tsFieldName?: string;
|
|
89
|
+
/** fail(500) copy when env vars are unset. */
|
|
90
|
+
unavailableMessage?: string;
|
|
91
|
+
/** fail(502) copy when the ingest endpoint rejects/errors. */
|
|
92
|
+
errorMessage?: string;
|
|
93
|
+
/** Injectable clock for tests. Default Date.now. */
|
|
94
|
+
now?: () => number;
|
|
95
|
+
};
|
|
96
|
+
type IngestActionData = {
|
|
97
|
+
success: true;
|
|
98
|
+
} | ActionFailure<{
|
|
99
|
+
error: string;
|
|
100
|
+
}>;
|
|
101
|
+
/**
|
|
102
|
+
* Build a SvelteKit `default` form action that screens for bots, forwards the
|
|
103
|
+
* submission to the dashboard ingest endpoint, and returns SvelteKit-shaped
|
|
104
|
+
* results. The per-form field mapping is the only thing a site must supply.
|
|
105
|
+
*/
|
|
106
|
+
declare function createIngestAction(opts: CreateIngestActionOptions): (event: RequestEvent) => Promise<IngestActionData>;
|
|
107
|
+
|
|
108
|
+
export { type CreateIngestActionOptions, FormType, type IngestActionConfig, type IngestActionData, type IngestClientResult, MIN_FILL_MS, type ScreenInput, type ScreenResult, type SubmissionPayload, type SubmitToIngestOptions, createIngestAction, screenSubmission, submitToIngest };
|
package/dist/forms/index.js
CHANGED
|
@@ -42,9 +42,55 @@ function screenSubmission(input) {
|
|
|
42
42
|
}
|
|
43
43
|
return { ok: true };
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
// src/forms/action.ts
|
|
47
|
+
import { fail } from "@sveltejs/kit";
|
|
48
|
+
function createIngestAction(opts) {
|
|
49
|
+
const botFieldName = opts.botFieldName ?? "bot-field";
|
|
50
|
+
const tsFieldName = opts.tsFieldName ?? "ts";
|
|
51
|
+
const now = opts.now ?? Date.now;
|
|
52
|
+
const unavailable = opts.unavailableMessage ?? "This form is temporarily unavailable. Please email us directly.";
|
|
53
|
+
const failed = opts.errorMessage ?? "Something went wrong sending your message. Please try again.";
|
|
54
|
+
return async (event) => {
|
|
55
|
+
let form;
|
|
56
|
+
try {
|
|
57
|
+
form = await event.request.formData();
|
|
58
|
+
} catch {
|
|
59
|
+
console.error(`[forms-ingest] ${opts.formType}: could not parse form body`);
|
|
60
|
+
return fail(400, { error: failed });
|
|
61
|
+
}
|
|
62
|
+
const screen = screenSubmission({
|
|
63
|
+
botField: form.get(botFieldName)?.toString() ?? null,
|
|
64
|
+
elapsedMs: elapsedMs(form.get(tsFieldName), now)
|
|
65
|
+
});
|
|
66
|
+
if (!screen.ok) return { success: true };
|
|
67
|
+
const { url, token } = opts.getConfig();
|
|
68
|
+
if (!url || !token) {
|
|
69
|
+
console.error(`[forms-ingest] config missing for formType=${opts.formType}`);
|
|
70
|
+
return fail(500, { error: unavailable });
|
|
71
|
+
}
|
|
72
|
+
const result = await submitToIngest({
|
|
73
|
+
url,
|
|
74
|
+
token,
|
|
75
|
+
fetch: event.fetch,
|
|
76
|
+
payload: { ...opts.buildPayload(form, event), formType: opts.formType }
|
|
77
|
+
});
|
|
78
|
+
if (!result.ok) {
|
|
79
|
+
console.error(`[forms-ingest] ${opts.formType} \u2192 ${result.status}: ${result.error}`);
|
|
80
|
+
return fail(502, { error: failed });
|
|
81
|
+
}
|
|
82
|
+
return { success: true };
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function elapsedMs(tsRaw, now) {
|
|
86
|
+
const ts = Number(tsRaw);
|
|
87
|
+
if (!Number.isFinite(ts) || ts <= 0) return null;
|
|
88
|
+
return now() - ts;
|
|
89
|
+
}
|
|
45
90
|
export {
|
|
46
91
|
MIN_FILL_MS,
|
|
47
92
|
SUBMISSION_FORM_TYPES,
|
|
93
|
+
createIngestAction,
|
|
48
94
|
screenSubmission,
|
|
49
95
|
submitToIngest
|
|
50
96
|
};
|
package/dist/forms/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/forms/types.ts","../../src/forms/client.ts"],"sourcesContent":["/**\n * Form-type enum, kept in a leaf module (no Airtable/Resend imports) so it can\n * be shared with fleet sites via the `@reddoorla/maintenance/forms` subpath\n * without dragging server SDKs into a site bundle.\n */\nexport const SUBMISSION_FORM_TYPES = [\n \"contact\",\n \"inquiry\",\n \"newsletter\",\n \"rsvp\",\n \"reserve\",\n] as const;\nexport type FormType = (typeof SUBMISSION_FORM_TYPES)[number];\n","import { SUBMISSION_FORM_TYPES, type FormType } from \"./types.js\";\n\n/**\n * The JSON a fleet site forwards to the dashboard ingest endpoint. Typed fields\n * are optional; the index signature lets a site include its own extra fields\n * (e.g. `company`) which the dashboard normalizer captures into `extraFields`.\n */\nexport type SubmissionPayload = {\n formType?: FormType | string;\n name?: string;\n firstName?: string;\n lastName?: string;\n email?: string;\n phone?: string;\n message?: string;\n sourceUrl?: string;\n utm?: string;\n [key: string]: unknown;\n};\n\nexport type IngestClientResult =\n | { ok: true; id: string }\n | { ok: false; status: number; error: string };\n\nexport type SubmitToIngestOptions = {\n /** Full ingest endpoint incl. the site slug, e.g. https://…/api/forms/reddoor */\n url: string;\n /** The shared FORMS_INGEST_TOKEN. */\n token: string;\n payload: SubmissionPayload;\n /** Injectable fetch (pass SvelteKit's `event.fetch`); defaults to global fetch. */\n fetch?: typeof fetch;\n};\n\n/**\n * Forward a submission to the dashboard ingest endpoint. Never throws — a network\n * failure or a non-2xx response is returned as `{ ok: false }` so the caller can\n * show a friendly error rather than a 500.\n */\nexport async function submitToIngest(opts: SubmitToIngestOptions): Promise<IngestClientResult> {\n const doFetch = opts.fetch ?? fetch;\n let res: Response;\n try {\n res = await doFetch(opts.url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"x-forms-token\": opts.token },\n body: JSON.stringify(opts.payload),\n });\n } catch (err) {\n return { ok: false, status: 0, error: `network error: ${String(err)}` };\n }\n let body: unknown = null;\n try {\n body = await res.json();\n } catch {\n // non-JSON response — fall through to the error path\n }\n const obj = body && typeof body === \"object\" ? (body as Record<string, unknown>) : null;\n if (res.ok && obj && obj.ok === true) {\n return { ok: true, id: String(obj.id ?? \"\") };\n }\n const error = obj && typeof obj.error === \"string\" ? obj.error : `ingest failed (${res.status})`;\n return { ok: false, status: res.status, error };\n}\n\nexport type ScreenInput = { botField?: string | null; elapsedMs?: number | null };\nexport type ScreenResult = { ok: true } | { ok: false; reason: \"honeypot\" | \"too-fast\" };\n\n/** Minimum plausible fill time; faster than this reads as a bot. */\nexport const MIN_FILL_MS = 2000;\n\n/**\n * Cheap bot screen for the site action. A filled honeypot is a bot; a submission\n * faster than MIN_FILL_MS is a bot. Missing timing data (null) is NOT a rejection\n * — a prerendered/cached page can't plant a fresh timestamp, and the honeypot\n * remains the primary signal.\n */\nexport function screenSubmission(input: ScreenInput): ScreenResult {\n if (typeof input.botField === \"string\" && input.botField.trim().length > 0) {\n return { ok: false, reason: \"honeypot\" };\n }\n if (\n typeof input.elapsedMs === \"number\" &&\n input.elapsedMs >= 0 &&\n input.elapsedMs < MIN_FILL_MS\n ) {\n return { ok: false, reason: \"too-fast\" };\n }\n return { ok: true };\n}\n\nexport { SUBMISSION_FORM_TYPES, type FormType };\n"],"mappings":";AAKO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AC4BA,eAAsB,eAAe,MAA0D;AAC7F,QAAM,UAAU,KAAK,SAAS;AAC9B,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,QAAQ,KAAK,KAAK;AAAA,MAC5B,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,iBAAiB,KAAK,MAAM;AAAA,MAC3E,MAAM,KAAK,UAAU,KAAK,OAAO;AAAA,IACnC,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,OAAO,kBAAkB,OAAO,GAAG,CAAC,GAAG;AAAA,EACxE;AACA,MAAI,OAAgB;AACpB,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AAAA,EAER;AACA,QAAM,MAAM,QAAQ,OAAO,SAAS,WAAY,OAAmC;AACnF,MAAI,IAAI,MAAM,OAAO,IAAI,OAAO,MAAM;AACpC,WAAO,EAAE,IAAI,MAAM,IAAI,OAAO,IAAI,MAAM,EAAE,EAAE;AAAA,EAC9C;AACA,QAAM,QAAQ,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,kBAAkB,IAAI,MAAM;AAC7F,SAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,QAAQ,MAAM;AAChD;AAMO,IAAM,cAAc;AAQpB,SAAS,iBAAiB,OAAkC;AACjE,MAAI,OAAO,MAAM,aAAa,YAAY,MAAM,SAAS,KAAK,EAAE,SAAS,GAAG;AAC1E,WAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAAA,EACzC;AACA,MACE,OAAO,MAAM,cAAc,YAC3B,MAAM,aAAa,KACnB,MAAM,YAAY,aAClB;AACA,WAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAAA,EACzC;AACA,SAAO,EAAE,IAAI,KAAK;AACpB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/forms/types.ts","../../src/forms/client.ts","../../src/forms/action.ts"],"sourcesContent":["/**\n * Form-type enum, kept in a leaf module (no Airtable/Resend imports) so it can\n * be shared with fleet sites via the `@reddoorla/maintenance/forms` subpath\n * without dragging server SDKs into a site bundle.\n */\nexport const SUBMISSION_FORM_TYPES = [\n \"contact\",\n \"inquiry\",\n \"newsletter\",\n \"rsvp\",\n \"reserve\",\n] as const;\nexport type FormType = (typeof SUBMISSION_FORM_TYPES)[number];\n","import { SUBMISSION_FORM_TYPES, type FormType } from \"./types.js\";\n\n/**\n * The JSON a fleet site forwards to the dashboard ingest endpoint. Typed fields\n * are optional; the index signature lets a site include its own extra fields\n * (e.g. `company`) which the dashboard normalizer captures into `extraFields`.\n *\n * Each typed field allows `string | undefined` (not just `string`) so a\n * `buildPayload` mapping can use the idiomatic `form.get(\"name\")?.toString()`\n * pattern under `exactOptionalPropertyTypes` without a cast — an absent field\n * and an explicit `undefined` both serialize away in the JSON body.\n */\nexport type SubmissionPayload = {\n formType?: FormType | string | undefined;\n name?: string | undefined;\n firstName?: string | undefined;\n lastName?: string | undefined;\n email?: string | undefined;\n phone?: string | undefined;\n message?: string | undefined;\n sourceUrl?: string | undefined;\n utm?: string | undefined;\n [key: string]: unknown;\n};\n\nexport type IngestClientResult =\n | { ok: true; id: string }\n | { ok: false; status: number; error: string };\n\nexport type SubmitToIngestOptions = {\n /** Full ingest endpoint incl. the site slug, e.g. https://…/api/forms/reddoor */\n url: string;\n /** The shared FORMS_INGEST_TOKEN. */\n token: string;\n payload: SubmissionPayload;\n /** Injectable fetch (pass SvelteKit's `event.fetch`); defaults to global fetch. */\n fetch?: typeof fetch;\n};\n\n/**\n * Forward a submission to the dashboard ingest endpoint. Never throws — a network\n * failure or a non-2xx response is returned as `{ ok: false }` so the caller can\n * show a friendly error rather than a 500.\n */\nexport async function submitToIngest(opts: SubmitToIngestOptions): Promise<IngestClientResult> {\n const doFetch = opts.fetch ?? fetch;\n let res: Response;\n try {\n res = await doFetch(opts.url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"x-forms-token\": opts.token },\n body: JSON.stringify(opts.payload),\n });\n } catch (err) {\n return { ok: false, status: 0, error: `network error: ${String(err)}` };\n }\n let body: unknown = null;\n try {\n body = await res.json();\n } catch {\n // non-JSON response — fall through to the error path\n }\n const obj = body && typeof body === \"object\" ? (body as Record<string, unknown>) : null;\n if (res.ok && obj && obj.ok === true) {\n return { ok: true, id: String(obj.id ?? \"\") };\n }\n const error = obj && typeof obj.error === \"string\" ? obj.error : `ingest failed (${res.status})`;\n return { ok: false, status: res.status, error };\n}\n\nexport type ScreenInput = { botField?: string | null; elapsedMs?: number | null };\nexport type ScreenResult = { ok: true } | { ok: false; reason: \"honeypot\" | \"too-fast\" };\n\n/** Minimum plausible fill time; faster than this reads as a bot. */\nexport const MIN_FILL_MS = 2000;\n\n/**\n * Cheap bot screen for the site action. A filled honeypot is a bot; a submission\n * faster than MIN_FILL_MS is a bot. Missing timing data (null) is NOT a rejection\n * — a prerendered/cached page can't plant a fresh timestamp, and the honeypot\n * remains the primary signal.\n */\nexport function screenSubmission(input: ScreenInput): ScreenResult {\n if (typeof input.botField === \"string\" && input.botField.trim().length > 0) {\n return { ok: false, reason: \"honeypot\" };\n }\n if (\n typeof input.elapsedMs === \"number\" &&\n input.elapsedMs >= 0 &&\n input.elapsedMs < MIN_FILL_MS\n ) {\n return { ok: false, reason: \"too-fast\" };\n }\n return { ok: true };\n}\n\nexport { SUBMISSION_FORM_TYPES, type FormType };\n","import { fail, type ActionFailure, type RequestEvent } from \"@sveltejs/kit\";\nimport { submitToIngest, screenSubmission, type SubmissionPayload } from \"./client.js\";\n\n/** Endpoint + token for the dashboard ingest, read per-request from site env. */\nexport type IngestActionConfig = { url?: string; token?: string };\n\nexport type CreateIngestActionOptions = {\n /** Stamped onto every payload as `formType` (a SUBMISSION_FORM_TYPES value). */\n formType: string;\n /** Read at call time so SvelteKit's dynamic private env resolves per-request. */\n getConfig: () => IngestActionConfig;\n /**\n * Map this form's fields to a payload. The factory's `formType` is always\n * authoritative and cannot be overridden by `buildPayload`.\n */\n buildPayload: (form: FormData, event: RequestEvent) => SubmissionPayload;\n /** Honeypot input name. Default \"bot-field\". */\n botFieldName?: string;\n /** Hidden timestamp input name (planted in `load`). Default \"ts\". */\n tsFieldName?: string;\n /** fail(500) copy when env vars are unset. */\n unavailableMessage?: string;\n /** fail(502) copy when the ingest endpoint rejects/errors. */\n errorMessage?: string;\n /** Injectable clock for tests. Default Date.now. */\n now?: () => number;\n};\n\nexport type IngestActionData = { success: true } | ActionFailure<{ error: string }>;\n\n/**\n * Build a SvelteKit `default` form action that screens for bots, forwards the\n * submission to the dashboard ingest endpoint, and returns SvelteKit-shaped\n * results. The per-form field mapping is the only thing a site must supply.\n */\nexport function createIngestAction(\n opts: CreateIngestActionOptions,\n): (event: RequestEvent) => Promise<IngestActionData> {\n const botFieldName = opts.botFieldName ?? \"bot-field\";\n const tsFieldName = opts.tsFieldName ?? \"ts\";\n const now = opts.now ?? Date.now;\n const unavailable =\n opts.unavailableMessage ?? \"This form is temporarily unavailable. Please email us directly.\";\n const failed =\n opts.errorMessage ?? \"Something went wrong sending your message. Please try again.\";\n\n return async (event) => {\n let form: FormData;\n try {\n form = await event.request.formData();\n } catch {\n console.error(`[forms-ingest] ${opts.formType}: could not parse form body`);\n return fail(400, { error: failed });\n }\n\n // Bot screen: a filled honeypot OR an implausibly fast fill is silently\n // accepted (return success, do NOT forward) so bots get no signal.\n const screen = screenSubmission({\n botField: form.get(botFieldName)?.toString() ?? null,\n elapsedMs: elapsedMs(form.get(tsFieldName), now),\n });\n if (!screen.ok) return { success: true };\n\n const { url, token } = opts.getConfig();\n if (!url || !token) {\n console.error(`[forms-ingest] config missing for formType=${opts.formType}`);\n return fail(500, { error: unavailable });\n }\n\n const result = await submitToIngest({\n url,\n token,\n fetch: event.fetch,\n payload: { ...opts.buildPayload(form, event), formType: opts.formType },\n });\n if (!result.ok) {\n console.error(`[forms-ingest] ${opts.formType} → ${result.status}: ${result.error}`);\n return fail(502, { error: failed });\n }\n return { success: true };\n };\n}\n\n// `FormDataEntryValue` is a DOM-lib global; this package compiles with only the\n// ES2022 lib + @types/node, where it is not in scope. Derive the type from the\n// in-scope `FormData.get` return instead — same value, no DOM-lib dependency.\nfunction elapsedMs(tsRaw: ReturnType<FormData[\"get\"]>, now: () => number): number | null {\n const ts = Number(tsRaw);\n if (!Number.isFinite(ts) || ts <= 0) return null;\n return now() - ts;\n}\n"],"mappings":";AAKO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACiCA,eAAsB,eAAe,MAA0D;AAC7F,QAAM,UAAU,KAAK,SAAS;AAC9B,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,QAAQ,KAAK,KAAK;AAAA,MAC5B,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,iBAAiB,KAAK,MAAM;AAAA,MAC3E,MAAM,KAAK,UAAU,KAAK,OAAO;AAAA,IACnC,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,OAAO,kBAAkB,OAAO,GAAG,CAAC,GAAG;AAAA,EACxE;AACA,MAAI,OAAgB;AACpB,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AAAA,EAER;AACA,QAAM,MAAM,QAAQ,OAAO,SAAS,WAAY,OAAmC;AACnF,MAAI,IAAI,MAAM,OAAO,IAAI,OAAO,MAAM;AACpC,WAAO,EAAE,IAAI,MAAM,IAAI,OAAO,IAAI,MAAM,EAAE,EAAE;AAAA,EAC9C;AACA,QAAM,QAAQ,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,kBAAkB,IAAI,MAAM;AAC7F,SAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,QAAQ,MAAM;AAChD;AAMO,IAAM,cAAc;AAQpB,SAAS,iBAAiB,OAAkC;AACjE,MAAI,OAAO,MAAM,aAAa,YAAY,MAAM,SAAS,KAAK,EAAE,SAAS,GAAG;AAC1E,WAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAAA,EACzC;AACA,MACE,OAAO,MAAM,cAAc,YAC3B,MAAM,aAAa,KACnB,MAAM,YAAY,aAClB;AACA,WAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAAA,EACzC;AACA,SAAO,EAAE,IAAI,KAAK;AACpB;;;AC9FA,SAAS,YAAmD;AAmCrD,SAAS,mBACd,MACoD;AACpD,QAAM,eAAe,KAAK,gBAAgB;AAC1C,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,MAAM,KAAK,OAAO,KAAK;AAC7B,QAAM,cACJ,KAAK,sBAAsB;AAC7B,QAAM,SACJ,KAAK,gBAAgB;AAEvB,SAAO,OAAO,UAAU;AACtB,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,MAAM,QAAQ,SAAS;AAAA,IACtC,QAAQ;AACN,cAAQ,MAAM,kBAAkB,KAAK,QAAQ,6BAA6B;AAC1E,aAAO,KAAK,KAAK,EAAE,OAAO,OAAO,CAAC;AAAA,IACpC;AAIA,UAAM,SAAS,iBAAiB;AAAA,MAC9B,UAAU,KAAK,IAAI,YAAY,GAAG,SAAS,KAAK;AAAA,MAChD,WAAW,UAAU,KAAK,IAAI,WAAW,GAAG,GAAG;AAAA,IACjD,CAAC;AACD,QAAI,CAAC,OAAO,GAAI,QAAO,EAAE,SAAS,KAAK;AAEvC,UAAM,EAAE,KAAK,MAAM,IAAI,KAAK,UAAU;AACtC,QAAI,CAAC,OAAO,CAAC,OAAO;AAClB,cAAQ,MAAM,8CAA8C,KAAK,QAAQ,EAAE;AAC3E,aAAO,KAAK,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,IACzC;AAEA,UAAM,SAAS,MAAM,eAAe;AAAA,MAClC;AAAA,MACA;AAAA,MACA,OAAO,MAAM;AAAA,MACb,SAAS,EAAE,GAAG,KAAK,aAAa,MAAM,KAAK,GAAG,UAAU,KAAK,SAAS;AAAA,IACxE,CAAC;AACD,QAAI,CAAC,OAAO,IAAI;AACd,cAAQ,MAAM,kBAAkB,KAAK,QAAQ,WAAM,OAAO,MAAM,KAAK,OAAO,KAAK,EAAE;AACnF,aAAO,KAAK,KAAK,EAAE,OAAO,OAAO,CAAC;AAAA,IACpC;AACA,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACF;AAKA,SAAS,UAAU,OAAoC,KAAkC;AACvF,QAAM,KAAK,OAAO,KAAK;AACvB,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,MAAM,EAAG,QAAO;AAC5C,SAAO,IAAI,IAAI;AACjB;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reddoorla/maintenance",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Canonical maintenance configs, audits, and recipes for the reddoor stack.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -72,9 +72,18 @@
|
|
|
72
72
|
"tinyglobby": "^0.2.10",
|
|
73
73
|
"typescript-eslint": "^8.59.1"
|
|
74
74
|
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"@sveltejs/kit": "^2.0.0"
|
|
77
|
+
},
|
|
78
|
+
"peerDependenciesMeta": {
|
|
79
|
+
"@sveltejs/kit": {
|
|
80
|
+
"optional": true
|
|
81
|
+
}
|
|
82
|
+
},
|
|
75
83
|
"devDependencies": {
|
|
76
84
|
"@changesets/cli": "^2.31.0",
|
|
77
85
|
"@netlify/functions": "^5.2.2",
|
|
86
|
+
"@sveltejs/kit": "^2.61.1",
|
|
78
87
|
"@types/mjml": "^4.7.4",
|
|
79
88
|
"@types/node": "^22.0.0",
|
|
80
89
|
"@vitest/coverage-v8": "^4.1.8",
|