@prairielearn/error 3.0.12 → 3.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @prairielearn/error
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 452f675: Add a shared generator for error correlation IDs.
8
+
9
+ ## 3.0.13
10
+
11
+ ### Patch Changes
12
+
13
+ - e2ed7cd: Apply additional eslint unicorn rules
14
+
3
15
  ## 3.0.12
4
16
 
5
17
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { type HtmlSafeString } from '@prairielearn/html';
2
2
  export { formatErrorStack, formatErrorStackSafe } from './format.js';
3
+ /** Generates an uppercase alphanumeric correlation ID for an error. */
4
+ export declare function generateErrorId(): string;
3
5
  interface ErrorWithData extends Error {
4
6
  data: any;
5
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAErE,UAAU,aAAc,SAAQ,KAAK;IACnC,IAAI,EAAE,GAAG,CAAC;CACX;AAED,UAAU,aAAc,SAAQ,KAAK;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,eAAgB,SAAQ,KAAK;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,sBAAuB,SAAQ,aAAa,EAAE,eAAe;CAAG;AAK1E,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC;AACvE,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,sBAAsB,CAAC;AAQzF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,aAAa,CAItE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAIzE;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,aAAa,CAK1D;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAOlE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,GAAG,EACR,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,GAC3E,sBAAsB,CAWxB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAK1D;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlE;CACF","sourcesContent":["import { isError } from 'es-toolkit';\n\nimport { type HtmlSafeString } from '@prairielearn/html';\n\nexport { formatErrorStack, formatErrorStackSafe } from './format.js';\n\ninterface ErrorWithData extends Error {\n data: any;\n}\n\ninterface ErrorWithInfo extends Error {\n info: string;\n}\n\ninterface ErrorWithStatus extends Error {\n status: number;\n}\n\ninterface ErrorWithStatusAndData extends ErrorWithData, ErrorWithStatus {}\n\n// TODO: rename all functions include \"error\" in the name so that they can\n// be more easily imported as named imports.\n\nexport function make(status: number, message: string): ErrorWithStatus;\nexport function make(status: number, message: string, data: any): ErrorWithStatusAndData;\nexport function make(status: number, message: string, data?: any): ErrorWithStatusAndData {\n const err = new Error(message) as ErrorWithStatusAndData;\n err.status = status;\n if (data) err.data = data;\n return err;\n}\n\nexport function makeWithData(message: string, data: any): ErrorWithData {\n const err = new Error(message) as ErrorWithData;\n err.data = data;\n return err;\n}\n\nexport function makeWithInfo(message: string, info: string): ErrorWithInfo {\n const err = new Error(message) as ErrorWithInfo;\n err.info = info;\n return err;\n}\n\nexport function addData(err: any, data: any): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n Object.assign(newErr.data, data);\n return newErr;\n}\n\nexport function newMessage(err: any, newMsg: string): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n newErr.data._previousMessages = newErr.data._previousMessages || [];\n newErr.data._previousMessages.splice(0, 0, newErr.message);\n newErr.message = `${newMsg}: ${newErr.message}`;\n return newErr;\n}\n\n/**\n * Create a new error based an existing one, optionally adding status, message,\n * and/or data. The existing error will be set as the `cause` of the new error.\n *\n * @param err An existing error.\n * @param param\n * @param param.status Status code to set on the new error.\n * @param param.message Message to add to the new error.\n * @param param.data Data to set on the new error.\n * @returns The augmented error.\n */\nexport function augmentError(\n err: any,\n { status, message, data }: { status?: number; message?: string; data?: any },\n): ErrorWithStatusAndData {\n let newErr: ErrorWithStatusAndData;\n if (err instanceof Error) {\n const combinedMessage = message ? `${message}: ${err.message}` : err.message;\n newErr = new Error(combinedMessage, { cause: err }) as ErrorWithStatusAndData;\n } else {\n newErr = new Error(message ?? String(err)) as ErrorWithStatusAndData;\n }\n newErr.status = status ?? 500;\n newErr.data = data;\n return newErr;\n}\n\nexport interface AugmentedErrorOptions {\n status?: number;\n data?: any;\n info?: HtmlSafeString;\n cause?: unknown;\n}\n\nexport class AugmentedError extends Error {\n status: number;\n data?: any;\n info?: string;\n\n constructor(message: string, options: AugmentedErrorOptions) {\n super(message, { cause: options.cause });\n this.status = options.status ?? 500;\n this.data = options.data;\n this.info = options.info?.toString();\n }\n}\n\nexport class HttpStatusError extends Error {\n status: number;\n\n constructor(status: number, message: string, options?: ErrorOptions) {\n super(message, options);\n this.status = status;\n }\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAIrE,uEAAuE;AACvE,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,UAAU,aAAc,SAAQ,KAAK;IACnC,IAAI,EAAE,GAAG,CAAC;CACX;AAED,UAAU,aAAc,SAAQ,KAAK;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,eAAgB,SAAQ,KAAK;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,sBAAuB,SAAQ,aAAa,EAAE,eAAe;CAAG;AAK1E,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC;AACvE,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,sBAAsB,CAAC;AAQzF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,aAAa,CAItE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAIzE;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,aAAa,CAK1D;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAOlE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,GAAG,EACR,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,GAC3E,sBAAsB,CAWxB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAK1D;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlE;CACF","sourcesContent":["import { isError, sampleSize } from 'es-toolkit';\n\nimport { type HtmlSafeString } from '@prairielearn/html';\n\nexport { formatErrorStack, formatErrorStackSafe } from './format.js';\n\nconst ERROR_ID_CHARACTERS = [...'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'];\n\n/** Generates an uppercase alphanumeric correlation ID for an error. */\nexport function generateErrorId(): string {\n return sampleSize(ERROR_ID_CHARACTERS, 12).join('');\n}\n\ninterface ErrorWithData extends Error {\n data: any;\n}\n\ninterface ErrorWithInfo extends Error {\n info: string;\n}\n\ninterface ErrorWithStatus extends Error {\n status: number;\n}\n\ninterface ErrorWithStatusAndData extends ErrorWithData, ErrorWithStatus {}\n\n// TODO: rename all functions include \"error\" in the name so that they can\n// be more easily imported as named imports.\n\nexport function make(status: number, message: string): ErrorWithStatus;\nexport function make(status: number, message: string, data: any): ErrorWithStatusAndData;\nexport function make(status: number, message: string, data?: any): ErrorWithStatusAndData {\n const err = new Error(message) as ErrorWithStatusAndData;\n err.status = status;\n if (data) err.data = data;\n return err;\n}\n\nexport function makeWithData(message: string, data: any): ErrorWithData {\n const err = new Error(message) as ErrorWithData;\n err.data = data;\n return err;\n}\n\nexport function makeWithInfo(message: string, info: string): ErrorWithInfo {\n const err = new Error(message) as ErrorWithInfo;\n err.info = info;\n return err;\n}\n\nexport function addData(err: any, data: any): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n Object.assign(newErr.data, data);\n return newErr;\n}\n\nexport function newMessage(err: any, newMsg: string): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n newErr.data._previousMessages = newErr.data._previousMessages || [];\n newErr.data._previousMessages.unshift(newErr.message);\n newErr.message = `${newMsg}: ${newErr.message}`;\n return newErr;\n}\n\n/**\n * Create a new error based an existing one, optionally adding status, message,\n * and/or data. The existing error will be set as the `cause` of the new error.\n *\n * @param err An existing error.\n * @param param\n * @param param.status Status code to set on the new error.\n * @param param.message Message to add to the new error.\n * @param param.data Data to set on the new error.\n * @returns The augmented error.\n */\nexport function augmentError(\n err: any,\n { status, message, data }: { status?: number; message?: string; data?: any },\n): ErrorWithStatusAndData {\n let newErr: ErrorWithStatusAndData;\n if (err instanceof Error) {\n const combinedMessage = message ? `${message}: ${err.message}` : err.message;\n newErr = new Error(combinedMessage, { cause: err }) as ErrorWithStatusAndData;\n } else {\n newErr = new Error(message ?? String(err)) as ErrorWithStatusAndData;\n }\n newErr.status = status ?? 500;\n newErr.data = data;\n return newErr;\n}\n\nexport interface AugmentedErrorOptions {\n status?: number;\n data?: any;\n info?: HtmlSafeString;\n cause?: unknown;\n}\n\nexport class AugmentedError extends Error {\n status: number;\n data?: any;\n info?: string;\n\n constructor(message: string, options: AugmentedErrorOptions) {\n super(message, { cause: options.cause });\n this.status = options.status ?? 500;\n this.data = options.data;\n this.info = options.info?.toString();\n }\n}\n\nexport class HttpStatusError extends Error {\n status: number;\n\n constructor(status: number, message: string, options?: ErrorOptions) {\n super(message, options);\n this.status = status;\n }\n}\n"]}
package/dist/index.js CHANGED
@@ -1,6 +1,11 @@
1
- import { isError } from 'es-toolkit';
1
+ import { isError, sampleSize } from 'es-toolkit';
2
2
  import {} from '@prairielearn/html';
3
3
  export { formatErrorStack, formatErrorStackSafe } from './format.js';
4
+ const ERROR_ID_CHARACTERS = [...'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'];
5
+ /** Generates an uppercase alphanumeric correlation ID for an error. */
6
+ export function generateErrorId() {
7
+ return sampleSize(ERROR_ID_CHARACTERS, 12).join('');
8
+ }
4
9
  export function make(status, message, data) {
5
10
  const err = new Error(message);
6
11
  err.status = status;
@@ -28,7 +33,7 @@ export function newMessage(err, newMsg) {
28
33
  const newErr = (isError(err) ? err : new Error(String(err)));
29
34
  newErr.data = newErr.data || {};
30
35
  newErr.data._previousMessages = newErr.data._previousMessages || [];
31
- newErr.data._previousMessages.splice(0, 0, newErr.message);
36
+ newErr.data._previousMessages.unshift(newErr.message);
32
37
  newErr.message = `${newMsg}: ${newErr.message}`;
33
38
  return newErr;
34
39
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { isError } from 'es-toolkit';\n\nimport { type HtmlSafeString } from '@prairielearn/html';\n\nexport { formatErrorStack, formatErrorStackSafe } from './format.js';\n\ninterface ErrorWithData extends Error {\n data: any;\n}\n\ninterface ErrorWithInfo extends Error {\n info: string;\n}\n\ninterface ErrorWithStatus extends Error {\n status: number;\n}\n\ninterface ErrorWithStatusAndData extends ErrorWithData, ErrorWithStatus {}\n\n// TODO: rename all functions include \"error\" in the name so that they can\n// be more easily imported as named imports.\n\nexport function make(status: number, message: string): ErrorWithStatus;\nexport function make(status: number, message: string, data: any): ErrorWithStatusAndData;\nexport function make(status: number, message: string, data?: any): ErrorWithStatusAndData {\n const err = new Error(message) as ErrorWithStatusAndData;\n err.status = status;\n if (data) err.data = data;\n return err;\n}\n\nexport function makeWithData(message: string, data: any): ErrorWithData {\n const err = new Error(message) as ErrorWithData;\n err.data = data;\n return err;\n}\n\nexport function makeWithInfo(message: string, info: string): ErrorWithInfo {\n const err = new Error(message) as ErrorWithInfo;\n err.info = info;\n return err;\n}\n\nexport function addData(err: any, data: any): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n Object.assign(newErr.data, data);\n return newErr;\n}\n\nexport function newMessage(err: any, newMsg: string): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n newErr.data._previousMessages = newErr.data._previousMessages || [];\n newErr.data._previousMessages.splice(0, 0, newErr.message);\n newErr.message = `${newMsg}: ${newErr.message}`;\n return newErr;\n}\n\n/**\n * Create a new error based an existing one, optionally adding status, message,\n * and/or data. The existing error will be set as the `cause` of the new error.\n *\n * @param err An existing error.\n * @param param\n * @param param.status Status code to set on the new error.\n * @param param.message Message to add to the new error.\n * @param param.data Data to set on the new error.\n * @returns The augmented error.\n */\nexport function augmentError(\n err: any,\n { status, message, data }: { status?: number; message?: string; data?: any },\n): ErrorWithStatusAndData {\n let newErr: ErrorWithStatusAndData;\n if (err instanceof Error) {\n const combinedMessage = message ? `${message}: ${err.message}` : err.message;\n newErr = new Error(combinedMessage, { cause: err }) as ErrorWithStatusAndData;\n } else {\n newErr = new Error(message ?? String(err)) as ErrorWithStatusAndData;\n }\n newErr.status = status ?? 500;\n newErr.data = data;\n return newErr;\n}\n\nexport interface AugmentedErrorOptions {\n status?: number;\n data?: any;\n info?: HtmlSafeString;\n cause?: unknown;\n}\n\nexport class AugmentedError extends Error {\n status: number;\n data?: any;\n info?: string;\n\n constructor(message: string, options: AugmentedErrorOptions) {\n super(message, { cause: options.cause });\n this.status = options.status ?? 500;\n this.data = options.data;\n this.info = options.info?.toString();\n }\n}\n\nexport class HttpStatusError extends Error {\n status: number;\n\n constructor(status: number, message: string, options?: ErrorOptions) {\n super(message, options);\n this.status = status;\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { isError, sampleSize } from 'es-toolkit';\n\nimport { type HtmlSafeString } from '@prairielearn/html';\n\nexport { formatErrorStack, formatErrorStackSafe } from './format.js';\n\nconst ERROR_ID_CHARACTERS = [...'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'];\n\n/** Generates an uppercase alphanumeric correlation ID for an error. */\nexport function generateErrorId(): string {\n return sampleSize(ERROR_ID_CHARACTERS, 12).join('');\n}\n\ninterface ErrorWithData extends Error {\n data: any;\n}\n\ninterface ErrorWithInfo extends Error {\n info: string;\n}\n\ninterface ErrorWithStatus extends Error {\n status: number;\n}\n\ninterface ErrorWithStatusAndData extends ErrorWithData, ErrorWithStatus {}\n\n// TODO: rename all functions include \"error\" in the name so that they can\n// be more easily imported as named imports.\n\nexport function make(status: number, message: string): ErrorWithStatus;\nexport function make(status: number, message: string, data: any): ErrorWithStatusAndData;\nexport function make(status: number, message: string, data?: any): ErrorWithStatusAndData {\n const err = new Error(message) as ErrorWithStatusAndData;\n err.status = status;\n if (data) err.data = data;\n return err;\n}\n\nexport function makeWithData(message: string, data: any): ErrorWithData {\n const err = new Error(message) as ErrorWithData;\n err.data = data;\n return err;\n}\n\nexport function makeWithInfo(message: string, info: string): ErrorWithInfo {\n const err = new Error(message) as ErrorWithInfo;\n err.info = info;\n return err;\n}\n\nexport function addData(err: any, data: any): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n Object.assign(newErr.data, data);\n return newErr;\n}\n\nexport function newMessage(err: any, newMsg: string): ErrorWithData {\n const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;\n newErr.data = newErr.data || {};\n newErr.data._previousMessages = newErr.data._previousMessages || [];\n newErr.data._previousMessages.unshift(newErr.message);\n newErr.message = `${newMsg}: ${newErr.message}`;\n return newErr;\n}\n\n/**\n * Create a new error based an existing one, optionally adding status, message,\n * and/or data. The existing error will be set as the `cause` of the new error.\n *\n * @param err An existing error.\n * @param param\n * @param param.status Status code to set on the new error.\n * @param param.message Message to add to the new error.\n * @param param.data Data to set on the new error.\n * @returns The augmented error.\n */\nexport function augmentError(\n err: any,\n { status, message, data }: { status?: number; message?: string; data?: any },\n): ErrorWithStatusAndData {\n let newErr: ErrorWithStatusAndData;\n if (err instanceof Error) {\n const combinedMessage = message ? `${message}: ${err.message}` : err.message;\n newErr = new Error(combinedMessage, { cause: err }) as ErrorWithStatusAndData;\n } else {\n newErr = new Error(message ?? String(err)) as ErrorWithStatusAndData;\n }\n newErr.status = status ?? 500;\n newErr.data = data;\n return newErr;\n}\n\nexport interface AugmentedErrorOptions {\n status?: number;\n data?: any;\n info?: HtmlSafeString;\n cause?: unknown;\n}\n\nexport class AugmentedError extends Error {\n status: number;\n data?: any;\n info?: string;\n\n constructor(message: string, options: AugmentedErrorOptions) {\n super(message, { cause: options.cause });\n this.status = options.status ?? 500;\n this.data = options.data;\n this.info = options.info?.toString();\n }\n}\n\nexport class HttpStatusError extends Error {\n status: number;\n\n constructor(status: number, message: string, options?: ErrorOptions) {\n super(message, options);\n this.status = status;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"","sourcesContent":["import { assert, describe, it } from 'vitest';\n\nimport {\n HttpStatusError,\n addData,\n augmentError,\n make,\n makeWithData,\n makeWithInfo,\n newMessage,\n} from './index.js';\n\ndescribe('make', () => {\n it('makes an error without data', () => {\n const err = make(404, 'Not Found');\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n });\n\n it('makes an error with data', () => {\n const err = make(404, 'Not Found', { foo: 'bar' });\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithData', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithData('Not Found', { foo: 'bar' });\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithInfo', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithInfo('Not Found', 'bar');\n assert.equal(err.message, 'Not Found');\n assert.equal(err.info, 'bar');\n });\n});\n\ndescribe('addData', () => {\n it('adds data to an error', () => {\n const err = new Error('Not Found');\n const newErr = addData(err, { foo: 'bar' });\n\n assert.equal(err.message, 'Not Found');\n assert.equal((err as any).data.foo, 'bar');\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = addData('Not Found', { foo: 'bar' });\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n});\n\ndescribe('newMessage', () => {\n it('adds a new message to an error', () => {\n const err = new Error('Not Found');\n const newErr = newMessage(err, '404');\n\n assert.equal(err.message, '404: Not Found');\n assert.equal((err as any).data._previousMessages[0], 'Not Found');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = newMessage('Not Found', '404');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n});\n\ndescribe('augmentError', () => {\n it('adds status, message, and data to an error', () => {\n const err = new Error('Not Found');\n const newErr = augmentError(err, { status: 404, message: 'Missing', data: { foo: 'bar' } });\n\n assert.equal(newErr.message, 'Missing: Not Found');\n assert.equal(newErr.status, 404);\n assert.equal(newErr.data.foo, 'bar');\n assert.equal(newErr.cause, err);\n assert.equal((newErr.cause as any).message, 'Not Found');\n });\n});\n\ndescribe('HttpStatusError', () => {\n it('supports causes like native Error', () => {\n const cause = new Error('Missing record');\n const err = new HttpStatusError(404, 'Not Found', { cause });\n\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.cause, cause);\n });\n});\n"]}
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"","sourcesContent":["import { assert, describe, it } from 'vitest';\n\nimport {\n HttpStatusError,\n addData,\n augmentError,\n generateErrorId,\n make,\n makeWithData,\n makeWithInfo,\n newMessage,\n} from './index.js';\n\ndescribe('generateErrorId', () => {\n it('generates a 12-character uppercase alphanumeric ID', () => {\n assert.match(generateErrorId(), /^[0-9A-Z]{12}$/);\n });\n});\n\ndescribe('make', () => {\n it('makes an error without data', () => {\n const err = make(404, 'Not Found');\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n });\n\n it('makes an error with data', () => {\n const err = make(404, 'Not Found', { foo: 'bar' });\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithData', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithData('Not Found', { foo: 'bar' });\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithInfo', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithInfo('Not Found', 'bar');\n assert.equal(err.message, 'Not Found');\n assert.equal(err.info, 'bar');\n });\n});\n\ndescribe('addData', () => {\n it('adds data to an error', () => {\n const err = new Error('Not Found');\n const newErr = addData(err, { foo: 'bar' });\n\n assert.equal(err.message, 'Not Found');\n assert.equal((err as any).data.foo, 'bar');\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = addData('Not Found', { foo: 'bar' });\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n});\n\ndescribe('newMessage', () => {\n it('adds a new message to an error', () => {\n const err = new Error('Not Found');\n const newErr = newMessage(err, '404');\n\n assert.equal(err.message, '404: Not Found');\n assert.equal((err as any).data._previousMessages[0], 'Not Found');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = newMessage('Not Found', '404');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n});\n\ndescribe('augmentError', () => {\n it('adds status, message, and data to an error', () => {\n const err = new Error('Not Found');\n const newErr = augmentError(err, { status: 404, message: 'Missing', data: { foo: 'bar' } });\n\n assert.equal(newErr.message, 'Missing: Not Found');\n assert.equal(newErr.status, 404);\n assert.equal(newErr.data.foo, 'bar');\n assert.equal(newErr.cause, err);\n assert.equal((newErr.cause as any).message, 'Not Found');\n });\n});\n\ndescribe('HttpStatusError', () => {\n it('supports causes like native Error', () => {\n const cause = new Error('Missing record');\n const err = new HttpStatusError(404, 'Not Found', { cause });\n\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.cause, cause);\n });\n});\n"]}
@@ -1,5 +1,10 @@
1
1
  import { assert, describe, it } from 'vitest';
2
- import { HttpStatusError, addData, augmentError, make, makeWithData, makeWithInfo, newMessage, } from './index.js';
2
+ import { HttpStatusError, addData, augmentError, generateErrorId, make, makeWithData, makeWithInfo, newMessage, } from './index.js';
3
+ describe('generateErrorId', () => {
4
+ it('generates a 12-character uppercase alphanumeric ID', () => {
5
+ assert.match(generateErrorId(), /^[0-9A-Z]{12}$/);
6
+ });
7
+ });
3
8
  describe('make', () => {
4
9
  it('makes an error without data', () => {
5
10
  const err = make(404, 'Not Found');
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert, describe, it } from 'vitest';\n\nimport {\n HttpStatusError,\n addData,\n augmentError,\n make,\n makeWithData,\n makeWithInfo,\n newMessage,\n} from './index.js';\n\ndescribe('make', () => {\n it('makes an error without data', () => {\n const err = make(404, 'Not Found');\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n });\n\n it('makes an error with data', () => {\n const err = make(404, 'Not Found', { foo: 'bar' });\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithData', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithData('Not Found', { foo: 'bar' });\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithInfo', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithInfo('Not Found', 'bar');\n assert.equal(err.message, 'Not Found');\n assert.equal(err.info, 'bar');\n });\n});\n\ndescribe('addData', () => {\n it('adds data to an error', () => {\n const err = new Error('Not Found');\n const newErr = addData(err, { foo: 'bar' });\n\n assert.equal(err.message, 'Not Found');\n assert.equal((err as any).data.foo, 'bar');\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = addData('Not Found', { foo: 'bar' });\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n});\n\ndescribe('newMessage', () => {\n it('adds a new message to an error', () => {\n const err = new Error('Not Found');\n const newErr = newMessage(err, '404');\n\n assert.equal(err.message, '404: Not Found');\n assert.equal((err as any).data._previousMessages[0], 'Not Found');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = newMessage('Not Found', '404');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n});\n\ndescribe('augmentError', () => {\n it('adds status, message, and data to an error', () => {\n const err = new Error('Not Found');\n const newErr = augmentError(err, { status: 404, message: 'Missing', data: { foo: 'bar' } });\n\n assert.equal(newErr.message, 'Missing: Not Found');\n assert.equal(newErr.status, 404);\n assert.equal(newErr.data.foo, 'bar');\n assert.equal(newErr.cause, err);\n assert.equal((newErr.cause as any).message, 'Not Found');\n });\n});\n\ndescribe('HttpStatusError', () => {\n it('supports causes like native Error', () => {\n const cause = new Error('Missing record');\n const err = new HttpStatusError(404, 'Not Found', { cause });\n\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.cause, cause);\n });\n});\n"]}
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert, describe, it } from 'vitest';\n\nimport {\n HttpStatusError,\n addData,\n augmentError,\n generateErrorId,\n make,\n makeWithData,\n makeWithInfo,\n newMessage,\n} from './index.js';\n\ndescribe('generateErrorId', () => {\n it('generates a 12-character uppercase alphanumeric ID', () => {\n assert.match(generateErrorId(), /^[0-9A-Z]{12}$/);\n });\n});\n\ndescribe('make', () => {\n it('makes an error without data', () => {\n const err = make(404, 'Not Found');\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n });\n\n it('makes an error with data', () => {\n const err = make(404, 'Not Found', { foo: 'bar' });\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithData', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithData('Not Found', { foo: 'bar' });\n assert.equal(err.message, 'Not Found');\n assert.equal(err.data.foo, 'bar');\n });\n});\n\ndescribe('makeWithInfo', () => {\n it('makes an error with the expected properties', () => {\n const err = makeWithInfo('Not Found', 'bar');\n assert.equal(err.message, 'Not Found');\n assert.equal(err.info, 'bar');\n });\n});\n\ndescribe('addData', () => {\n it('adds data to an error', () => {\n const err = new Error('Not Found');\n const newErr = addData(err, { foo: 'bar' });\n\n assert.equal(err.message, 'Not Found');\n assert.equal((err as any).data.foo, 'bar');\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = addData('Not Found', { foo: 'bar' });\n assert.equal(newErr.message, 'Not Found');\n assert.equal(newErr.data.foo, 'bar');\n });\n});\n\ndescribe('newMessage', () => {\n it('adds a new message to an error', () => {\n const err = new Error('Not Found');\n const newErr = newMessage(err, '404');\n\n assert.equal(err.message, '404: Not Found');\n assert.equal((err as any).data._previousMessages[0], 'Not Found');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n\n it('coerces a non-error to a string', () => {\n const newErr = newMessage('Not Found', '404');\n assert.equal(newErr.message, '404: Not Found');\n assert.equal(newErr.data._previousMessages[0], 'Not Found');\n });\n});\n\ndescribe('augmentError', () => {\n it('adds status, message, and data to an error', () => {\n const err = new Error('Not Found');\n const newErr = augmentError(err, { status: 404, message: 'Missing', data: { foo: 'bar' } });\n\n assert.equal(newErr.message, 'Missing: Not Found');\n assert.equal(newErr.status, 404);\n assert.equal(newErr.data.foo, 'bar');\n assert.equal(newErr.cause, err);\n assert.equal((newErr.cause as any).message, 'Not Found');\n });\n});\n\ndescribe('HttpStatusError', () => {\n it('supports causes like native Error', () => {\n const cause = new Error('Missing record');\n const err = new HttpStatusError(404, 'Not Found', { cause });\n\n assert.equal(err.status, 404);\n assert.equal(err.message, 'Not Found');\n assert.equal(err.cause, cause);\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prairielearn/error",
3
- "version": "3.0.12",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
package/src/index.test.ts CHANGED
@@ -4,12 +4,19 @@ import {
4
4
  HttpStatusError,
5
5
  addData,
6
6
  augmentError,
7
+ generateErrorId,
7
8
  make,
8
9
  makeWithData,
9
10
  makeWithInfo,
10
11
  newMessage,
11
12
  } from './index.js';
12
13
 
14
+ describe('generateErrorId', () => {
15
+ it('generates a 12-character uppercase alphanumeric ID', () => {
16
+ assert.match(generateErrorId(), /^[0-9A-Z]{12}$/);
17
+ });
18
+ });
19
+
13
20
  describe('make', () => {
14
21
  it('makes an error without data', () => {
15
22
  const err = make(404, 'Not Found');
package/src/index.ts CHANGED
@@ -1,9 +1,16 @@
1
- import { isError } from 'es-toolkit';
1
+ import { isError, sampleSize } from 'es-toolkit';
2
2
 
3
3
  import { type HtmlSafeString } from '@prairielearn/html';
4
4
 
5
5
  export { formatErrorStack, formatErrorStackSafe } from './format.js';
6
6
 
7
+ const ERROR_ID_CHARACTERS = [...'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'];
8
+
9
+ /** Generates an uppercase alphanumeric correlation ID for an error. */
10
+ export function generateErrorId(): string {
11
+ return sampleSize(ERROR_ID_CHARACTERS, 12).join('');
12
+ }
13
+
7
14
  interface ErrorWithData extends Error {
8
15
  data: any;
9
16
  }
@@ -53,7 +60,7 @@ export function newMessage(err: any, newMsg: string): ErrorWithData {
53
60
  const newErr = (isError(err) ? err : new Error(String(err))) as ErrorWithData;
54
61
  newErr.data = newErr.data || {};
55
62
  newErr.data._previousMessages = newErr.data._previousMessages || [];
56
- newErr.data._previousMessages.splice(0, 0, newErr.message);
63
+ newErr.data._previousMessages.unshift(newErr.message);
57
64
  newErr.message = `${newMsg}: ${newErr.message}`;
58
65
  return newErr;
59
66
  }