@seedcord/kit 0.1.1-next.0 → 0.2.0-next.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/{CustomId-TT4MsOsD.d.mts → CustomId-CtpiL97C.d.mts} +2 -2
- package/dist/index.cjs +30 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +31 -2
- package/dist/index.mjs +30 -2
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.cjs +11 -0
- package/dist/internal.index.cjs.map +1 -1
- package/dist/internal.index.d.mts +12 -2
- package/dist/internal.index.mjs +11 -2
- package/dist/internal.index.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -310,5 +310,5 @@ type DecodedRoute<Defs extends readonly AnyCustomId[]> = { [Index in keyof Defs]
|
|
|
310
310
|
*/
|
|
311
311
|
declare function decodeFor<Defs extends readonly AnyCustomId[]>(defs: Defs, wire: string): DecodedRoute<Defs>;
|
|
312
312
|
//#endregion
|
|
313
|
-
export {
|
|
314
|
-
//# sourceMappingURL=CustomId-
|
|
313
|
+
export { CustomIdField as a, RowComponent as c, prefixOf as i, BuilderType as l, CustomId as n, DecodedParams as o, decodeFor as r, BuilderComponent as s, AnyCustomId as t, RowType as u };
|
|
314
|
+
//# sourceMappingURL=CustomId-CtpiL97C.d.mts.map
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_CustomId = require('./CustomId-NsInJKhD.cjs');
|
|
3
|
+
let _seedcord_errors = require("@seedcord/errors");
|
|
4
|
+
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
3
5
|
|
|
4
6
|
//#region src/stops/Fault.ts
|
|
5
7
|
/**
|
|
@@ -68,10 +70,36 @@ var Silence = class extends Error {
|
|
|
68
70
|
}
|
|
69
71
|
};
|
|
70
72
|
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/pagination/paginate.ts
|
|
75
|
+
/**
|
|
76
|
+
* Pure page math, usable headless. Clamps `page` into range and slices the window.
|
|
77
|
+
*
|
|
78
|
+
* @param items - The full list to page over.
|
|
79
|
+
* @param page - The requested zero-based page, clamped into `[0, totalPages - 1]`.
|
|
80
|
+
* @param perPage - The page size. Must be a positive integer.
|
|
81
|
+
* @returns The sliced {@link PageView}, with `totalPages` always known.
|
|
82
|
+
* @throws SeedcordRangeError when `perPage` is not a positive integer.
|
|
83
|
+
*/
|
|
84
|
+
function paginate(items, page, perPage) {
|
|
85
|
+
if (!Number.isInteger(perPage) || perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);
|
|
86
|
+
const totalPages = Math.max(1, Math.ceil(items.length / perPage));
|
|
87
|
+
const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);
|
|
88
|
+
const start = clamped * perPage;
|
|
89
|
+
return {
|
|
90
|
+
items: items.slice(start, start + perPage),
|
|
91
|
+
page: clamped,
|
|
92
|
+
perPage,
|
|
93
|
+
totalPages,
|
|
94
|
+
hasPrev: clamped > 0,
|
|
95
|
+
hasNext: clamped < totalPages - 1
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
71
99
|
//#endregion
|
|
72
100
|
//#region src/index.ts
|
|
73
101
|
/** Package version */
|
|
74
|
-
const version = "0.
|
|
102
|
+
const version = "0.2.0-next.0";
|
|
75
103
|
|
|
76
104
|
//#endregion
|
|
77
105
|
exports.BuilderComponent = require_CustomId.BuilderComponent;
|
|
@@ -80,5 +108,6 @@ exports.Fault = Fault;
|
|
|
80
108
|
exports.Notice = require_CustomId.Notice;
|
|
81
109
|
exports.RowComponent = require_CustomId.RowComponent;
|
|
82
110
|
exports.Silence = Silence;
|
|
111
|
+
exports.paginate = paginate;
|
|
83
112
|
exports.version = version;
|
|
84
113
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Notice","NoticeCard"],"sources":["../src/stops/Fault.ts","../src/stops/Silence.ts","../src/index.ts"],"sourcesContent":["import { Notice } from './Notice';\nimport { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\n\n/**\n * A generic fault you throw after catching an error you do not have a specific message for.\n *\n * The user sees a fixed generic reply with the tracking uuid, never the cause. `report` defaults to\n * `true`, so the framework logs it and publishes it to the `handledException` bus. Pass `report: false`\n * to show the generic reply without the bus publish. The original error is stored as the standard\n * `cause`, so the real stack reaches the webhook. For a fault the user should see a real message,\n * subclass {@link Notice} and write your own `render` instead.\n *\n * The framework also renders this for an unhandled throw, where it points the user at\n * `ctx.developerUsername`.\n *\n * @example\n * ```ts\n * import { Fault } from '@seedcord/kit';\n *\n * try {\n * await db.write(record);\n * } catch (cause) {\n * // user sees the generic reply with the uuid, the real error rides along as cause for the webhook\n * throw new Fault({ cause });\n *\n * // pass report: false to show the same reply without publishing to the handledException bus\n * // throw new Fault({ cause, report: false });\n * }\n * ```\n */\nexport class Fault extends Notice {\n public constructor(options?: { cause?: unknown; report?: boolean }) {\n super('A fault occurred', options?.cause === undefined ? undefined : { cause: options.cause });\n this.report = options?.report ?? true;\n }\n\n public render(ctx: RenderContext): ReplyResponse {\n const contact = ctx.developerUsername ?? 'the developer';\n const card = new NoticeCard(\n `Something went wrong. Please reach out to ${contact} with a way to reproduce the error and the following:\\n### UUID: \\`${ctx.uuid}\\``,\n 'Error'\n );\n return { components: [card.component] };\n }\n}\n","/**\n * Throw to stop a handler with no reply and no report.\n *\n * The framework boundary catches `Silence` before {@link Notice}, makes zero Discord calls, and stops.\n * Ideally you'd only throw this in `EventHandlers` (or `Gates` for those), because it doesn't make sense to\n * leave the user with no reply for an interaction.\n *\n * @example\n * ```ts\n * import { Silence } from '@seedcord/kit';\n *\n * // before any reply or defer, drop the interaction with no reply and no report\n * if (await isBlacklisted(interaction.user.id)) throw new Silence('blacklisted user');\n * ```\n */\nexport class Silence extends Error {\n /**\n * @param reason - Optional note written only to a debug log, never shown to the user or reported.\n */\n public constructor(public readonly reason?: string) {\n super(reason ?? 'Silence');\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","export { BuilderComponent, RowComponent } from '@components/Component';\nexport { type RowType, type BuilderType } from '@components/builderTypes';\nexport { Notice } from '@stops/Notice';\nexport { Fault } from '@stops/Fault';\nexport { Silence } from '@stops/Silence';\nexport { CustomId } from '@customId/CustomId';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Notice","NoticeCard","SeedcordRangeError","SeedcordErrorCode"],"sources":["../src/stops/Fault.ts","../src/stops/Silence.ts","../src/pagination/paginate.ts","../src/index.ts"],"sourcesContent":["import { Notice } from './Notice';\nimport { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\n\n/**\n * A generic fault you throw after catching an error you do not have a specific message for.\n *\n * The user sees a fixed generic reply with the tracking uuid, never the cause. `report` defaults to\n * `true`, so the framework logs it and publishes it to the `handledException` bus. Pass `report: false`\n * to show the generic reply without the bus publish. The original error is stored as the standard\n * `cause`, so the real stack reaches the webhook. For a fault the user should see a real message,\n * subclass {@link Notice} and write your own `render` instead.\n *\n * The framework also renders this for an unhandled throw, where it points the user at\n * `ctx.developerUsername`.\n *\n * @example\n * ```ts\n * import { Fault } from '@seedcord/kit';\n *\n * try {\n * await db.write(record);\n * } catch (cause) {\n * // user sees the generic reply with the uuid, the real error rides along as cause for the webhook\n * throw new Fault({ cause });\n *\n * // pass report: false to show the same reply without publishing to the handledException bus\n * // throw new Fault({ cause, report: false });\n * }\n * ```\n */\nexport class Fault extends Notice {\n public constructor(options?: { cause?: unknown; report?: boolean }) {\n super('A fault occurred', options?.cause === undefined ? undefined : { cause: options.cause });\n this.report = options?.report ?? true;\n }\n\n public render(ctx: RenderContext): ReplyResponse {\n const contact = ctx.developerUsername ?? 'the developer';\n const card = new NoticeCard(\n `Something went wrong. Please reach out to ${contact} with a way to reproduce the error and the following:\\n### UUID: \\`${ctx.uuid}\\``,\n 'Error'\n );\n return { components: [card.component] };\n }\n}\n","/**\n * Throw to stop a handler with no reply and no report.\n *\n * The framework boundary catches `Silence` before {@link Notice}, makes zero Discord calls, and stops.\n * Ideally you'd only throw this in `EventHandlers` (or `Gates` for those), because it doesn't make sense to\n * leave the user with no reply for an interaction.\n *\n * @example\n * ```ts\n * import { Silence } from '@seedcord/kit';\n *\n * // before any reply or defer, drop the interaction with no reply and no report\n * if (await isBlacklisted(interaction.user.id)) throw new Silence('blacklisted user');\n * ```\n */\nexport class Silence extends Error {\n /**\n * @param reason - Optional note written only to a debug log, never shown to the user or reported.\n */\n public constructor(public readonly reason?: string) {\n super(reason ?? 'Silence');\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError } from '@seedcord/errors/internal';\n\nimport type { PageView } from './PageView';\n\n/**\n * Pure page math, usable headless. Clamps `page` into range and slices the window.\n *\n * @param items - The full list to page over.\n * @param page - The requested zero-based page, clamped into `[0, totalPages - 1]`.\n * @param perPage - The page size. Must be a positive integer.\n * @returns The sliced {@link PageView}, with `totalPages` always known.\n * @throws SeedcordRangeError when `perPage` is not a positive integer.\n */\nexport function paginate<Item>(items: readonly Item[], page: number, perPage: number): PageView<Item> {\n if (!Number.isInteger(perPage) || perPage <= 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);\n }\n\n const totalPages = Math.max(1, Math.ceil(items.length / perPage));\n // a headless caller can pass a non-integer or out-of-range page.\n const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);\n const start = clamped * perPage;\n\n return {\n items: items.slice(start, start + perPage),\n page: clamped,\n perPage,\n totalPages,\n hasPrev: clamped > 0,\n hasNext: clamped < totalPages - 1\n };\n}\n","export { BuilderComponent, RowComponent } from '@components/Component';\nexport { type RowType, type BuilderType } from '@components/builderTypes';\nexport { Notice } from '@stops/Notice';\nexport { Fault } from '@stops/Fault';\nexport { Silence } from '@stops/Silence';\nexport { CustomId } from '@customId/CustomId';\nexport { paginate } from '@pagination/paginate';\nexport { type PageView } from '@pagination/PageView';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,QAAb,cAA2BA,wBAAO;CAC9B,AAAO,YAAY,SAAiD;EAChE,MAAM,oBAAoB,SAAS,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EAC7F,KAAK,SAAS,SAAS,UAAU;CACrC;CAEA,AAAO,OAAO,KAAmC;EAM7C,OAAO,EAAE,YAAY,CAAC,IAJLC,4BACb,6CAFY,IAAI,qBAAqB,gBAEgB,qEAAqE,IAAI,KAAK,KACnI,OAEqB,CAAC,CAAC,SAAS,EAAE;CAC1C;AACJ;;;;;;;;;;;;;;;;;;;AC/BA,IAAa,UAAb,cAA6B,MAAM;CAII;;;;CAAnC,AAAO,YAAY,AAAgB,QAAiB;EAChD,MAAM,UAAU,SAAS;EADM;EAG/B,MAAM,kBAAkB,MAAM,KAAK,WAAW;CAClD;AACJ;;;;;;;;;;;;;ACVA,SAAgB,SAAe,OAAwB,MAAc,SAAiC;CAClG,IAAI,CAAC,OAAO,UAAU,OAAO,KAAK,WAAW,GACzC,MAAM,IAAIC,6CAAmBC,mCAAkB,0BAA0B,CAAC,OAAO,CAAC;CAGtF,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,OAAO,CAAC;CAEhE,MAAM,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC;CACtE,MAAM,QAAQ,UAAU;CAExB,OAAO;EACH,OAAO,MAAM,MAAM,OAAO,QAAQ,OAAO;EACzC,MAAM;EACN;EACA;EACA,SAAS,UAAU;EACnB,SAAS,UAAU,aAAa;CACpC;AACJ;;;;;ACtBA,MAAa"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { c as RowComponent, l as BuilderType, n as CustomId, s as BuilderComponent, u as RowType } from "./CustomId-CtpiL97C.mjs";
|
|
2
2
|
import { RenderContext, ReplyResponse } from "@seedcord/types";
|
|
3
3
|
|
|
4
4
|
//#region src/stops/Notice.d.ts
|
|
@@ -127,9 +127,38 @@ declare class Silence extends Error {
|
|
|
127
127
|
constructor(reason?: string | undefined);
|
|
128
128
|
}
|
|
129
129
|
//#endregion
|
|
130
|
+
//#region src/pagination/PageView.d.ts
|
|
131
|
+
/**
|
|
132
|
+
* One rendered page of a paginated list.
|
|
133
|
+
*
|
|
134
|
+
* @typeParam Item - The item type.
|
|
135
|
+
*/
|
|
136
|
+
interface PageView<Item> {
|
|
137
|
+
items: Item[];
|
|
138
|
+
/** Zero-based, already clamped into range. */
|
|
139
|
+
page: number;
|
|
140
|
+
perPage: number;
|
|
141
|
+
/** The total page count, or `undefined` for a cursor source that has no cheap total. */
|
|
142
|
+
totalPages?: number;
|
|
143
|
+
hasPrev: boolean;
|
|
144
|
+
hasNext: boolean;
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/pagination/paginate.d.ts
|
|
148
|
+
/**
|
|
149
|
+
* Pure page math, usable headless. Clamps `page` into range and slices the window.
|
|
150
|
+
*
|
|
151
|
+
* @param items - The full list to page over.
|
|
152
|
+
* @param page - The requested zero-based page, clamped into `[0, totalPages - 1]`.
|
|
153
|
+
* @param perPage - The page size. Must be a positive integer.
|
|
154
|
+
* @returns The sliced {@link PageView}, with `totalPages` always known.
|
|
155
|
+
* @throws SeedcordRangeError when `perPage` is not a positive integer.
|
|
156
|
+
*/
|
|
157
|
+
declare function paginate<Item>(items: readonly Item[], page: number, perPage: number): PageView<Item>;
|
|
158
|
+
//#endregion
|
|
130
159
|
//#region src/index.d.ts
|
|
131
160
|
/** Package version */
|
|
132
161
|
declare const version: string;
|
|
133
162
|
//#endregion
|
|
134
|
-
export { BuilderComponent, type BuilderType, CustomId, Fault, Notice, RowComponent, type RowType, Silence, version };
|
|
163
|
+
export { BuilderComponent, type BuilderType, CustomId, Fault, Notice, type PageView, RowComponent, type RowType, Silence, paginate, version };
|
|
135
164
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { a as Notice, i as NoticeCard, o as BuilderComponent, s as RowComponent, t as CustomId } from "./CustomId-BKioviDw.mjs";
|
|
2
|
+
import { SeedcordErrorCode } from "@seedcord/errors";
|
|
3
|
+
import { SeedcordRangeError } from "@seedcord/errors/internal";
|
|
2
4
|
|
|
3
5
|
//#region src/stops/Fault.ts
|
|
4
6
|
/**
|
|
@@ -67,11 +69,37 @@ var Silence = class extends Error {
|
|
|
67
69
|
}
|
|
68
70
|
};
|
|
69
71
|
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/pagination/paginate.ts
|
|
74
|
+
/**
|
|
75
|
+
* Pure page math, usable headless. Clamps `page` into range and slices the window.
|
|
76
|
+
*
|
|
77
|
+
* @param items - The full list to page over.
|
|
78
|
+
* @param page - The requested zero-based page, clamped into `[0, totalPages - 1]`.
|
|
79
|
+
* @param perPage - The page size. Must be a positive integer.
|
|
80
|
+
* @returns The sliced {@link PageView}, with `totalPages` always known.
|
|
81
|
+
* @throws SeedcordRangeError when `perPage` is not a positive integer.
|
|
82
|
+
*/
|
|
83
|
+
function paginate(items, page, perPage) {
|
|
84
|
+
if (!Number.isInteger(perPage) || perPage <= 0) throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);
|
|
85
|
+
const totalPages = Math.max(1, Math.ceil(items.length / perPage));
|
|
86
|
+
const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);
|
|
87
|
+
const start = clamped * perPage;
|
|
88
|
+
return {
|
|
89
|
+
items: items.slice(start, start + perPage),
|
|
90
|
+
page: clamped,
|
|
91
|
+
perPage,
|
|
92
|
+
totalPages,
|
|
93
|
+
hasPrev: clamped > 0,
|
|
94
|
+
hasNext: clamped < totalPages - 1
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
70
98
|
//#endregion
|
|
71
99
|
//#region src/index.ts
|
|
72
100
|
/** Package version */
|
|
73
|
-
const version = "0.
|
|
101
|
+
const version = "0.2.0-next.0";
|
|
74
102
|
|
|
75
103
|
//#endregion
|
|
76
|
-
export { BuilderComponent, CustomId, Fault, Notice, RowComponent, Silence, version };
|
|
104
|
+
export { BuilderComponent, CustomId, Fault, Notice, RowComponent, Silence, paginate, version };
|
|
77
105
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/stops/Fault.ts","../src/stops/Silence.ts","../src/index.ts"],"sourcesContent":["import { Notice } from './Notice';\nimport { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\n\n/**\n * A generic fault you throw after catching an error you do not have a specific message for.\n *\n * The user sees a fixed generic reply with the tracking uuid, never the cause. `report` defaults to\n * `true`, so the framework logs it and publishes it to the `handledException` bus. Pass `report: false`\n * to show the generic reply without the bus publish. The original error is stored as the standard\n * `cause`, so the real stack reaches the webhook. For a fault the user should see a real message,\n * subclass {@link Notice} and write your own `render` instead.\n *\n * The framework also renders this for an unhandled throw, where it points the user at\n * `ctx.developerUsername`.\n *\n * @example\n * ```ts\n * import { Fault } from '@seedcord/kit';\n *\n * try {\n * await db.write(record);\n * } catch (cause) {\n * // user sees the generic reply with the uuid, the real error rides along as cause for the webhook\n * throw new Fault({ cause });\n *\n * // pass report: false to show the same reply without publishing to the handledException bus\n * // throw new Fault({ cause, report: false });\n * }\n * ```\n */\nexport class Fault extends Notice {\n public constructor(options?: { cause?: unknown; report?: boolean }) {\n super('A fault occurred', options?.cause === undefined ? undefined : { cause: options.cause });\n this.report = options?.report ?? true;\n }\n\n public render(ctx: RenderContext): ReplyResponse {\n const contact = ctx.developerUsername ?? 'the developer';\n const card = new NoticeCard(\n `Something went wrong. Please reach out to ${contact} with a way to reproduce the error and the following:\\n### UUID: \\`${ctx.uuid}\\``,\n 'Error'\n );\n return { components: [card.component] };\n }\n}\n","/**\n * Throw to stop a handler with no reply and no report.\n *\n * The framework boundary catches `Silence` before {@link Notice}, makes zero Discord calls, and stops.\n * Ideally you'd only throw this in `EventHandlers` (or `Gates` for those), because it doesn't make sense to\n * leave the user with no reply for an interaction.\n *\n * @example\n * ```ts\n * import { Silence } from '@seedcord/kit';\n *\n * // before any reply or defer, drop the interaction with no reply and no report\n * if (await isBlacklisted(interaction.user.id)) throw new Silence('blacklisted user');\n * ```\n */\nexport class Silence extends Error {\n /**\n * @param reason - Optional note written only to a debug log, never shown to the user or reported.\n */\n public constructor(public readonly reason?: string) {\n super(reason ?? 'Silence');\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","export { BuilderComponent, RowComponent } from '@components/Component';\nexport { type RowType, type BuilderType } from '@components/builderTypes';\nexport { Notice } from '@stops/Notice';\nexport { Fault } from '@stops/Fault';\nexport { Silence } from '@stops/Silence';\nexport { CustomId } from '@customId/CustomId';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/stops/Fault.ts","../src/stops/Silence.ts","../src/pagination/paginate.ts","../src/index.ts"],"sourcesContent":["import { Notice } from './Notice';\nimport { NoticeCard } from './NoticeCard';\n\nimport type { RenderContext, ReplyResponse } from '@seedcord/types';\n\n/**\n * A generic fault you throw after catching an error you do not have a specific message for.\n *\n * The user sees a fixed generic reply with the tracking uuid, never the cause. `report` defaults to\n * `true`, so the framework logs it and publishes it to the `handledException` bus. Pass `report: false`\n * to show the generic reply without the bus publish. The original error is stored as the standard\n * `cause`, so the real stack reaches the webhook. For a fault the user should see a real message,\n * subclass {@link Notice} and write your own `render` instead.\n *\n * The framework also renders this for an unhandled throw, where it points the user at\n * `ctx.developerUsername`.\n *\n * @example\n * ```ts\n * import { Fault } from '@seedcord/kit';\n *\n * try {\n * await db.write(record);\n * } catch (cause) {\n * // user sees the generic reply with the uuid, the real error rides along as cause for the webhook\n * throw new Fault({ cause });\n *\n * // pass report: false to show the same reply without publishing to the handledException bus\n * // throw new Fault({ cause, report: false });\n * }\n * ```\n */\nexport class Fault extends Notice {\n public constructor(options?: { cause?: unknown; report?: boolean }) {\n super('A fault occurred', options?.cause === undefined ? undefined : { cause: options.cause });\n this.report = options?.report ?? true;\n }\n\n public render(ctx: RenderContext): ReplyResponse {\n const contact = ctx.developerUsername ?? 'the developer';\n const card = new NoticeCard(\n `Something went wrong. Please reach out to ${contact} with a way to reproduce the error and the following:\\n### UUID: \\`${ctx.uuid}\\``,\n 'Error'\n );\n return { components: [card.component] };\n }\n}\n","/**\n * Throw to stop a handler with no reply and no report.\n *\n * The framework boundary catches `Silence` before {@link Notice}, makes zero Discord calls, and stops.\n * Ideally you'd only throw this in `EventHandlers` (or `Gates` for those), because it doesn't make sense to\n * leave the user with no reply for an interaction.\n *\n * @example\n * ```ts\n * import { Silence } from '@seedcord/kit';\n *\n * // before any reply or defer, drop the interaction with no reply and no report\n * if (await isBlacklisted(interaction.user.id)) throw new Silence('blacklisted user');\n * ```\n */\nexport class Silence extends Error {\n /**\n * @param reason - Optional note written only to a debug log, never shown to the user or reported.\n */\n public constructor(public readonly reason?: string) {\n super(reason ?? 'Silence');\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","import { SeedcordErrorCode } from '@seedcord/errors';\nimport { SeedcordRangeError } from '@seedcord/errors/internal';\n\nimport type { PageView } from './PageView';\n\n/**\n * Pure page math, usable headless. Clamps `page` into range and slices the window.\n *\n * @param items - The full list to page over.\n * @param page - The requested zero-based page, clamped into `[0, totalPages - 1]`.\n * @param perPage - The page size. Must be a positive integer.\n * @returns The sliced {@link PageView}, with `totalPages` always known.\n * @throws SeedcordRangeError when `perPage` is not a positive integer.\n */\nexport function paginate<Item>(items: readonly Item[], page: number, perPage: number): PageView<Item> {\n if (!Number.isInteger(perPage) || perPage <= 0) {\n throw new SeedcordRangeError(SeedcordErrorCode.PaginationInvalidPerPage, [perPage]);\n }\n\n const totalPages = Math.max(1, Math.ceil(items.length / perPage));\n // a headless caller can pass a non-integer or out-of-range page.\n const clamped = Math.min(Math.max(Math.trunc(page), 0), totalPages - 1);\n const start = clamped * perPage;\n\n return {\n items: items.slice(start, start + perPage),\n page: clamped,\n perPage,\n totalPages,\n hasPrev: clamped > 0,\n hasNext: clamped < totalPages - 1\n };\n}\n","export { BuilderComponent, RowComponent } from '@components/Component';\nexport { type RowType, type BuilderType } from '@components/builderTypes';\nexport { Notice } from '@stops/Notice';\nexport { Fault } from '@stops/Fault';\nexport { Silence } from '@stops/Silence';\nexport { CustomId } from '@customId/CustomId';\nexport { paginate } from '@pagination/paginate';\nexport { type PageView } from '@pagination/PageView';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,QAAb,cAA2B,OAAO;CAC9B,AAAO,YAAY,SAAiD;EAChE,MAAM,oBAAoB,SAAS,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EAC7F,KAAK,SAAS,SAAS,UAAU;CACrC;CAEA,AAAO,OAAO,KAAmC;EAM7C,OAAO,EAAE,YAAY,CAAC,IAJL,WACb,6CAFY,IAAI,qBAAqB,gBAEgB,qEAAqE,IAAI,KAAK,KACnI,OAEqB,CAAC,CAAC,SAAS,EAAE;CAC1C;AACJ;;;;;;;;;;;;;;;;;;;AC/BA,IAAa,UAAb,cAA6B,MAAM;CAII;;;;CAAnC,AAAO,YAAY,AAAgB,QAAiB;EAChD,MAAM,UAAU,SAAS;EADM;EAG/B,MAAM,kBAAkB,MAAM,KAAK,WAAW;CAClD;AACJ;;;;;;;;;;;;;ACVA,SAAgB,SAAe,OAAwB,MAAc,SAAiC;CAClG,IAAI,CAAC,OAAO,UAAU,OAAO,KAAK,WAAW,GACzC,MAAM,IAAI,mBAAmB,kBAAkB,0BAA0B,CAAC,OAAO,CAAC;CAGtF,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,OAAO,CAAC;CAEhE,MAAM,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC;CACtE,MAAM,QAAQ,UAAU;CAExB,OAAO;EACH,OAAO,MAAM,MAAM,OAAO,QAAQ,OAAO;EACzC,MAAM;EACN;EACA;EACA,SAAS,UAAU;EACnB,SAAS,UAAU,aAAa;CACpC;AACJ;;;;;ACtBA,MAAa"}
|
package/dist/internal.index.cjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_CustomId = require('./CustomId-NsInJKhD.cjs');
|
|
3
3
|
|
|
4
|
+
//#region src/pagination/cursor.ts
|
|
5
|
+
const PAGE_MAX = 1e6;
|
|
6
|
+
const SLOT_MAX = 4;
|
|
7
|
+
/** Build the page cursor for a paginator. */
|
|
8
|
+
function pageCursor(prefix) {
|
|
9
|
+
return new require_CustomId.CustomId(prefix).int("page", 0, PAGE_MAX).int("slot", 0, 4);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
4
13
|
//#region src/customId/routing.ts
|
|
5
14
|
/**
|
|
6
15
|
* The route decorators store a handler's customId definitions here so the component base can decode
|
|
@@ -13,7 +22,9 @@ const ComponentDefsKey = Symbol("seedcord:customId:componentDefs");
|
|
|
13
22
|
//#endregion
|
|
14
23
|
exports.ComponentDefsKey = ComponentDefsKey;
|
|
15
24
|
exports.NoticeCard = require_CustomId.NoticeCard;
|
|
25
|
+
exports.PAGE_MAX = PAGE_MAX;
|
|
16
26
|
exports.decodeFor = require_CustomId.decodeFor;
|
|
27
|
+
exports.pageCursor = pageCursor;
|
|
17
28
|
exports.prefixOf = require_CustomId.prefixOf;
|
|
18
29
|
exports.setBotColor = require_CustomId.setBotColor;
|
|
19
30
|
//# sourceMappingURL=internal.index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.index.cjs","names":[],"sources":["../src/customId/routing.ts"],"sourcesContent":["import type { AnyCustomId } from './CustomId';\n\n/**\n * The route decorators store a handler's customId definitions here so the component base can decode\n * against them at runtime.\n *\n * @internal\n */\nexport const ComponentDefsKey = Symbol('seedcord:customId:componentDefs');\n\n/**\n * The phantom a component handler base carries. A route decorator constrains its argument to this, so\n * passing different definitions to the decorator and the handler's generic is a compile error. Never set at runtime.\n *\n * @internal\n */\nexport interface HasComponentDefs<Defs extends readonly AnyCustomId[]> {\n readonly __componentDefs?: Defs;\n}\n"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"internal.index.cjs","names":["CustomId"],"sources":["../src/pagination/cursor.ts","../src/customId/routing.ts"],"sourcesContent":["import { CustomId } from '@customId/CustomId';\n\nimport type { CustomIdField } from '@customId/Field';\n\n// Included in the layout hash so it is fixed per prefix. Reducing it marks every active button as StaleCustomId.\n// @internal\nexport const PAGE_MAX = 1_000_000;\n\n// Discord rejects a message with duplicate custom_ids, and two controls can target the same page (first and\n// prev both hit 0), so each control uses a distinct slot.\n// @internal\nexport const SLOT_MAX = 4;\n\n/** Page cursor for a paginator. */\nexport type PageCursor<Prefix extends string> = CustomId<\n Prefix,\n { page: CustomIdField<number>; slot: CustomIdField<number> }\n>;\n\n/** Build the page cursor for a paginator. */\nexport function pageCursor<Prefix extends string>(prefix: Prefix): PageCursor<Prefix> {\n return new CustomId(prefix).int('page', 0, PAGE_MAX).int('slot', 0, SLOT_MAX);\n}\n","import type { AnyCustomId } from './CustomId';\n\n/**\n * The route decorators store a handler's customId definitions here so the component base can decode\n * against them at runtime.\n *\n * @internal\n */\nexport const ComponentDefsKey = Symbol('seedcord:customId:componentDefs');\n\n/**\n * The phantom a component handler base carries. A route decorator constrains its argument to this, so\n * passing different definitions to the decorator and the handler's generic is a compile error. Never set at runtime.\n *\n * @internal\n */\nexport interface HasComponentDefs<Defs extends readonly AnyCustomId[]> {\n readonly __componentDefs?: Defs;\n}\n"],"mappings":";;;;AAMA,MAAa,WAAW;AAKxB,MAAa,WAAW;;AASxB,SAAgB,WAAkC,QAAoC;CAClF,OAAO,IAAIA,0BAAS,MAAM,CAAC,CAAC,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC,IAAI,QAAQ,IAAW;AAChF;;;;;;;;;;ACdA,MAAa,mBAAmB,OAAO,iCAAiC"}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as CustomIdField, i as prefixOf, n as CustomId, o as DecodedParams, r as decodeFor, s as BuilderComponent, t as AnyCustomId } from "./CustomId-CtpiL97C.mjs";
|
|
2
2
|
import { ColorResolvable } from "discord.js";
|
|
3
3
|
|
|
4
4
|
//#region src/botColorHolder.d.ts
|
|
5
5
|
/** @internal */
|
|
6
6
|
declare function setBotColor(color: ColorResolvable | undefined): void;
|
|
7
7
|
//#endregion
|
|
8
|
+
//#region src/pagination/cursor.d.ts
|
|
9
|
+
declare const PAGE_MAX = 1000000;
|
|
10
|
+
/** Page cursor for a paginator. */
|
|
11
|
+
type PageCursor<Prefix extends string> = CustomId<Prefix, {
|
|
12
|
+
page: CustomIdField<number>;
|
|
13
|
+
slot: CustomIdField<number>;
|
|
14
|
+
}>;
|
|
15
|
+
/** Build the page cursor for a paginator. */
|
|
16
|
+
declare function pageCursor<Prefix extends string>(prefix: Prefix): PageCursor<Prefix>;
|
|
17
|
+
//#endregion
|
|
8
18
|
//#region src/customId/routing.d.ts
|
|
9
19
|
/**
|
|
10
20
|
* The route decorators store a handler's customId definitions here so the component base can decode
|
|
@@ -32,5 +42,5 @@ declare class NoticeCard extends BuilderComponent<'container'> {
|
|
|
32
42
|
constructor(description: string, title?: string);
|
|
33
43
|
}
|
|
34
44
|
//#endregion
|
|
35
|
-
export { type AnyCustomId, ComponentDefsKey, type DecodedParams, type HasComponentDefs, NoticeCard, decodeFor, prefixOf, setBotColor };
|
|
45
|
+
export { type AnyCustomId, ComponentDefsKey, type DecodedParams, type HasComponentDefs, NoticeCard, PAGE_MAX, type PageCursor, decodeFor, pageCursor, prefixOf, setBotColor };
|
|
36
46
|
//# sourceMappingURL=internal.index.d.mts.map
|
package/dist/internal.index.mjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import { c as setBotColor, i as NoticeCard, n as decodeFor, r as prefixOf } from "./CustomId-BKioviDw.mjs";
|
|
1
|
+
import { c as setBotColor, i as NoticeCard, n as decodeFor, r as prefixOf, t as CustomId } from "./CustomId-BKioviDw.mjs";
|
|
2
2
|
|
|
3
|
+
//#region src/pagination/cursor.ts
|
|
4
|
+
const PAGE_MAX = 1e6;
|
|
5
|
+
const SLOT_MAX = 4;
|
|
6
|
+
/** Build the page cursor for a paginator. */
|
|
7
|
+
function pageCursor(prefix) {
|
|
8
|
+
return new CustomId(prefix).int("page", 0, PAGE_MAX).int("slot", 0, 4);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
3
12
|
//#region src/customId/routing.ts
|
|
4
13
|
/**
|
|
5
14
|
* The route decorators store a handler's customId definitions here so the component base can decode
|
|
@@ -10,5 +19,5 @@ import { c as setBotColor, i as NoticeCard, n as decodeFor, r as prefixOf } from
|
|
|
10
19
|
const ComponentDefsKey = Symbol("seedcord:customId:componentDefs");
|
|
11
20
|
|
|
12
21
|
//#endregion
|
|
13
|
-
export { ComponentDefsKey, NoticeCard, decodeFor, prefixOf, setBotColor };
|
|
22
|
+
export { ComponentDefsKey, NoticeCard, PAGE_MAX, decodeFor, pageCursor, prefixOf, setBotColor };
|
|
14
23
|
//# sourceMappingURL=internal.index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.index.mjs","names":[],"sources":["../src/customId/routing.ts"],"sourcesContent":["import type { AnyCustomId } from './CustomId';\n\n/**\n * The route decorators store a handler's customId definitions here so the component base can decode\n * against them at runtime.\n *\n * @internal\n */\nexport const ComponentDefsKey = Symbol('seedcord:customId:componentDefs');\n\n/**\n * The phantom a component handler base carries. A route decorator constrains its argument to this, so\n * passing different definitions to the decorator and the handler's generic is a compile error. Never set at runtime.\n *\n * @internal\n */\nexport interface HasComponentDefs<Defs extends readonly AnyCustomId[]> {\n readonly __componentDefs?: Defs;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"internal.index.mjs","names":[],"sources":["../src/pagination/cursor.ts","../src/customId/routing.ts"],"sourcesContent":["import { CustomId } from '@customId/CustomId';\n\nimport type { CustomIdField } from '@customId/Field';\n\n// Included in the layout hash so it is fixed per prefix. Reducing it marks every active button as StaleCustomId.\n// @internal\nexport const PAGE_MAX = 1_000_000;\n\n// Discord rejects a message with duplicate custom_ids, and two controls can target the same page (first and\n// prev both hit 0), so each control uses a distinct slot.\n// @internal\nexport const SLOT_MAX = 4;\n\n/** Page cursor for a paginator. */\nexport type PageCursor<Prefix extends string> = CustomId<\n Prefix,\n { page: CustomIdField<number>; slot: CustomIdField<number> }\n>;\n\n/** Build the page cursor for a paginator. */\nexport function pageCursor<Prefix extends string>(prefix: Prefix): PageCursor<Prefix> {\n return new CustomId(prefix).int('page', 0, PAGE_MAX).int('slot', 0, SLOT_MAX);\n}\n","import type { AnyCustomId } from './CustomId';\n\n/**\n * The route decorators store a handler's customId definitions here so the component base can decode\n * against them at runtime.\n *\n * @internal\n */\nexport const ComponentDefsKey = Symbol('seedcord:customId:componentDefs');\n\n/**\n * The phantom a component handler base carries. A route decorator constrains its argument to this, so\n * passing different definitions to the decorator and the handler's generic is a compile error. Never set at runtime.\n *\n * @internal\n */\nexport interface HasComponentDefs<Defs extends readonly AnyCustomId[]> {\n readonly __componentDefs?: Defs;\n}\n"],"mappings":";;;AAMA,MAAa,WAAW;AAKxB,MAAa,WAAW;;AASxB,SAAgB,WAAkC,QAAoC;CAClF,OAAO,IAAI,SAAS,MAAM,CAAC,CAAC,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC,IAAI,QAAQ,IAAW;AAChF;;;;;;;;;;ACdA,MAAa,mBAAmB,OAAO,iCAAiC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seedcord/kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0-next.0",
|
|
5
5
|
"description": "Reusable Seedcord building blocks: component builders, the Notice error tree, and the typed customId codec",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -42,15 +42,15 @@
|
|
|
42
42
|
"typescript": "^6.0.3"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@seedcord/errors": "0.2.
|
|
46
|
-
"@seedcord/types": "0.7.0
|
|
45
|
+
"@seedcord/errors": "0.2.1-next.0",
|
|
46
|
+
"@seedcord/types": "0.7.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"discord.js": "^14.26.4",
|
|
50
50
|
"type-fest": "^5.7.0",
|
|
51
51
|
"vite": "^8.0.16",
|
|
52
|
-
"@seedcord/eslint-config": "1.4.2-next.0",
|
|
53
52
|
"@seedcord/tsconfig": "2.0.0",
|
|
53
|
+
"@seedcord/eslint-config": "1.4.2",
|
|
54
54
|
"@seedcord/tsdown-config": "2.0.0"
|
|
55
55
|
},
|
|
56
56
|
"publishConfig": {
|