@shware/http 3.2.1 → 3.3.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/error/parse.cjs +27 -0
- package/dist/error/parse.cjs.map +1 -1
- package/dist/error/parse.d.cts +24 -1
- package/dist/error/parse.d.cts.map +1 -1
- package/dist/error/parse.d.mts +24 -1
- package/dist/error/parse.d.mts.map +1 -1
- package/dist/error/parse.mjs +27 -1
- package/dist/error/parse.mjs.map +1 -1
- package/dist/hono/csrf.cjs.map +1 -1
- package/dist/hono/geolocation.cjs.map +1 -1
- package/dist/hono/handler.cjs.map +1 -1
- package/dist/hono/validator.cjs.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/response.cjs.map +1 -1
- package/dist/webhook/index.cjs.map +1 -1
- package/package.json +1 -1
package/dist/error/parse.cjs
CHANGED
|
@@ -50,6 +50,32 @@ function getErrorMessage(data, t) {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
+
* Whether an error body carries `ErrorInfo` with one of the given reasons. Meant for the cases a
|
|
54
|
+
* component wants to handle itself (inline field error, retry, redirect) so a global error handler
|
|
55
|
+
* can skip them.
|
|
56
|
+
*
|
|
57
|
+
* @example With a react-query MutationCache:
|
|
58
|
+
*
|
|
59
|
+
* const login = useMutation({
|
|
60
|
+
* mutationFn: api.auth.loginEmail,
|
|
61
|
+
* meta: { handledReasons: ['INVALID_VERIFICATION_CODE'] },
|
|
62
|
+
* onError: (e) => {
|
|
63
|
+
* if (isErrorReason(e.data, 'INVALID_VERIFICATION_CODE')) setError('code', { message });
|
|
64
|
+
* },
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* new MutationCache({
|
|
68
|
+
* onError: (e, _v, _c, mutation) => {
|
|
69
|
+
* if (isErrorReason(e.data, ...(mutation.meta?.handledReasons ?? []))) return;
|
|
70
|
+
* toast.error(getErrorMessage(e.data, t).message);
|
|
71
|
+
* },
|
|
72
|
+
* });
|
|
73
|
+
*/
|
|
74
|
+
function isErrorReason(data, ...reasons) {
|
|
75
|
+
const reason = getErrorInfo(data)?.reason;
|
|
76
|
+
return reason !== void 0 && reasons.includes(reason);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
53
79
|
* @example For react-hook-form:
|
|
54
80
|
*
|
|
55
81
|
* const { setError } = useForm();
|
|
@@ -67,5 +93,6 @@ function getFieldViolations(data) {
|
|
|
67
93
|
exports.getErrorInfo = getErrorInfo;
|
|
68
94
|
exports.getErrorMessage = getErrorMessage;
|
|
69
95
|
exports.getFieldViolations = getFieldViolations;
|
|
96
|
+
exports.isErrorReason = isErrorReason;
|
|
70
97
|
|
|
71
98
|
//# sourceMappingURL=parse.cjs.map
|
package/dist/error/parse.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.cjs","names":["DetailType"],"sources":["../../src/error/parse.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { DefaultNamespace, Namespace, TFunction } from 'i18next';\nimport { type BadRequest, DetailType, type ErrorInfo } from './detail';\nimport type { ResolvedErrorReason } from './reason';\nimport type { ErrorBody } from './status';\n\n/**\n * `ErrorBody` as it arrives off the wire: bodies parsed from an HTTP error\n * response with `JSON.parse` guarantee no field at runtime.\n */\ntype LooseErrorBody = {\n error?: Partial<ErrorBody['error']>;\n};\n\nexport function getErrorInfo(data: unknown): ErrorInfo | null {\n if (typeof data !== 'object' || data === null || !('error' in data)) return null;\n const { error } = data as LooseErrorBody;\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n return errorInfo ?? null;\n}\n\n/**\n * @example For axios:\n *\n * const { t } = useTranslation('error');\n *\n * const { message } = getErrorMessage(error.response.data, t);\n * toast.error(message);\n */\nexport function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(\n data: unknown,\n t: TFunction<Ns, KPrefix>\n): { reason: ResolvedErrorReason | null; message: string } {\n // 0. unknown error\n if (typeof data !== 'object' || data === null || !('error' in data)) {\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n }\n\n const { error } = data as LooseErrorBody;\n\n // 1. from server via Accept-Language header or lng param\n const localizedMessage = error?.details?.find((d) => d['@type'] === DetailType.LOCALIZED_MESSAGE);\n if (localizedMessage) return { reason: null, message: localizedMessage.message };\n\n // 2. from business logic error\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n if (errorInfo) {\n return {\n reason: errorInfo.reason,\n message: t(errorInfo.reason, errorInfo.metadata),\n };\n }\n\n // 3. error message in english\n const message = error?.message;\n if (hasText(message)) return { reason: null, message };\n\n // 4. from server via status code\n if (error?.status) return { reason: null, message: t(error.status) };\n\n // 5. unknown error\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n}\n\n/**\n * @example For react-hook-form:\n *\n * const { setError } = useForm();\n * const fieldViolations = getFieldViolations(error.response.data);\n * fieldViolations.forEach((violation) => {\n * setError(violation.field, { message: violation.description });\n * });\n */\nexport function getFieldViolations(data: unknown): BadRequest['fieldViolations'] {\n if (typeof data !== 'object' || data === null || !('error' in data)) return [];\n const { error } = data as LooseErrorBody;\n const badRequest = error?.details?.find((d) => d['@type'] === DetailType.BAD_REQUEST);\n return badRequest?.fieldViolations ?? [];\n}\n"],"mappings":";;;;AAcA,SAAgB,aAAa,MAAiC;CAC5D,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO;CAC5E,MAAM,EAAE,UAAU;CAElB,OADkB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,UAAU,KAC9D;AACtB;;;;;;;;;AAUA,SAAgB,gBACd,MACA,GACyD;CAEzD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO;EACnE,QAAQ,MAAM,sBAAsB,IAAI;EACxC,OAAO;GAAE,QAAQ;GAAM,SAAS,EAAE,SAAS;EAAE;CAC/C;CAEA,MAAM,EAAE,UAAU;CAGlB,MAAM,mBAAmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,iBAAiB;CAChG,IAAI,kBAAkB,OAAO;EAAE,QAAQ;EAAM,SAAS,iBAAiB;CAAQ;CAG/E,MAAM,YAAY,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,UAAU;CAClF,IAAI,WACF,OAAO;EACL,QAAQ,UAAU;EAClB,SAAS,EAAE,UAAU,QAAQ,UAAU,QAAQ;CACjD;CAIF,MAAM,UAAU,OAAO;CACvB,KAAA,
|
|
1
|
+
{"version":3,"file":"parse.cjs","names":["DetailType","hasText"],"sources":["../../src/error/parse.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { DefaultNamespace, Namespace, TFunction } from 'i18next';\nimport { type BadRequest, DetailType, type ErrorInfo } from './detail';\nimport type { ResolvedErrorReason } from './reason';\nimport type { ErrorBody } from './status';\n\n/**\n * `ErrorBody` as it arrives off the wire: bodies parsed from an HTTP error\n * response with `JSON.parse` guarantee no field at runtime.\n */\ntype LooseErrorBody = {\n error?: Partial<ErrorBody['error']>;\n};\n\nexport function getErrorInfo(data: unknown): ErrorInfo | null {\n if (typeof data !== 'object' || data === null || !('error' in data)) return null;\n const { error } = data as LooseErrorBody;\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n return errorInfo ?? null;\n}\n\n/**\n * @example For axios:\n *\n * const { t } = useTranslation('error');\n *\n * const { message } = getErrorMessage(error.response.data, t);\n * toast.error(message);\n */\nexport function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(\n data: unknown,\n t: TFunction<Ns, KPrefix>\n): { reason: ResolvedErrorReason | null; message: string } {\n // 0. unknown error\n if (typeof data !== 'object' || data === null || !('error' in data)) {\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n }\n\n const { error } = data as LooseErrorBody;\n\n // 1. from server via Accept-Language header or lng param\n const localizedMessage = error?.details?.find((d) => d['@type'] === DetailType.LOCALIZED_MESSAGE);\n if (localizedMessage) return { reason: null, message: localizedMessage.message };\n\n // 2. from business logic error\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n if (errorInfo) {\n return {\n reason: errorInfo.reason,\n message: t(errorInfo.reason, errorInfo.metadata),\n };\n }\n\n // 3. error message in english\n const message = error?.message;\n if (hasText(message)) return { reason: null, message };\n\n // 4. from server via status code\n if (error?.status) return { reason: null, message: t(error.status) };\n\n // 5. unknown error\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n}\n\n/**\n * Whether an error body carries `ErrorInfo` with one of the given reasons. Meant for the cases a\n * component wants to handle itself (inline field error, retry, redirect) so a global error handler\n * can skip them.\n *\n * @example With a react-query MutationCache:\n *\n * const login = useMutation({\n * mutationFn: api.auth.loginEmail,\n * meta: { handledReasons: ['INVALID_VERIFICATION_CODE'] },\n * onError: (e) => {\n * if (isErrorReason(e.data, 'INVALID_VERIFICATION_CODE')) setError('code', { message });\n * },\n * });\n *\n * new MutationCache({\n * onError: (e, _v, _c, mutation) => {\n * if (isErrorReason(e.data, ...(mutation.meta?.handledReasons ?? []))) return;\n * toast.error(getErrorMessage(e.data, t).message);\n * },\n * });\n */\nexport function isErrorReason(data: unknown, ...reasons: ResolvedErrorReason[]): boolean {\n const reason = getErrorInfo(data)?.reason;\n return reason !== undefined && reasons.includes(reason);\n}\n\n/**\n * @example For react-hook-form:\n *\n * const { setError } = useForm();\n * const fieldViolations = getFieldViolations(error.response.data);\n * fieldViolations.forEach((violation) => {\n * setError(violation.field, { message: violation.description });\n * });\n */\nexport function getFieldViolations(data: unknown): BadRequest['fieldViolations'] {\n if (typeof data !== 'object' || data === null || !('error' in data)) return [];\n const { error } = data as LooseErrorBody;\n const badRequest = error?.details?.find((d) => d['@type'] === DetailType.BAD_REQUEST);\n return badRequest?.fieldViolations ?? [];\n}\n"],"mappings":";;;;AAcA,SAAgB,aAAa,MAAiC;CAC5D,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO;CAC5E,MAAM,EAAE,UAAU;CAElB,OADkB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,UAAU,KAC9D;AACtB;;;;;;;;;AAUA,SAAgB,gBACd,MACA,GACyD;CAEzD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO;EACnE,QAAQ,MAAM,sBAAsB,IAAI;EACxC,OAAO;GAAE,QAAQ;GAAM,SAAS,EAAE,SAAS;EAAE;CAC/C;CAEA,MAAM,EAAE,UAAU;CAGlB,MAAM,mBAAmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,iBAAiB;CAChG,IAAI,kBAAkB,OAAO;EAAE,QAAQ;EAAM,SAAS,iBAAiB;CAAQ;CAG/E,MAAM,YAAY,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaA,qBAAAA,WAAW,UAAU;CAClF,IAAI,WACF,OAAO;EACL,QAAQ,UAAU;EAClB,SAAS,EAAE,UAAU,QAAQ,UAAU,QAAQ;CACjD;CAIF,MAAM,UAAU,OAAO;CACvB,KAAA,GAAIC,cAAAA,QAAAA,CAAQ,OAAO,GAAG,OAAO;EAAE,QAAQ;EAAM;CAAQ;CAGrD,IAAI,OAAO,QAAQ,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,MAAM,MAAM;CAAE;CAGnE,QAAQ,MAAM,sBAAsB,IAAI;CACxC,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,SAAS;CAAE;AAC/C;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,cAAc,MAAe,GAAG,SAAyC;CACvF,MAAM,SAAS,aAAa,IAAI,CAAC,EAAE;CACnC,OAAO,WAAW,KAAA,KAAa,QAAQ,SAAS,MAAM;AACxD;;;;;;;;;;AAWA,SAAgB,mBAAmB,MAA8C;CAC/E,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO,CAAC;CAC7E,MAAM,EAAE,UAAU;CAElB,QADmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAaD,qBAAAA,WAAW,WAAW,EAAA,EACjE,mBAAmB,CAAC;AACzC"}
|
package/dist/error/parse.d.cts
CHANGED
|
@@ -15,6 +15,29 @@ declare function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefi
|
|
|
15
15
|
reason: ResolvedErrorReason | null;
|
|
16
16
|
message: string;
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Whether an error body carries `ErrorInfo` with one of the given reasons. Meant for the cases a
|
|
20
|
+
* component wants to handle itself (inline field error, retry, redirect) so a global error handler
|
|
21
|
+
* can skip them.
|
|
22
|
+
*
|
|
23
|
+
* @example With a react-query MutationCache:
|
|
24
|
+
*
|
|
25
|
+
* const login = useMutation({
|
|
26
|
+
* mutationFn: api.auth.loginEmail,
|
|
27
|
+
* meta: { handledReasons: ['INVALID_VERIFICATION_CODE'] },
|
|
28
|
+
* onError: (e) => {
|
|
29
|
+
* if (isErrorReason(e.data, 'INVALID_VERIFICATION_CODE')) setError('code', { message });
|
|
30
|
+
* },
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* new MutationCache({
|
|
34
|
+
* onError: (e, _v, _c, mutation) => {
|
|
35
|
+
* if (isErrorReason(e.data, ...(mutation.meta?.handledReasons ?? []))) return;
|
|
36
|
+
* toast.error(getErrorMessage(e.data, t).message);
|
|
37
|
+
* },
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
40
|
+
declare function isErrorReason(data: unknown, ...reasons: ResolvedErrorReason[]): boolean;
|
|
18
41
|
/**
|
|
19
42
|
* @example For react-hook-form:
|
|
20
43
|
*
|
|
@@ -26,5 +49,5 @@ declare function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefi
|
|
|
26
49
|
*/
|
|
27
50
|
declare function getFieldViolations(data: unknown): BadRequest['fieldViolations'];
|
|
28
51
|
//#endregion
|
|
29
|
-
export { getErrorInfo, getErrorMessage, getFieldViolations };
|
|
52
|
+
export { getErrorInfo, getErrorMessage, getFieldViolations, isErrorReason };
|
|
30
53
|
//# sourceMappingURL=parse.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.cts","names":[],"sources":["../../src/error/parse.ts"],"mappings":";;;;iBAcgB,aAAa,gBAAgB;;;;;;;;;iBAe7B,gBAAgB,WAAW,YAAY,kBAAkB,qBACvE,eACA,GAAG,UAAU,IAAI;EACd,QAAQ;EAA4B
|
|
1
|
+
{"version":3,"file":"parse.d.cts","names":[],"sources":["../../src/error/parse.ts"],"mappings":";;;;iBAcgB,aAAa,gBAAgB;;;;;;;;;iBAe7B,gBAAgB,WAAW,YAAY,kBAAkB,qBACvE,eACA,GAAG,UAAU,IAAI;EACd,QAAQ;EAA4B;;;;;;;;;;;;;;;;;;;;;;;;iBAwDzB,cAAc,kBAAkB,SAAS;;;;;;;;;;iBAczC,mBAAmB,gBAAgB"}
|
package/dist/error/parse.d.mts
CHANGED
|
@@ -15,6 +15,29 @@ declare function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefi
|
|
|
15
15
|
reason: ResolvedErrorReason | null;
|
|
16
16
|
message: string;
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Whether an error body carries `ErrorInfo` with one of the given reasons. Meant for the cases a
|
|
20
|
+
* component wants to handle itself (inline field error, retry, redirect) so a global error handler
|
|
21
|
+
* can skip them.
|
|
22
|
+
*
|
|
23
|
+
* @example With a react-query MutationCache:
|
|
24
|
+
*
|
|
25
|
+
* const login = useMutation({
|
|
26
|
+
* mutationFn: api.auth.loginEmail,
|
|
27
|
+
* meta: { handledReasons: ['INVALID_VERIFICATION_CODE'] },
|
|
28
|
+
* onError: (e) => {
|
|
29
|
+
* if (isErrorReason(e.data, 'INVALID_VERIFICATION_CODE')) setError('code', { message });
|
|
30
|
+
* },
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* new MutationCache({
|
|
34
|
+
* onError: (e, _v, _c, mutation) => {
|
|
35
|
+
* if (isErrorReason(e.data, ...(mutation.meta?.handledReasons ?? []))) return;
|
|
36
|
+
* toast.error(getErrorMessage(e.data, t).message);
|
|
37
|
+
* },
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
40
|
+
declare function isErrorReason(data: unknown, ...reasons: ResolvedErrorReason[]): boolean;
|
|
18
41
|
/**
|
|
19
42
|
* @example For react-hook-form:
|
|
20
43
|
*
|
|
@@ -26,5 +49,5 @@ declare function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefi
|
|
|
26
49
|
*/
|
|
27
50
|
declare function getFieldViolations(data: unknown): BadRequest['fieldViolations'];
|
|
28
51
|
//#endregion
|
|
29
|
-
export { getErrorInfo, getErrorMessage, getFieldViolations };
|
|
52
|
+
export { getErrorInfo, getErrorMessage, getFieldViolations, isErrorReason };
|
|
30
53
|
//# sourceMappingURL=parse.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.mts","names":[],"sources":["../../src/error/parse.ts"],"mappings":";;;;iBAcgB,aAAa,gBAAgB;;;;;;;;;iBAe7B,gBAAgB,WAAW,YAAY,kBAAkB,qBACvE,eACA,GAAG,UAAU,IAAI;EACd,QAAQ;EAA4B
|
|
1
|
+
{"version":3,"file":"parse.d.mts","names":[],"sources":["../../src/error/parse.ts"],"mappings":";;;;iBAcgB,aAAa,gBAAgB;;;;;;;;;iBAe7B,gBAAgB,WAAW,YAAY,kBAAkB,qBACvE,eACA,GAAG,UAAU,IAAI;EACd,QAAQ;EAA4B;;;;;;;;;;;;;;;;;;;;;;;;iBAwDzB,cAAc,kBAAkB,SAAS;;;;;;;;;;iBAczC,mBAAmB,gBAAgB"}
|
package/dist/error/parse.mjs
CHANGED
|
@@ -49,6 +49,32 @@ function getErrorMessage(data, t) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
+
* Whether an error body carries `ErrorInfo` with one of the given reasons. Meant for the cases a
|
|
53
|
+
* component wants to handle itself (inline field error, retry, redirect) so a global error handler
|
|
54
|
+
* can skip them.
|
|
55
|
+
*
|
|
56
|
+
* @example With a react-query MutationCache:
|
|
57
|
+
*
|
|
58
|
+
* const login = useMutation({
|
|
59
|
+
* mutationFn: api.auth.loginEmail,
|
|
60
|
+
* meta: { handledReasons: ['INVALID_VERIFICATION_CODE'] },
|
|
61
|
+
* onError: (e) => {
|
|
62
|
+
* if (isErrorReason(e.data, 'INVALID_VERIFICATION_CODE')) setError('code', { message });
|
|
63
|
+
* },
|
|
64
|
+
* });
|
|
65
|
+
*
|
|
66
|
+
* new MutationCache({
|
|
67
|
+
* onError: (e, _v, _c, mutation) => {
|
|
68
|
+
* if (isErrorReason(e.data, ...(mutation.meta?.handledReasons ?? []))) return;
|
|
69
|
+
* toast.error(getErrorMessage(e.data, t).message);
|
|
70
|
+
* },
|
|
71
|
+
* });
|
|
72
|
+
*/
|
|
73
|
+
function isErrorReason(data, ...reasons) {
|
|
74
|
+
const reason = getErrorInfo(data)?.reason;
|
|
75
|
+
return reason !== void 0 && reasons.includes(reason);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
52
78
|
* @example For react-hook-form:
|
|
53
79
|
*
|
|
54
80
|
* const { setError } = useForm();
|
|
@@ -63,6 +89,6 @@ function getFieldViolations(data) {
|
|
|
63
89
|
return (error?.details?.find((d) => d["@type"] === DetailType.BAD_REQUEST))?.fieldViolations ?? [];
|
|
64
90
|
}
|
|
65
91
|
//#endregion
|
|
66
|
-
export { getErrorInfo, getErrorMessage, getFieldViolations };
|
|
92
|
+
export { getErrorInfo, getErrorMessage, getFieldViolations, isErrorReason };
|
|
67
93
|
|
|
68
94
|
//# sourceMappingURL=parse.mjs.map
|
package/dist/error/parse.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.mjs","names":[],"sources":["../../src/error/parse.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { DefaultNamespace, Namespace, TFunction } from 'i18next';\nimport { type BadRequest, DetailType, type ErrorInfo } from './detail';\nimport type { ResolvedErrorReason } from './reason';\nimport type { ErrorBody } from './status';\n\n/**\n * `ErrorBody` as it arrives off the wire: bodies parsed from an HTTP error\n * response with `JSON.parse` guarantee no field at runtime.\n */\ntype LooseErrorBody = {\n error?: Partial<ErrorBody['error']>;\n};\n\nexport function getErrorInfo(data: unknown): ErrorInfo | null {\n if (typeof data !== 'object' || data === null || !('error' in data)) return null;\n const { error } = data as LooseErrorBody;\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n return errorInfo ?? null;\n}\n\n/**\n * @example For axios:\n *\n * const { t } = useTranslation('error');\n *\n * const { message } = getErrorMessage(error.response.data, t);\n * toast.error(message);\n */\nexport function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(\n data: unknown,\n t: TFunction<Ns, KPrefix>\n): { reason: ResolvedErrorReason | null; message: string } {\n // 0. unknown error\n if (typeof data !== 'object' || data === null || !('error' in data)) {\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n }\n\n const { error } = data as LooseErrorBody;\n\n // 1. from server via Accept-Language header or lng param\n const localizedMessage = error?.details?.find((d) => d['@type'] === DetailType.LOCALIZED_MESSAGE);\n if (localizedMessage) return { reason: null, message: localizedMessage.message };\n\n // 2. from business logic error\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n if (errorInfo) {\n return {\n reason: errorInfo.reason,\n message: t(errorInfo.reason, errorInfo.metadata),\n };\n }\n\n // 3. error message in english\n const message = error?.message;\n if (hasText(message)) return { reason: null, message };\n\n // 4. from server via status code\n if (error?.status) return { reason: null, message: t(error.status) };\n\n // 5. unknown error\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n}\n\n/**\n * @example For react-hook-form:\n *\n * const { setError } = useForm();\n * const fieldViolations = getFieldViolations(error.response.data);\n * fieldViolations.forEach((violation) => {\n * setError(violation.field, { message: violation.description });\n * });\n */\nexport function getFieldViolations(data: unknown): BadRequest['fieldViolations'] {\n if (typeof data !== 'object' || data === null || !('error' in data)) return [];\n const { error } = data as LooseErrorBody;\n const badRequest = error?.details?.find((d) => d['@type'] === DetailType.BAD_REQUEST);\n return badRequest?.fieldViolations ?? [];\n}\n"],"mappings":";;;AAcA,SAAgB,aAAa,MAAiC;CAC5D,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO;CAC5E,MAAM,EAAE,UAAU;CAElB,OADkB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,UAAU,KAC9D;AACtB;;;;;;;;;AAUA,SAAgB,gBACd,MACA,GACyD;CAEzD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO;EACnE,QAAQ,MAAM,sBAAsB,IAAI;EACxC,OAAO;GAAE,QAAQ;GAAM,SAAS,EAAE,SAAS;EAAE;CAC/C;CAEA,MAAM,EAAE,UAAU;CAGlB,MAAM,mBAAmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,iBAAiB;CAChG,IAAI,kBAAkB,OAAO;EAAE,QAAQ;EAAM,SAAS,iBAAiB;CAAQ;CAG/E,MAAM,YAAY,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,UAAU;CAClF,IAAI,WACF,OAAO;EACL,QAAQ,UAAU;EAClB,SAAS,EAAE,UAAU,QAAQ,UAAU,QAAQ;CACjD;CAIF,MAAM,UAAU,OAAO;CACvB,IAAI,QAAQ,OAAO,GAAG,OAAO;EAAE,QAAQ;EAAM;CAAQ;CAGrD,IAAI,OAAO,QAAQ,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,MAAM,MAAM;CAAE;CAGnE,QAAQ,MAAM,sBAAsB,IAAI;CACxC,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,SAAS;CAAE;AAC/C;;;;;;;;;;AAWA,SAAgB,mBAAmB,MAA8C;CAC/E,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO,CAAC;CAC7E,MAAM,EAAE,UAAU;CAElB,QADmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,WAAW,EAAA,EACjE,mBAAmB,CAAC;AACzC"}
|
|
1
|
+
{"version":3,"file":"parse.mjs","names":[],"sources":["../../src/error/parse.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { DefaultNamespace, Namespace, TFunction } from 'i18next';\nimport { type BadRequest, DetailType, type ErrorInfo } from './detail';\nimport type { ResolvedErrorReason } from './reason';\nimport type { ErrorBody } from './status';\n\n/**\n * `ErrorBody` as it arrives off the wire: bodies parsed from an HTTP error\n * response with `JSON.parse` guarantee no field at runtime.\n */\ntype LooseErrorBody = {\n error?: Partial<ErrorBody['error']>;\n};\n\nexport function getErrorInfo(data: unknown): ErrorInfo | null {\n if (typeof data !== 'object' || data === null || !('error' in data)) return null;\n const { error } = data as LooseErrorBody;\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n return errorInfo ?? null;\n}\n\n/**\n * @example For axios:\n *\n * const { t } = useTranslation('error');\n *\n * const { message } = getErrorMessage(error.response.data, t);\n * toast.error(message);\n */\nexport function getErrorMessage<Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(\n data: unknown,\n t: TFunction<Ns, KPrefix>\n): { reason: ResolvedErrorReason | null; message: string } {\n // 0. unknown error\n if (typeof data !== 'object' || data === null || !('error' in data)) {\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n }\n\n const { error } = data as LooseErrorBody;\n\n // 1. from server via Accept-Language header or lng param\n const localizedMessage = error?.details?.find((d) => d['@type'] === DetailType.LOCALIZED_MESSAGE);\n if (localizedMessage) return { reason: null, message: localizedMessage.message };\n\n // 2. from business logic error\n const errorInfo = error?.details?.find((d) => d['@type'] === DetailType.ERROR_INFO);\n if (errorInfo) {\n return {\n reason: errorInfo.reason,\n message: t(errorInfo.reason, errorInfo.metadata),\n };\n }\n\n // 3. error message in english\n const message = error?.message;\n if (hasText(message)) return { reason: null, message };\n\n // 4. from server via status code\n if (error?.status) return { reason: null, message: t(error.status) };\n\n // 5. unknown error\n console.error('Unknown API error:', data);\n return { reason: null, message: t('UNKNOWN') };\n}\n\n/**\n * Whether an error body carries `ErrorInfo` with one of the given reasons. Meant for the cases a\n * component wants to handle itself (inline field error, retry, redirect) so a global error handler\n * can skip them.\n *\n * @example With a react-query MutationCache:\n *\n * const login = useMutation({\n * mutationFn: api.auth.loginEmail,\n * meta: { handledReasons: ['INVALID_VERIFICATION_CODE'] },\n * onError: (e) => {\n * if (isErrorReason(e.data, 'INVALID_VERIFICATION_CODE')) setError('code', { message });\n * },\n * });\n *\n * new MutationCache({\n * onError: (e, _v, _c, mutation) => {\n * if (isErrorReason(e.data, ...(mutation.meta?.handledReasons ?? []))) return;\n * toast.error(getErrorMessage(e.data, t).message);\n * },\n * });\n */\nexport function isErrorReason(data: unknown, ...reasons: ResolvedErrorReason[]): boolean {\n const reason = getErrorInfo(data)?.reason;\n return reason !== undefined && reasons.includes(reason);\n}\n\n/**\n * @example For react-hook-form:\n *\n * const { setError } = useForm();\n * const fieldViolations = getFieldViolations(error.response.data);\n * fieldViolations.forEach((violation) => {\n * setError(violation.field, { message: violation.description });\n * });\n */\nexport function getFieldViolations(data: unknown): BadRequest['fieldViolations'] {\n if (typeof data !== 'object' || data === null || !('error' in data)) return [];\n const { error } = data as LooseErrorBody;\n const badRequest = error?.details?.find((d) => d['@type'] === DetailType.BAD_REQUEST);\n return badRequest?.fieldViolations ?? [];\n}\n"],"mappings":";;;AAcA,SAAgB,aAAa,MAAiC;CAC5D,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO;CAC5E,MAAM,EAAE,UAAU;CAElB,OADkB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,UAAU,KAC9D;AACtB;;;;;;;;;AAUA,SAAgB,gBACd,MACA,GACyD;CAEzD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO;EACnE,QAAQ,MAAM,sBAAsB,IAAI;EACxC,OAAO;GAAE,QAAQ;GAAM,SAAS,EAAE,SAAS;EAAE;CAC/C;CAEA,MAAM,EAAE,UAAU;CAGlB,MAAM,mBAAmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,iBAAiB;CAChG,IAAI,kBAAkB,OAAO;EAAE,QAAQ;EAAM,SAAS,iBAAiB;CAAQ;CAG/E,MAAM,YAAY,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,UAAU;CAClF,IAAI,WACF,OAAO;EACL,QAAQ,UAAU;EAClB,SAAS,EAAE,UAAU,QAAQ,UAAU,QAAQ;CACjD;CAIF,MAAM,UAAU,OAAO;CACvB,IAAI,QAAQ,OAAO,GAAG,OAAO;EAAE,QAAQ;EAAM;CAAQ;CAGrD,IAAI,OAAO,QAAQ,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,MAAM,MAAM;CAAE;CAGnE,QAAQ,MAAM,sBAAsB,IAAI;CACxC,OAAO;EAAE,QAAQ;EAAM,SAAS,EAAE,SAAS;CAAE;AAC/C;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,cAAc,MAAe,GAAG,SAAyC;CACvF,MAAM,SAAS,aAAa,IAAI,CAAC,EAAE;CACnC,OAAO,WAAW,KAAA,KAAa,QAAQ,SAAS,MAAM;AACxD;;;;;;;;;;AAWA,SAAgB,mBAAmB,MAA8C;CAC/E,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,EAAE,WAAW,OAAO,OAAO,CAAC;CAC7E,MAAM,EAAE,UAAU;CAElB,QADmB,OAAO,SAAS,MAAM,MAAM,EAAE,aAAa,WAAW,WAAW,EAAA,EACjE,mBAAmB,CAAC;AACzC"}
|
package/dist/hono/csrf.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csrf.cjs","names":["SmartRouter","RegExpRouter","TrieRouter","METHOD_NAME_ALL","Status"],"sources":["../../src/hono/csrf.ts"],"sourcesContent":["import { timingSafeEqual } from 'node:crypto';\nimport type { MiddlewareHandler } from 'hono';\nimport { getCookie } from 'hono/cookie';\nimport { METHOD_NAME_ALL } from 'hono/router';\nimport { RegExpRouter } from 'hono/router/reg-exp-router';\nimport { SmartRouter } from 'hono/router/smart-router';\nimport { TrieRouter } from 'hono/router/trie-router';\nimport { Status } from '../error/status';\n\ntype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';\n\nexport type CSRFIgnoreRule = string | { path: string; methods?: [HTTPMethod, ...HTTPMethod[]] };\n\nexport interface CSRFConfig {\n /**\n * Cookie name for CSRF token\n * @default 'XSRF-TOKEN'\n */\n cookieName?: string;\n\n /**\n * Header name for CSRF token\n * @default 'X-XSRF-TOKEN'\n */\n headerName?: string;\n\n /**\n * Ignore rules for specific paths and methods\n * @example\n * [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' }, // ignores all methods\n * ]\n */\n ignores?: CSRFIgnoreRule[];\n\n /**\n * Skip CSRF check for these methods\n * @default ['GET', 'HEAD', 'OPTIONS']\n */\n safeMethods?: HTTPMethod[];\n\n /**\n * Origin allowed to bypass CSRF check\n * @default undefined\n */\n origin?: string[];\n\n /**\n * Sec-Fetch-Site allowed to bypass CSRF check\n * @default undefined\n */\n secFetchSite?: Array<'same-origin' | 'same-site' | 'none' | 'cross-origin'>;\n\n /**\n * Custom error message\n * @default 'CSRF token validation failed'\n */\n errorMessage?: string;\n}\n\n/** use timing safe compare to prevent timing attack */\nfunction safeCompare(a: string, b: string): boolean {\n if (typeof a !== 'string' || typeof b !== 'string') return false;\n if (a.length === 0 || b.length === 0) return false;\n\n const bufferA = Buffer.from(a, 'utf-8');\n const bufferB = Buffer.from(b, 'utf-8');\n if (bufferA.length !== bufferB.length) return false;\n return timingSafeEqual(bufferA, bufferB);\n}\n\n/**\n * Create CSRF protection middleware\n *\n * @example\n * ```ts\n * import { Hono } from 'hono';\n * import { csrf } from '@shware/http/hono';\n *\n * const app = new Hono();\n *\n * // basic usage\n * app.use(csrf());\n *\n * // with configuration\n * app.use(csrf({\n * cookieName: 'csrf-token',\n * headerName: 'X-CSRF-Token',\n * ignores: [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' },\n * ]\n * }));\n * ```\n */\nexport function csrf(config: CSRFConfig = {}): MiddlewareHandler {\n const cookieName = config.cookieName ?? 'XSRF-TOKEN';\n const headerName = config.headerName ?? 'X-XSRF-TOKEN';\n const safeMethods = new Set(config.safeMethods ?? ['GET', 'HEAD', 'OPTIONS']);\n const errorMessage = config.errorMessage ?? 'CSRF token validation failed';\n\n // initialize router for matching ignore rules\n const router = new SmartRouter<boolean>({\n routers: [new RegExpRouter(), new TrieRouter()],\n });\n\n // register ignore rules\n if (config.ignores) {\n for (const rule of config.ignores) {\n if (typeof rule === 'string') {\n router.add(METHOD_NAME_ALL, rule, true);\n } else if (rule.methods && rule.methods.length > 0) {\n for (const method of rule.methods) {\n router.add(method, rule.path, true);\n }\n } else {\n // if no methods are specified, ignore all methods\n router.add(METHOD_NAME_ALL, rule.path, true);\n }\n }\n }\n\n // return middleware\n return async (c, next) => {\n const method = c.req.method;\n const path = c.req.path;\n\n // check if the request should be ignored\n // 1. ignore safe methods\n if (safeMethods.has(method)) {\n await next();\n return;\n }\n\n // 2. ignore configured origin\n if (config.origin?.includes(c.req.header('origin') ?? '')) {\n await next();\n return;\n }\n\n // 3. ignore configured secFetchSite\n if (config.secFetchSite?.includes((c.req.header('sec-fetch-site') ?? '') as never)) {\n await next();\n return;\n }\n\n // 4. ignore configured ignore rules\n const [matched] = router.match(method, path);\n if (matched.length > 0) {\n await next();\n return;\n }\n\n const cookieToken = getCookie(c, cookieName);\n const headerToken = c.req.header(headerName);\n\n if (!cookieToken || !headerToken) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n if (!safeCompare(cookieToken, headerToken)) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n await next();\n };\n}\n"],"mappings":";;;;;;;;;;AA8DA,SAAS,YAAY,GAAW,GAAoB;CAClD,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU,OAAO;CAC3D,IAAI,EAAE,WAAW,KAAK,EAAE,WAAW,GAAG,OAAO;CAE7C,MAAM,UAAU,OAAO,KAAK,GAAG,OAAO;CACtC,MAAM,UAAU,OAAO,KAAK,GAAG,OAAO;CACtC,IAAI,QAAQ,WAAW,QAAQ,QAAQ,OAAO;CAC9C,QAAA,
|
|
1
|
+
{"version":3,"file":"csrf.cjs","names":["timingSafeEqual","SmartRouter","RegExpRouter","TrieRouter","METHOD_NAME_ALL","getCookie","Status"],"sources":["../../src/hono/csrf.ts"],"sourcesContent":["import { timingSafeEqual } from 'node:crypto';\nimport type { MiddlewareHandler } from 'hono';\nimport { getCookie } from 'hono/cookie';\nimport { METHOD_NAME_ALL } from 'hono/router';\nimport { RegExpRouter } from 'hono/router/reg-exp-router';\nimport { SmartRouter } from 'hono/router/smart-router';\nimport { TrieRouter } from 'hono/router/trie-router';\nimport { Status } from '../error/status';\n\ntype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';\n\nexport type CSRFIgnoreRule = string | { path: string; methods?: [HTTPMethod, ...HTTPMethod[]] };\n\nexport interface CSRFConfig {\n /**\n * Cookie name for CSRF token\n * @default 'XSRF-TOKEN'\n */\n cookieName?: string;\n\n /**\n * Header name for CSRF token\n * @default 'X-XSRF-TOKEN'\n */\n headerName?: string;\n\n /**\n * Ignore rules for specific paths and methods\n * @example\n * [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' }, // ignores all methods\n * ]\n */\n ignores?: CSRFIgnoreRule[];\n\n /**\n * Skip CSRF check for these methods\n * @default ['GET', 'HEAD', 'OPTIONS']\n */\n safeMethods?: HTTPMethod[];\n\n /**\n * Origin allowed to bypass CSRF check\n * @default undefined\n */\n origin?: string[];\n\n /**\n * Sec-Fetch-Site allowed to bypass CSRF check\n * @default undefined\n */\n secFetchSite?: Array<'same-origin' | 'same-site' | 'none' | 'cross-origin'>;\n\n /**\n * Custom error message\n * @default 'CSRF token validation failed'\n */\n errorMessage?: string;\n}\n\n/** use timing safe compare to prevent timing attack */\nfunction safeCompare(a: string, b: string): boolean {\n if (typeof a !== 'string' || typeof b !== 'string') return false;\n if (a.length === 0 || b.length === 0) return false;\n\n const bufferA = Buffer.from(a, 'utf-8');\n const bufferB = Buffer.from(b, 'utf-8');\n if (bufferA.length !== bufferB.length) return false;\n return timingSafeEqual(bufferA, bufferB);\n}\n\n/**\n * Create CSRF protection middleware\n *\n * @example\n * ```ts\n * import { Hono } from 'hono';\n * import { csrf } from '@shware/http/hono';\n *\n * const app = new Hono();\n *\n * // basic usage\n * app.use(csrf());\n *\n * // with configuration\n * app.use(csrf({\n * cookieName: 'csrf-token',\n * headerName: 'X-CSRF-Token',\n * ignores: [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' },\n * ]\n * }));\n * ```\n */\nexport function csrf(config: CSRFConfig = {}): MiddlewareHandler {\n const cookieName = config.cookieName ?? 'XSRF-TOKEN';\n const headerName = config.headerName ?? 'X-XSRF-TOKEN';\n const safeMethods = new Set(config.safeMethods ?? ['GET', 'HEAD', 'OPTIONS']);\n const errorMessage = config.errorMessage ?? 'CSRF token validation failed';\n\n // initialize router for matching ignore rules\n const router = new SmartRouter<boolean>({\n routers: [new RegExpRouter(), new TrieRouter()],\n });\n\n // register ignore rules\n if (config.ignores) {\n for (const rule of config.ignores) {\n if (typeof rule === 'string') {\n router.add(METHOD_NAME_ALL, rule, true);\n } else if (rule.methods && rule.methods.length > 0) {\n for (const method of rule.methods) {\n router.add(method, rule.path, true);\n }\n } else {\n // if no methods are specified, ignore all methods\n router.add(METHOD_NAME_ALL, rule.path, true);\n }\n }\n }\n\n // return middleware\n return async (c, next) => {\n const method = c.req.method;\n const path = c.req.path;\n\n // check if the request should be ignored\n // 1. ignore safe methods\n if (safeMethods.has(method)) {\n await next();\n return;\n }\n\n // 2. ignore configured origin\n if (config.origin?.includes(c.req.header('origin') ?? '')) {\n await next();\n return;\n }\n\n // 3. ignore configured secFetchSite\n if (config.secFetchSite?.includes((c.req.header('sec-fetch-site') ?? '') as never)) {\n await next();\n return;\n }\n\n // 4. ignore configured ignore rules\n const [matched] = router.match(method, path);\n if (matched.length > 0) {\n await next();\n return;\n }\n\n const cookieToken = getCookie(c, cookieName);\n const headerToken = c.req.header(headerName);\n\n if (!cookieToken || !headerToken) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n if (!safeCompare(cookieToken, headerToken)) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n await next();\n };\n}\n"],"mappings":";;;;;;;;;;AA8DA,SAAS,YAAY,GAAW,GAAoB;CAClD,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU,OAAO;CAC3D,IAAI,EAAE,WAAW,KAAK,EAAE,WAAW,GAAG,OAAO;CAE7C,MAAM,UAAU,OAAO,KAAK,GAAG,OAAO;CACtC,MAAM,UAAU,OAAO,KAAK,GAAG,OAAO;CACtC,IAAI,QAAQ,WAAW,QAAQ,QAAQ,OAAO;CAC9C,QAAA,GAAOA,YAAAA,gBAAAA,CAAgB,SAAS,OAAO;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,KAAK,SAAqB,CAAC,GAAsB;CAC/D,MAAM,aAAa,OAAO,cAAc;CACxC,MAAM,aAAa,OAAO,cAAc;CACxC,MAAM,cAAc,IAAI,IAAI,OAAO,eAAe;EAAC;EAAO;EAAQ;CAAS,CAAC;CAC5E,MAAM,eAAe,OAAO,gBAAgB;CAG5C,MAAM,SAAS,IAAIC,yBAAAA,YAAqB,EACtC,SAAS,CAAC,IAAIC,2BAAAA,aAAa,GAAG,IAAIC,wBAAAA,WAAW,CAAC,EAChD,CAAC;CAGD,IAAI,OAAO,SACT,KAAK,MAAM,QAAQ,OAAO,SACxB,IAAI,OAAO,SAAS,UAClB,OAAO,IAAIC,YAAAA,iBAAiB,MAAM,IAAI;MACjC,IAAI,KAAK,WAAW,KAAK,QAAQ,SAAS,GAC/C,KAAK,MAAM,UAAU,KAAK,SACxB,OAAO,IAAI,QAAQ,KAAK,MAAM,IAAI;MAIpC,OAAO,IAAIA,YAAAA,iBAAiB,KAAK,MAAM,IAAI;CAMjD,OAAO,OAAO,GAAG,SAAS;EACxB,MAAM,SAAS,EAAE,IAAI;EACrB,MAAM,OAAO,EAAE,IAAI;EAInB,IAAI,YAAY,IAAI,MAAM,GAAG;GAC3B,MAAM,KAAK;GACX;EACF;EAGA,IAAI,OAAO,QAAQ,SAAS,EAAE,IAAI,OAAO,QAAQ,KAAK,EAAE,GAAG;GACzD,MAAM,KAAK;GACX;EACF;EAGA,IAAI,OAAO,cAAc,SAAU,EAAE,IAAI,OAAO,gBAAgB,KAAK,EAAY,GAAG;GAClF,MAAM,KAAK;GACX;EACF;EAGA,MAAM,CAAC,WAAW,OAAO,MAAM,QAAQ,IAAI;EAC3C,IAAI,QAAQ,SAAS,GAAG;GACtB,MAAM,KAAK;GACX;EACF;EAEA,MAAM,eAAA,GAAcC,YAAAA,UAAAA,CAAU,GAAG,UAAU;EAC3C,MAAM,cAAc,EAAE,IAAI,OAAO,UAAU;EAE3C,IAAI,CAAC,eAAe,CAAC,aACnB,MAAMC,qBAAAA,OAAO,iBAAiB,YAAY,CAAC,CAAC,MAAM;EAGpD,IAAI,CAAC,YAAY,aAAa,WAAW,GACvC,MAAMA,qBAAAA,OAAO,iBAAiB,YAAY,CAAC,CAAC,MAAM;EAGpD,MAAM,KAAK;CACb;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geolocation.cjs","names":["getGeolocationFromCloudflare","getGeolocationFromVercel","getGeolocationFromCloudfront"],"sources":["../../src/hono/geolocation.ts"],"sourcesContent":["import type { Context } from 'hono';\nimport { getRuntimeKey } from 'hono/adapter';\nimport {\n type Geolocation,\n getGeolocationFromCloudflare,\n getGeolocationFromCloudfront,\n getGeolocationFromVercel,\n} from '../utils/geolocation';\n\nexport function geolocation(c: Context): Geolocation {\n if (getRuntimeKey() === 'workerd') return getGeolocationFromCloudflare(c.req.raw);\n if (c.req.header('x-vercel-id')) return getGeolocationFromVercel(c.req.raw);\n return getGeolocationFromCloudfront(c.req.raw);\n}\n"],"mappings":";;;;AASA,SAAgB,YAAY,GAAyB;CACnD,KAAA,
|
|
1
|
+
{"version":3,"file":"geolocation.cjs","names":["getRuntimeKey","getGeolocationFromCloudflare","getGeolocationFromVercel","getGeolocationFromCloudfront"],"sources":["../../src/hono/geolocation.ts"],"sourcesContent":["import type { Context } from 'hono';\nimport { getRuntimeKey } from 'hono/adapter';\nimport {\n type Geolocation,\n getGeolocationFromCloudflare,\n getGeolocationFromCloudfront,\n getGeolocationFromVercel,\n} from '../utils/geolocation';\n\nexport function geolocation(c: Context): Geolocation {\n if (getRuntimeKey() === 'workerd') return getGeolocationFromCloudflare(c.req.raw);\n if (c.req.header('x-vercel-id')) return getGeolocationFromVercel(c.req.raw);\n return getGeolocationFromCloudfront(c.req.raw);\n}\n"],"mappings":";;;;AASA,SAAgB,YAAY,GAAyB;CACnD,KAAA,GAAIA,aAAAA,cAAAA,CAAc,MAAM,WAAW,OAAOC,0BAAAA,6BAA6B,EAAE,IAAI,GAAG;CAChF,IAAI,EAAE,IAAI,OAAO,aAAa,GAAG,OAAOC,0BAAAA,yBAAyB,EAAE,IAAI,GAAG;CAC1E,OAAOC,0BAAAA,6BAA6B,EAAE,IAAI,GAAG;AAC/C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.cjs","names":["Code","StatusCode","StatusError","Details","Status","HTTPException"],"sources":["../../src/hono/handler.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { Context } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\nimport type { RequestIdVariables } from 'hono/request-id';\nimport type { Bindings, HTTPResponseError } from 'hono/types';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\nimport { Details } from '../error/detail';\nimport { Code, Status, StatusCode, StatusError } from '../error/status';\n\nexport type Env = {\n Variables: RequestIdVariables;\n Bindings?: Bindings;\n};\n\ntype AxiosError = {\n code?: string;\n cause?: unknown;\n status?: number;\n message?: string;\n isAxiosError: boolean;\n response?: {\n data: unknown;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n };\n config?: { url?: string; data?: unknown; method?: string; headers?: Record<string, string> };\n};\n\n/**\n * `Code` maps a name to an HTTP status, and that mapping is not injective: 400 is INVALID_ARGUMENT,\n * FAILED_PRECONDITION and OUT_OF_RANGE alike. Reversing it therefore needs a tiebreaker for the\n * statuses several codes share — the rest is derived, so new entries in `Code` come along for free.\n */\nconst CANONICAL_CODE: Record<number, keyof typeof Code> = {\n 400: 'INVALID_ARGUMENT',\n 409: 'ALREADY_EXISTS',\n 500: 'INTERNAL',\n};\n\nconst CODE_BY_HTTP_STATUS: Record<number, keyof typeof Code> = {\n ...Object.fromEntries(Object.entries(Code).map(([name, status]) => [status, name])),\n ...CANONICAL_CODE,\n};\n\n/** Canonical code for errors that only carry an HTTP status, such as hono's `HTTPException`. */\nfunction toStatusCode(status: number, message?: string): StatusCode {\n const code = CODE_BY_HTTP_STATUS[status] ?? (status < 500 ? 'INVALID_ARGUMENT' : 'INTERNAL');\n return StatusCode.of(code, hasText(message) ? message : undefined);\n}\n\nfunction summarize(error: unknown): string {\n if (error instanceof Error) return `${error.name}: ${error.message}`;\n return String(error);\n}\n\nexport function isAxiosError(payload: unknown): payload is AxiosError {\n return (\n payload !== null &&\n typeof payload === 'object' &&\n 'isAxiosError' in payload &&\n payload.isAxiosError === true\n );\n}\n\n/**\n * `instanceof` alone is not enough: two copies of this package (duplicated in a lockfile, or one\n * bundled into a dependency) produce distinct classes, and `Status.adapter` may swap in a\n * host-defined error class entirely. Fall back to recognizing the error by shape.\n */\nexport function isStatusError(payload: unknown): payload is StatusError {\n if (payload instanceof StatusError) return true;\n if (payload === null || typeof payload !== 'object') return false;\n const { status, body } = payload as { status?: unknown; body?: { error?: { code?: unknown } } };\n return typeof status === 'number' && typeof body?.error?.code === 'number';\n}\n\nexport function errorHandler<E extends Env = never>(\n error: Error | HTTPResponseError,\n c: Context<E>\n): Response | Promise<Response> {\n const requestId = c.get('requestId');\n const servingData = `${c.req.method}: ${c.req.path}`;\n const details = Details.new().requestInfo({ requestId, servingData });\n\n if (isStatusError(error)) {\n const body = error.body ?? Status.internal().body();\n body.error.details.push(...details.list);\n // Only 5xx is a server fault; logging expected 4xx at error level drowns out the real ones.\n // Field violations travel inside `details`, so one line carries the whole error.\n const log = error.status >= 500 ? console.error : console.warn;\n log(servingData, JSON.stringify(body.error));\n return c.json(body, error.status as ContentfulStatusCode);\n }\n\n // The client vanished mid-request: tab closed, navigation, or an aborted fetch. Whatever we\n // return is discarded, and nothing failed on our side, so keep it to one terse line. Shows up\n // in local dev because the request body is streamed straight off the socket — a truncated body\n // fails to parse; platforms that buffer the whole request before invoking (API Gateway, Lambda\n // function URLs) never hand us a partial request in the first place.\n if (c.req.raw.signal.aborted) {\n console.warn(`Client closed request: ${servingData}`, summarize(error));\n return Status.cancelled().response(details);\n }\n\n if (error instanceof HTTPException) {\n // Middleware such as `bearerAuth` attaches a prepared response carrying headers the client\n // needs (`WWW-Authenticate`, …); hand that back untouched instead of re-wrapping it.\n const log = error.status >= 500 ? console.error : console.warn;\n log(`HTTP exception: ${servingData}`, error.status, error.message);\n if (error.res) return error.res;\n return toStatusCode(error.status, error.message).response(details);\n }\n\n if (isAxiosError(error)) {\n console.error(`Axios error: ${servingData}`, {\n requestId,\n code: error.code,\n status: error.status ?? error.response?.status,\n message: error.message,\n request: {\n method: error.config?.method,\n url: error.config?.url,\n data: error.config?.data,\n },\n response: { data: error.response?.data },\n });\n return Status.internal('Axios error').response(details);\n }\n\n console.error(`Unknown error: ${servingData}`, requestId, error);\n return Status.internal('Unknown error').response(details);\n}\n"],"mappings":";;;;;;;;;;;AAkCA,MAAM,iBAAoD;CACxD,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAEA,MAAM,sBAAyD;CAC7D,GAAG,OAAO,YAAY,OAAO,QAAQA,qBAAAA,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,CAAC,QAAQ,IAAI,CAAC,CAAC;CAClF,GAAG;AACL;;AAGA,SAAS,aAAa,QAAgB,SAA8B;CAClE,MAAM,OAAO,oBAAoB,YAAY,SAAS,MAAM,qBAAqB;CACjF,OAAOC,qBAAAA,WAAW,GAAG,OAAA,
|
|
1
|
+
{"version":3,"file":"handler.cjs","names":["Code","StatusCode","hasText","StatusError","Details","Status","HTTPException"],"sources":["../../src/hono/handler.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { Context } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\nimport type { RequestIdVariables } from 'hono/request-id';\nimport type { Bindings, HTTPResponseError } from 'hono/types';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\nimport { Details } from '../error/detail';\nimport { Code, Status, StatusCode, StatusError } from '../error/status';\n\nexport type Env = {\n Variables: RequestIdVariables;\n Bindings?: Bindings;\n};\n\ntype AxiosError = {\n code?: string;\n cause?: unknown;\n status?: number;\n message?: string;\n isAxiosError: boolean;\n response?: {\n data: unknown;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n };\n config?: { url?: string; data?: unknown; method?: string; headers?: Record<string, string> };\n};\n\n/**\n * `Code` maps a name to an HTTP status, and that mapping is not injective: 400 is INVALID_ARGUMENT,\n * FAILED_PRECONDITION and OUT_OF_RANGE alike. Reversing it therefore needs a tiebreaker for the\n * statuses several codes share — the rest is derived, so new entries in `Code` come along for free.\n */\nconst CANONICAL_CODE: Record<number, keyof typeof Code> = {\n 400: 'INVALID_ARGUMENT',\n 409: 'ALREADY_EXISTS',\n 500: 'INTERNAL',\n};\n\nconst CODE_BY_HTTP_STATUS: Record<number, keyof typeof Code> = {\n ...Object.fromEntries(Object.entries(Code).map(([name, status]) => [status, name])),\n ...CANONICAL_CODE,\n};\n\n/** Canonical code for errors that only carry an HTTP status, such as hono's `HTTPException`. */\nfunction toStatusCode(status: number, message?: string): StatusCode {\n const code = CODE_BY_HTTP_STATUS[status] ?? (status < 500 ? 'INVALID_ARGUMENT' : 'INTERNAL');\n return StatusCode.of(code, hasText(message) ? message : undefined);\n}\n\nfunction summarize(error: unknown): string {\n if (error instanceof Error) return `${error.name}: ${error.message}`;\n return String(error);\n}\n\nexport function isAxiosError(payload: unknown): payload is AxiosError {\n return (\n payload !== null &&\n typeof payload === 'object' &&\n 'isAxiosError' in payload &&\n payload.isAxiosError === true\n );\n}\n\n/**\n * `instanceof` alone is not enough: two copies of this package (duplicated in a lockfile, or one\n * bundled into a dependency) produce distinct classes, and `Status.adapter` may swap in a\n * host-defined error class entirely. Fall back to recognizing the error by shape.\n */\nexport function isStatusError(payload: unknown): payload is StatusError {\n if (payload instanceof StatusError) return true;\n if (payload === null || typeof payload !== 'object') return false;\n const { status, body } = payload as { status?: unknown; body?: { error?: { code?: unknown } } };\n return typeof status === 'number' && typeof body?.error?.code === 'number';\n}\n\nexport function errorHandler<E extends Env = never>(\n error: Error | HTTPResponseError,\n c: Context<E>\n): Response | Promise<Response> {\n const requestId = c.get('requestId');\n const servingData = `${c.req.method}: ${c.req.path}`;\n const details = Details.new().requestInfo({ requestId, servingData });\n\n if (isStatusError(error)) {\n const body = error.body ?? Status.internal().body();\n body.error.details.push(...details.list);\n // Only 5xx is a server fault; logging expected 4xx at error level drowns out the real ones.\n // Field violations travel inside `details`, so one line carries the whole error.\n const log = error.status >= 500 ? console.error : console.warn;\n log(servingData, JSON.stringify(body.error));\n return c.json(body, error.status as ContentfulStatusCode);\n }\n\n // The client vanished mid-request: tab closed, navigation, or an aborted fetch. Whatever we\n // return is discarded, and nothing failed on our side, so keep it to one terse line. Shows up\n // in local dev because the request body is streamed straight off the socket — a truncated body\n // fails to parse; platforms that buffer the whole request before invoking (API Gateway, Lambda\n // function URLs) never hand us a partial request in the first place.\n if (c.req.raw.signal.aborted) {\n console.warn(`Client closed request: ${servingData}`, summarize(error));\n return Status.cancelled().response(details);\n }\n\n if (error instanceof HTTPException) {\n // Middleware such as `bearerAuth` attaches a prepared response carrying headers the client\n // needs (`WWW-Authenticate`, …); hand that back untouched instead of re-wrapping it.\n const log = error.status >= 500 ? console.error : console.warn;\n log(`HTTP exception: ${servingData}`, error.status, error.message);\n if (error.res) return error.res;\n return toStatusCode(error.status, error.message).response(details);\n }\n\n if (isAxiosError(error)) {\n console.error(`Axios error: ${servingData}`, {\n requestId,\n code: error.code,\n status: error.status ?? error.response?.status,\n message: error.message,\n request: {\n method: error.config?.method,\n url: error.config?.url,\n data: error.config?.data,\n },\n response: { data: error.response?.data },\n });\n return Status.internal('Axios error').response(details);\n }\n\n console.error(`Unknown error: ${servingData}`, requestId, error);\n return Status.internal('Unknown error').response(details);\n}\n"],"mappings":";;;;;;;;;;;AAkCA,MAAM,iBAAoD;CACxD,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAEA,MAAM,sBAAyD;CAC7D,GAAG,OAAO,YAAY,OAAO,QAAQA,qBAAAA,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,CAAC,QAAQ,IAAI,CAAC,CAAC;CAClF,GAAG;AACL;;AAGA,SAAS,aAAa,QAAgB,SAA8B;CAClE,MAAM,OAAO,oBAAoB,YAAY,SAAS,MAAM,qBAAqB;CACjF,OAAOC,qBAAAA,WAAW,GAAG,OAAA,GAAMC,cAAAA,QAAAA,CAAQ,OAAO,IAAI,UAAU,KAAA,CAAS;AACnE;AAEA,SAAS,UAAU,OAAwB;CACzC,IAAI,iBAAiB,OAAO,OAAO,GAAG,MAAM,KAAK,IAAI,MAAM;CAC3D,OAAO,OAAO,KAAK;AACrB;AAEA,SAAgB,aAAa,SAAyC;CACpE,OACE,YAAY,QACZ,OAAO,YAAY,YACnB,kBAAkB,WAClB,QAAQ,iBAAiB;AAE7B;;;;;;AAOA,SAAgB,cAAc,SAA0C;CACtE,IAAI,mBAAmBC,qBAAAA,aAAa,OAAO;CAC3C,IAAI,YAAY,QAAQ,OAAO,YAAY,UAAU,OAAO;CAC5D,MAAM,EAAE,QAAQ,SAAS;CACzB,OAAO,OAAO,WAAW,YAAY,OAAO,MAAM,OAAO,SAAS;AACpE;AAEA,SAAgB,aACd,OACA,GAC8B;CAC9B,MAAM,YAAY,EAAE,IAAI,WAAW;CACnC,MAAM,cAAc,GAAG,EAAE,IAAI,OAAO,IAAI,EAAE,IAAI;CAC9C,MAAM,UAAUC,qBAAAA,QAAQ,IAAI,CAAC,CAAC,YAAY;EAAE;EAAW;CAAY,CAAC;CAEpE,IAAI,cAAc,KAAK,GAAG;EACxB,MAAM,OAAO,MAAM,QAAQC,qBAAAA,OAAO,SAAS,CAAC,CAAC,KAAK;EAClD,KAAK,MAAM,QAAQ,KAAK,GAAG,QAAQ,IAAI;EAIvC,CADY,MAAM,UAAU,MAAM,QAAQ,QAAQ,QAAQ,KAAA,CACtD,aAAa,KAAK,UAAU,KAAK,KAAK,CAAC;EAC3C,OAAO,EAAE,KAAK,MAAM,MAAM,MAA8B;CAC1D;CAOA,IAAI,EAAE,IAAI,IAAI,OAAO,SAAS;EAC5B,QAAQ,KAAK,0BAA0B,eAAe,UAAU,KAAK,CAAC;EACtE,OAAOA,qBAAAA,OAAO,UAAU,CAAC,CAAC,SAAS,OAAO;CAC5C;CAEA,IAAI,iBAAiBC,oBAAAA,eAAe;EAIlC,CADY,MAAM,UAAU,MAAM,QAAQ,QAAQ,QAAQ,KAAA,CACtD,mBAAmB,eAAe,MAAM,QAAQ,MAAM,OAAO;EACjE,IAAI,MAAM,KAAK,OAAO,MAAM;EAC5B,OAAO,aAAa,MAAM,QAAQ,MAAM,OAAO,CAAC,CAAC,SAAS,OAAO;CACnE;CAEA,IAAI,aAAa,KAAK,GAAG;EACvB,QAAQ,MAAM,gBAAgB,eAAe;GAC3C;GACA,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU,MAAM,UAAU;GACxC,SAAS,MAAM;GACf,SAAS;IACP,QAAQ,MAAM,QAAQ;IACtB,KAAK,MAAM,QAAQ;IACnB,MAAM,MAAM,QAAQ;GACtB;GACA,UAAU,EAAE,MAAM,MAAM,UAAU,KAAK;EACzC,CAAC;EACD,OAAOD,qBAAAA,OAAO,SAAS,aAAa,CAAC,CAAC,SAAS,OAAO;CACxD;CAEA,QAAQ,MAAM,kBAAkB,eAAe,WAAW,KAAK;CAC/D,OAAOA,qBAAAA,OAAO,SAAS,eAAe,CAAC,CAAC,SAAS,OAAO;AAC1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.cjs","names":["Details","Status","NEVER"],"sources":["../../src/hono/validator.ts"],"sourcesContent":["import type { ValidationTargets } from 'hono';\nimport { validator } from 'hono/validator';\nimport type { ZodType, output as outputV4 } from 'zod';\nimport {\n NEVER,\n type ZodMiniType,\n type output as outputMini,\n pipe,\n string,\n transform,\n} from 'zod/mini';\nimport { type BadRequest, Details } from '../error/detail';\nimport { Status } from '../error/status';\n\nexport function zValidator<S extends ZodType | ZodMiniType>(\n target: keyof ValidationTargets,\n schema: S\n) {\n return validator(target, async (value) => {\n const result = await schema.safeParseAsync(value);\n if (result.success) return result.data as S extends ZodType ? outputV4<S> : outputMini<S>;\n\n const fieldViolations: BadRequest['fieldViolations'] = result.error.issues.map(\n ({ code, path, message }) => ({\n field: path.join('.'),\n description: message,\n reason: code.toUpperCase(),\n localizedMessage: { locale: 'en-US', message },\n })\n );\n const details = Details.new().badRequest({ fieldViolations });\n throw Status.invalidArgument().error(details);\n });\n}\n\nexport const bigintId = pipe(\n string(),\n transform((input, ctx) => {\n if (!/^(0|[1-9]\\d{0,19})$/.test(input)) {\n const message = `Invalid bigint id: ${input}`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n try {\n return BigInt(input);\n } catch {\n const message = `Parse bigint id: ${input} failed`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n })\n);\n"],"mappings":";;;;;;AAcA,SAAgB,WACd,QACA,QACA;CACA,QAAA,
|
|
1
|
+
{"version":3,"file":"validator.cjs","names":["validator","Details","Status","pipe","string","transform","NEVER"],"sources":["../../src/hono/validator.ts"],"sourcesContent":["import type { ValidationTargets } from 'hono';\nimport { validator } from 'hono/validator';\nimport type { ZodType, output as outputV4 } from 'zod';\nimport {\n NEVER,\n type ZodMiniType,\n type output as outputMini,\n pipe,\n string,\n transform,\n} from 'zod/mini';\nimport { type BadRequest, Details } from '../error/detail';\nimport { Status } from '../error/status';\n\nexport function zValidator<S extends ZodType | ZodMiniType>(\n target: keyof ValidationTargets,\n schema: S\n) {\n return validator(target, async (value) => {\n const result = await schema.safeParseAsync(value);\n if (result.success) return result.data as S extends ZodType ? outputV4<S> : outputMini<S>;\n\n const fieldViolations: BadRequest['fieldViolations'] = result.error.issues.map(\n ({ code, path, message }) => ({\n field: path.join('.'),\n description: message,\n reason: code.toUpperCase(),\n localizedMessage: { locale: 'en-US', message },\n })\n );\n const details = Details.new().badRequest({ fieldViolations });\n throw Status.invalidArgument().error(details);\n });\n}\n\nexport const bigintId = pipe(\n string(),\n transform((input, ctx) => {\n if (!/^(0|[1-9]\\d{0,19})$/.test(input)) {\n const message = `Invalid bigint id: ${input}`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n try {\n return BigInt(input);\n } catch {\n const message = `Parse bigint id: ${input} failed`;\n ctx.issues.push({ code: 'custom', input, message });\n return NEVER;\n }\n })\n);\n"],"mappings":";;;;;;AAcA,SAAgB,WACd,QACA,QACA;CACA,QAAA,GAAOA,eAAAA,UAAAA,CAAU,QAAQ,OAAO,UAAU;EACxC,MAAM,SAAS,MAAM,OAAO,eAAe,KAAK;EAChD,IAAI,OAAO,SAAS,OAAO,OAAO;EAElC,MAAM,kBAAiD,OAAO,MAAM,OAAO,KACxE,EAAE,MAAM,MAAM,eAAe;GAC5B,OAAO,KAAK,KAAK,GAAG;GACpB,aAAa;GACb,QAAQ,KAAK,YAAY;GACzB,kBAAkB;IAAE,QAAQ;IAAS;GAAQ;EAC/C,EACF;EACA,MAAM,UAAUC,qBAAAA,QAAQ,IAAI,CAAC,CAAC,WAAW,EAAE,gBAAgB,CAAC;EAC5D,MAAMC,qBAAAA,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO;CAC9C,CAAC;AACH;AAEA,MAAa,YAAA,GAAWC,SAAAA,KAAAA,EAAAA,GACtBC,SAAAA,OAAAA,CAAO,IAAA,GACPC,SAAAA,UAAAA,EAAW,OAAO,QAAQ;CACxB,IAAI,CAAC,sBAAsB,KAAK,KAAK,GAAG;EACtC,MAAM,UAAU,sBAAsB;EACtC,IAAI,OAAO,KAAK;GAAE,MAAM;GAAU;GAAO;EAAQ,CAAC;EAClD,OAAOC,SAAAA;CACT;CACA,IAAI;EACF,OAAO,OAAO,KAAK;CACrB,QAAQ;EACN,MAAM,UAAU,oBAAoB,MAAM;EAC1C,IAAI,OAAO,KAAK;GAAE,MAAM;GAAU;GAAO;EAAQ,CAAC;EAClD,OAAOA,SAAAA;CACT;AACF,CAAC,CACH"}
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ exports.getGeolocation = require_utils_geolocation.getGeolocation;
|
|
|
37
37
|
exports.getNextPageParam = require_response.getNextPageParam;
|
|
38
38
|
exports.getPreviousPageParam = require_response.getPreviousPageParam;
|
|
39
39
|
exports.initialPageParam = require_response.initialPageParam;
|
|
40
|
+
exports.isErrorReason = require_error_parse.isErrorReason;
|
|
40
41
|
exports.pageParamsSchema = require_response.pageParamsSchema;
|
|
41
42
|
exports.timing = require_utils_timing.timing;
|
|
42
43
|
exports.uid = require_snowflake.uid;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ErrorReason, ResolvedErrorReason } from "./error/reason.cjs";
|
|
2
2
|
import { BadRequest, DebugInfo, Detail, DetailType, Details, ErrorInfo, Help, LocalizedMessage, PreconditionFailure, QuotaFailure, RequestInfo, ResourceInfo, RetryInfo } from "./error/detail.cjs";
|
|
3
3
|
import { CheckoutCreateError, LoginCanceledError, LoginTimeoutError, PurchaseError } from "./error/index.cjs";
|
|
4
|
-
import { getErrorInfo, getErrorMessage, getFieldViolations } from "./error/parse.cjs";
|
|
4
|
+
import { getErrorInfo, getErrorMessage, getFieldViolations, isErrorReason } from "./error/parse.cjs";
|
|
5
5
|
import { ErrorBody, Status, StatusError } from "./error/status.cjs";
|
|
6
6
|
import { getGeolocation } from "./utils/geolocation.cjs";
|
|
7
7
|
import { Cursor, Empty, Entity, EntityId, InfinitePageData, InitParams, Items, NextParams, Page, PageParams, Pages, ParentPageParams, PrevParams, Response, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema } from "./response.cjs";
|
|
@@ -10,4 +10,4 @@ import { index_d_exports } from "./max-length/index.cjs";
|
|
|
10
10
|
import { timing } from "./utils/timing.cjs";
|
|
11
11
|
import { forwardToGoogleTagGateway } from "./utils/google-tag-gateway.cjs";
|
|
12
12
|
import { ISO3166CountryCode, ISO_3601_1 } from "./iso/iso-3601-1.cjs";
|
|
13
|
-
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
|
13
|
+
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, isErrorReason, pageParamsSchema, timing, uid };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ErrorReason, ResolvedErrorReason } from "./error/reason.mjs";
|
|
2
2
|
import { BadRequest, DebugInfo, Detail, DetailType, Details, ErrorInfo, Help, LocalizedMessage, PreconditionFailure, QuotaFailure, RequestInfo, ResourceInfo, RetryInfo } from "./error/detail.mjs";
|
|
3
3
|
import { CheckoutCreateError, LoginCanceledError, LoginTimeoutError, PurchaseError } from "./error/index.mjs";
|
|
4
|
-
import { getErrorInfo, getErrorMessage, getFieldViolations } from "./error/parse.mjs";
|
|
4
|
+
import { getErrorInfo, getErrorMessage, getFieldViolations, isErrorReason } from "./error/parse.mjs";
|
|
5
5
|
import { ErrorBody, Status, StatusError } from "./error/status.mjs";
|
|
6
6
|
import { getGeolocation } from "./utils/geolocation.mjs";
|
|
7
7
|
import { Cursor, Empty, Entity, EntityId, InfinitePageData, InitParams, Items, NextParams, Page, PageParams, Pages, ParentPageParams, PrevParams, Response, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema } from "./response.mjs";
|
|
@@ -10,4 +10,4 @@ import { index_d_exports } from "./max-length/index.mjs";
|
|
|
10
10
|
import { timing } from "./utils/timing.mjs";
|
|
11
11
|
import { forwardToGoogleTagGateway } from "./utils/google-tag-gateway.mjs";
|
|
12
12
|
import { ISO3166CountryCode, ISO_3601_1 } from "./iso/iso-3601-1.mjs";
|
|
13
|
-
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
|
13
|
+
export { type BadRequest, CheckoutCreateError, Cursor, type DebugInfo, type Detail, DetailType, Details, type Empty, type Entity, type EntityId, type ErrorBody, type ErrorInfo, type ErrorReason, type Help, type ISO3166CountryCode, ISO_3601_1, type InfinitePageData, type InitParams, Items, type LocalizedMessage, LoginCanceledError, LoginTimeoutError, index_d_exports as MAX_LENGTH, type NextParams, type Page, type PageParams, Pages, type ParentPageParams, type PreconditionFailure, type PrevParams, PurchaseError, type QuotaFailure, type RequestInfo, type ResolvedErrorReason, type ResourceInfo, type Response, type RetryInfo, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, isErrorReason, pageParamsSchema, timing, uid };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CheckoutCreateError, LoginCanceledError, LoginTimeoutError, PurchaseError } from "./error/index.mjs";
|
|
2
2
|
import { DetailType, Details } from "./error/detail.mjs";
|
|
3
3
|
import { Status, StatusError } from "./error/status.mjs";
|
|
4
|
-
import { getErrorInfo, getErrorMessage, getFieldViolations } from "./error/parse.mjs";
|
|
4
|
+
import { getErrorInfo, getErrorMessage, getFieldViolations, isErrorReason } from "./error/parse.mjs";
|
|
5
5
|
import { Cursor, Items, Pages, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema } from "./response.mjs";
|
|
6
6
|
import { UidGenerator, uid } from "./snowflake.mjs";
|
|
7
7
|
import { max_length_exports } from "./max-length/index.mjs";
|
|
@@ -9,4 +9,4 @@ import { timing } from "./utils/timing.mjs";
|
|
|
9
9
|
import { getGeolocation } from "./utils/geolocation.mjs";
|
|
10
10
|
import { forwardToGoogleTagGateway } from "./utils/google-tag-gateway.mjs";
|
|
11
11
|
import { ISO_3601_1 } from "./iso/iso-3601-1.mjs";
|
|
12
|
-
export { CheckoutCreateError, Cursor, DetailType, Details, ISO_3601_1, Items, LoginCanceledError, LoginTimeoutError, max_length_exports as MAX_LENGTH, Pages, PurchaseError, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, pageParamsSchema, timing, uid };
|
|
12
|
+
export { CheckoutCreateError, Cursor, DetailType, Details, ISO_3601_1, Items, LoginCanceledError, LoginTimeoutError, max_length_exports as MAX_LENGTH, Pages, PurchaseError, Status, StatusError, UidGenerator, forwardToGoogleTagGateway, getErrorInfo, getErrorMessage, getFieldViolations, getGeolocation, getNextPageParam, getPreviousPageParam, initialPageParam, isErrorReason, pageParamsSchema, timing, uid };
|
package/dist/response.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.cjs","names":["coerce"],"sources":["../src/response.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport { _default, coerce, int, maximum, minimum, object, optional, string } from 'zod/mini';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport type Empty = {};\nexport type EntityId = string | number;\nexport type Entity = { id: EntityId };\n\nexport type Response<T = never> = { data: T };\n\nexport type InitParams = { limit: number; parent?: string };\nexport type NextParams = { limit: number; parent?: string; next: string };\nexport type PrevParams = { limit: number; parent?: string; prev: string };\nexport type PageParams = { limit: number; prev?: string; next?: string };\nexport type ParentPageParams = { limit: number; parent: string; prev?: string; next?: string };\nexport type Page<T = never> = { data: T[]; paging: { next?: string; prev?: string } };\n\nexport function pageParamsSchema(max = 100, defaultLimit = 20) {\n return object({\n limit: _default(coerce.number().check(int(), minimum(1), maximum(max)), defaultLimit),\n prev: optional(string()),\n next: optional(string()),\n });\n}\n\nexport const Cursor = {\n of(\n prev: bigint | number | string | undefined,\n next: bigint | number | string | undefined\n ): Page['paging'] {\n return {\n prev: prev ? this.encode(prev) : undefined,\n next: next ? this.encode(next) : undefined,\n };\n },\n empty(): Page['paging'] {\n return { prev: undefined, next: undefined };\n },\n encode(id: bigint | number | string | undefined): string | undefined {\n if (!id) return undefined;\n return Buffer.from(id.toString(), 'utf-8').toString('base64');\n },\n decode<T extends 'bigint' | 'number' | 'string' = 'bigint'>(\n token: string,\n type: T = 'bigint' as T\n ): T extends 'bigint' ? bigint : T extends 'number' ? number : string {\n const value = Buffer.from(token, 'base64').toString('utf-8');\n if (type === 'bigint')\n return BigInt(value) as T extends 'bigint' ? bigint : T extends 'number' ? number : string;\n if (type === 'number')\n return Number(value) as T extends 'bigint' ? bigint : T extends 'number' ? number : string;\n return value as T extends 'bigint' ? bigint : T extends 'number' ? number : string;\n },\n};\n\nexport const initialPageParam: Page['paging'] = {};\n\nexport function getPreviousPageParam<T = never>(first: Page<T>): Page['paging'] | null {\n return hasText(first.paging.prev) ? { prev: first.paging.prev } : null;\n}\n\nexport function getNextPageParam<T = never>(last: Page<T>): Page['paging'] | null {\n return hasText(last.paging.next) ? { next: last.paging.next } : null;\n}\n\nexport type InfinitePageData<T> = {\n pages: Array<Page<T>>;\n pageParams: Array<Page['paging']>;\n};\n\nexport const Pages = {\n flatten<T>(data: InfinitePageData<T>): T[] {\n return data.pages.flatMap((page) => page.data);\n },\n dedupe<T extends Entity>(data: InfinitePageData<T>): T[] {\n const seen = new Set<EntityId>();\n const result: T[] = [];\n for (const page of data.pages) {\n for (const item of page.data) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n }\n return result;\n },\n prepend<T>(item: T) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n const first = data.pages.at(0);\n const rest = data.pages.slice(1);\n if (!first) return { ...data, pages: [{ data: [item], paging: {} }] };\n return { ...data, pages: [{ ...first, data: [item, ...first.data] }, ...rest] };\n };\n },\n append<T>(item: T) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n const pages = data.pages;\n const last = pages.at(-1);\n if (!last) return { ...data, pages: [{ data: [item], paging: {} }] };\n return { ...data, pages: [...pages.slice(0, -1), { ...last, data: [...last.data, item] }] };\n };\n },\n update<T>(updater: T, predicate: (item: T) => boolean) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n return {\n ...data,\n pages: data.pages.map((page) => ({\n ...page,\n data: page.data.map((item) => (predicate(item) ? updater : item)),\n })),\n };\n };\n },\n updateById<T extends Entity>(updated: T) {\n return this.update<T>(updated, (item) => item.id === updated.id);\n },\n remove<T>(predicate: (item: T) => boolean) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n return {\n ...data,\n pages: data.pages.map((page) => ({\n ...page,\n data: page.data.filter((item) => !predicate(item)),\n })),\n };\n };\n },\n removeById<T extends Entity>(id: EntityId) {\n return this.remove<T>((item) => item.id === id);\n },\n};\n\nexport const Items = {\n push<T>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n return [...data, ...newItems];\n };\n },\n unshift<T>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n return [...newItems, ...data];\n };\n },\n pushAndDedupe<T extends Entity>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n const seen = new Set<EntityId>();\n const result: T[] = [];\n for (const item of data) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n for (const item of newItems) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n return result;\n };\n },\n unshiftAndDedupe<T extends Entity>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n const seen = new Set<EntityId>();\n const result: T[] = [];\n for (const item of newItems) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n for (const item of data) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n return result;\n };\n },\n update<T>(updater: T, predicate: (item: T) => boolean) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return data;\n return data.map((item) => (predicate(item) ? updater : item));\n };\n },\n updateById<T extends Entity>(updated: T) {\n return this.update<T>(updated, (item) => item.id === updated.id);\n },\n remove<T>(predicate: (item: T) => boolean) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return data;\n return data.filter((item) => !predicate(item));\n };\n },\n removeById<T extends Entity>(id: EntityId) {\n return this.remove<T>((item) => item.id === id);\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"response.cjs","names":["object","_default","coerce","int","minimum","maximum","optional","string","hasText"],"sources":["../src/response.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport { _default, coerce, int, maximum, minimum, object, optional, string } from 'zod/mini';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport type Empty = {};\nexport type EntityId = string | number;\nexport type Entity = { id: EntityId };\n\nexport type Response<T = never> = { data: T };\n\nexport type InitParams = { limit: number; parent?: string };\nexport type NextParams = { limit: number; parent?: string; next: string };\nexport type PrevParams = { limit: number; parent?: string; prev: string };\nexport type PageParams = { limit: number; prev?: string; next?: string };\nexport type ParentPageParams = { limit: number; parent: string; prev?: string; next?: string };\nexport type Page<T = never> = { data: T[]; paging: { next?: string; prev?: string } };\n\nexport function pageParamsSchema(max = 100, defaultLimit = 20) {\n return object({\n limit: _default(coerce.number().check(int(), minimum(1), maximum(max)), defaultLimit),\n prev: optional(string()),\n next: optional(string()),\n });\n}\n\nexport const Cursor = {\n of(\n prev: bigint | number | string | undefined,\n next: bigint | number | string | undefined\n ): Page['paging'] {\n return {\n prev: prev ? this.encode(prev) : undefined,\n next: next ? this.encode(next) : undefined,\n };\n },\n empty(): Page['paging'] {\n return { prev: undefined, next: undefined };\n },\n encode(id: bigint | number | string | undefined): string | undefined {\n if (!id) return undefined;\n return Buffer.from(id.toString(), 'utf-8').toString('base64');\n },\n decode<T extends 'bigint' | 'number' | 'string' = 'bigint'>(\n token: string,\n type: T = 'bigint' as T\n ): T extends 'bigint' ? bigint : T extends 'number' ? number : string {\n const value = Buffer.from(token, 'base64').toString('utf-8');\n if (type === 'bigint')\n return BigInt(value) as T extends 'bigint' ? bigint : T extends 'number' ? number : string;\n if (type === 'number')\n return Number(value) as T extends 'bigint' ? bigint : T extends 'number' ? number : string;\n return value as T extends 'bigint' ? bigint : T extends 'number' ? number : string;\n },\n};\n\nexport const initialPageParam: Page['paging'] = {};\n\nexport function getPreviousPageParam<T = never>(first: Page<T>): Page['paging'] | null {\n return hasText(first.paging.prev) ? { prev: first.paging.prev } : null;\n}\n\nexport function getNextPageParam<T = never>(last: Page<T>): Page['paging'] | null {\n return hasText(last.paging.next) ? { next: last.paging.next } : null;\n}\n\nexport type InfinitePageData<T> = {\n pages: Array<Page<T>>;\n pageParams: Array<Page['paging']>;\n};\n\nexport const Pages = {\n flatten<T>(data: InfinitePageData<T>): T[] {\n return data.pages.flatMap((page) => page.data);\n },\n dedupe<T extends Entity>(data: InfinitePageData<T>): T[] {\n const seen = new Set<EntityId>();\n const result: T[] = [];\n for (const page of data.pages) {\n for (const item of page.data) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n }\n return result;\n },\n prepend<T>(item: T) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n const first = data.pages.at(0);\n const rest = data.pages.slice(1);\n if (!first) return { ...data, pages: [{ data: [item], paging: {} }] };\n return { ...data, pages: [{ ...first, data: [item, ...first.data] }, ...rest] };\n };\n },\n append<T>(item: T) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n const pages = data.pages;\n const last = pages.at(-1);\n if (!last) return { ...data, pages: [{ data: [item], paging: {} }] };\n return { ...data, pages: [...pages.slice(0, -1), { ...last, data: [...last.data, item] }] };\n };\n },\n update<T>(updater: T, predicate: (item: T) => boolean) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n return {\n ...data,\n pages: data.pages.map((page) => ({\n ...page,\n data: page.data.map((item) => (predicate(item) ? updater : item)),\n })),\n };\n };\n },\n updateById<T extends Entity>(updated: T) {\n return this.update<T>(updated, (item) => item.id === updated.id);\n },\n remove<T>(predicate: (item: T) => boolean) {\n return (data: InfinitePageData<T> | undefined): InfinitePageData<T> | undefined => {\n if (!data) return data;\n return {\n ...data,\n pages: data.pages.map((page) => ({\n ...page,\n data: page.data.filter((item) => !predicate(item)),\n })),\n };\n };\n },\n removeById<T extends Entity>(id: EntityId) {\n return this.remove<T>((item) => item.id === id);\n },\n};\n\nexport const Items = {\n push<T>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n return [...data, ...newItems];\n };\n },\n unshift<T>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n return [...newItems, ...data];\n };\n },\n pushAndDedupe<T extends Entity>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n const seen = new Set<EntityId>();\n const result: T[] = [];\n for (const item of data) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n for (const item of newItems) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n return result;\n };\n },\n unshiftAndDedupe<T extends Entity>(newItems: T[]) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return newItems;\n const seen = new Set<EntityId>();\n const result: T[] = [];\n for (const item of newItems) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n for (const item of data) {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n result.push(item);\n }\n }\n return result;\n };\n },\n update<T>(updater: T, predicate: (item: T) => boolean) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return data;\n return data.map((item) => (predicate(item) ? updater : item));\n };\n },\n updateById<T extends Entity>(updated: T) {\n return this.update<T>(updated, (item) => item.id === updated.id);\n },\n remove<T>(predicate: (item: T) => boolean) {\n return (data: T[] | undefined): T[] | undefined => {\n if (!data) return data;\n return data.filter((item) => !predicate(item));\n };\n },\n removeById<T extends Entity>(id: EntityId) {\n return this.remove<T>((item) => item.id === id);\n },\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Status"],"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { Status } from '../error/status';\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nfunction verifyTimestamp(webhookTimestamp: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = Number.parseInt(webhookTimestamp, 10);\n if (Number.isNaN(timestamp)) {\n throw Status.invalidArgument('invalid webhook timestamp').error();\n }\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too old').error();\n }\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too new').error();\n }\n return timestamp;\n}\n\n/**\n * reference: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript\n * hono usage:\n * ```ts\n * const webhook = verifyStandardWebhook(c.req.header(), await c.req.text(), 'secret');\n * ```\n */\nexport function verifyStandardWebhook<T = unknown>(\n headers: Record<string, string>,\n payload: string,\n secret: string\n): T {\n const webhookId = headers['webhook-id'];\n const webhookTimestamp = headers['webhook-timestamp'];\n const webhookSignature = headers['webhook-signature'];\n if (!webhookId || !webhookTimestamp || !webhookSignature) {\n throw Status.invalidArgument('invalid webhook').error();\n }\n const timestamp = verifyTimestamp(webhookTimestamp);\n\n const encoder = new TextEncoder();\n const toSign = encoder.encode(`${webhookId}.${timestamp}.${payload}`);\n const hmac = createHmac('sha256', Buffer.from(secret, 'base64'));\n const digest = hmac.update(toSign).digest();\n\n const computedSignature = `v1,${Buffer.from(digest).toString('base64')}`;\n const expectedSignature = computedSignature.split(',')[1];\n const passedSignatures = webhookSignature.split(' ');\n\n for (const versionedSignature of passedSignatures) {\n const [version, signature] = versionedSignature.split(',');\n if (version !== 'v1') continue;\n if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) {\n try {\n return JSON.parse(payload) as T;\n } catch {\n console.error('invalid payload', payload);\n throw Status.invalidArgument('invalid webhook payload').error();\n }\n }\n }\n console.error('webhook verification failed');\n throw Status.invalidArgument('invalid webhook signature').error();\n}\n"],"mappings":";;;;AAGA,MAAM,+BAA+B;AAErC,SAAS,gBAAgB,kBAA0B;CACjD,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,MAAM,YAAY,OAAO,SAAS,kBAAkB,EAAE;CACtD,IAAI,OAAO,MAAM,SAAS,GACxB,MAAMA,qBAAAA,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;CAElE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,OAAO;AACT;;;;;;;;AASA,SAAgB,sBACd,SACA,SACA,QACG;CACH,MAAM,YAAY,QAAQ;CAC1B,MAAM,mBAAmB,QAAQ;CACjC,MAAM,mBAAmB,QAAQ;CACjC,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,kBACtC,MAAMA,qBAAAA,OAAO,gBAAgB,iBAAiB,CAAC,CAAC,MAAM;CAExD,MAAM,YAAY,gBAAgB,gBAAgB;CAElD,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,SAAS,QAAQ,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS;CAEpE,MAAM,UAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Status","createHmac","timingSafeEqual"],"sources":["../../src/webhook/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { Status } from '../error/status';\n\nconst WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60; // 5 minutes\n\nfunction verifyTimestamp(webhookTimestamp: string) {\n const now = Math.floor(Date.now() / 1000);\n const timestamp = Number.parseInt(webhookTimestamp, 10);\n if (Number.isNaN(timestamp)) {\n throw Status.invalidArgument('invalid webhook timestamp').error();\n }\n if (timestamp < now - WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too old').error();\n }\n if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {\n throw Status.invalidArgument('webhook timestamp is too new').error();\n }\n return timestamp;\n}\n\n/**\n * reference: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript\n * hono usage:\n * ```ts\n * const webhook = verifyStandardWebhook(c.req.header(), await c.req.text(), 'secret');\n * ```\n */\nexport function verifyStandardWebhook<T = unknown>(\n headers: Record<string, string>,\n payload: string,\n secret: string\n): T {\n const webhookId = headers['webhook-id'];\n const webhookTimestamp = headers['webhook-timestamp'];\n const webhookSignature = headers['webhook-signature'];\n if (!webhookId || !webhookTimestamp || !webhookSignature) {\n throw Status.invalidArgument('invalid webhook').error();\n }\n const timestamp = verifyTimestamp(webhookTimestamp);\n\n const encoder = new TextEncoder();\n const toSign = encoder.encode(`${webhookId}.${timestamp}.${payload}`);\n const hmac = createHmac('sha256', Buffer.from(secret, 'base64'));\n const digest = hmac.update(toSign).digest();\n\n const computedSignature = `v1,${Buffer.from(digest).toString('base64')}`;\n const expectedSignature = computedSignature.split(',')[1];\n const passedSignatures = webhookSignature.split(' ');\n\n for (const versionedSignature of passedSignatures) {\n const [version, signature] = versionedSignature.split(',');\n if (version !== 'v1') continue;\n if (timingSafeEqual(encoder.encode(signature), encoder.encode(expectedSignature))) {\n try {\n return JSON.parse(payload) as T;\n } catch {\n console.error('invalid payload', payload);\n throw Status.invalidArgument('invalid webhook payload').error();\n }\n }\n }\n console.error('webhook verification failed');\n throw Status.invalidArgument('invalid webhook signature').error();\n}\n"],"mappings":";;;;AAGA,MAAM,+BAA+B;AAErC,SAAS,gBAAgB,kBAA0B;CACjD,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,MAAM,YAAY,OAAO,SAAS,kBAAkB,EAAE;CACtD,IAAI,OAAO,MAAM,SAAS,GACxB,MAAMA,qBAAAA,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;CAElE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,IAAI,YAAY,MAAM,8BACpB,MAAMA,qBAAAA,OAAO,gBAAgB,8BAA8B,CAAC,CAAC,MAAM;CAErE,OAAO;AACT;;;;;;;;AASA,SAAgB,sBACd,SACA,SACA,QACG;CACH,MAAM,YAAY,QAAQ;CAC1B,MAAM,mBAAmB,QAAQ;CACjC,MAAM,mBAAmB,QAAQ;CACjC,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,kBACtC,MAAMA,qBAAAA,OAAO,gBAAgB,iBAAiB,CAAC,CAAC,MAAM;CAExD,MAAM,YAAY,gBAAgB,gBAAgB;CAElD,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,SAAS,QAAQ,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS;CAEpE,MAAM,UAAA,GADOC,YAAAA,WAAAA,CAAW,UAAU,OAAO,KAAK,QAAQ,QAAQ,CAC5C,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO;CAG1C,MAAM,oBAAoB,MADM,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,IACzB,MAAM,GAAG,CAAC,CAAC;CACvD,MAAM,mBAAmB,iBAAiB,MAAM,GAAG;CAEnD,KAAK,MAAM,sBAAsB,kBAAkB;EACjD,MAAM,CAAC,SAAS,aAAa,mBAAmB,MAAM,GAAG;EACzD,IAAI,YAAY,MAAM;EACtB,KAAA,GAAIC,YAAAA,gBAAAA,CAAgB,QAAQ,OAAO,SAAS,GAAG,QAAQ,OAAO,iBAAiB,CAAC,GAC9E,IAAI;GACF,OAAO,KAAK,MAAM,OAAO;EAC3B,QAAQ;GACN,QAAQ,MAAM,mBAAmB,OAAO;GACxC,MAAMF,qBAAAA,OAAO,gBAAgB,yBAAyB,CAAC,CAAC,MAAM;EAChE;CAEJ;CACA,QAAQ,MAAM,6BAA6B;CAC3C,MAAMA,qBAAAA,OAAO,gBAAgB,2BAA2B,CAAC,CAAC,MAAM;AAClE"}
|