@warp-drive-mirror/utilities 5.10.0-alpha.2 → 5.10.0-alpha.21
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/README.md +92 -7
- package/cjs-dist/string.cjs +23 -8
- package/cjs-dist/string.cjs.map +1 -1
- package/dist/-private.d.ts +0 -2
- package/dist/-private.d.ts.map +1 -1
- package/dist/-private.js +1 -1
- package/dist/active-record.d.ts +21 -6
- package/dist/active-record.d.ts.map +1 -1
- package/dist/active-record.js +17 -8
- package/dist/active-record.js.map +1 -1
- package/dist/{builder-utils-BkXwUT0x.js → builder-utils-bjXbHa-m.js} +2 -3
- package/dist/{builder-utils-BkXwUT0x.js.map → builder-utils-bjXbHa-m.js.map} +1 -1
- package/dist/derivations.d.ts +3 -2
- package/dist/derivations.d.ts.map +1 -1
- package/dist/derivations.js +7 -2
- package/dist/derivations.js.map +1 -1
- package/dist/handlers.d.ts +26 -9
- package/dist/handlers.d.ts.map +1 -1
- package/dist/handlers.js +29 -9
- package/dist/handlers.js.map +1 -1
- package/dist/{index-BeMAuqgZ.d.ts → index-Dv5kZpGe.d.ts} +47 -3
- package/dist/index-Dv5kZpGe.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +54 -8
- package/dist/index.js.map +1 -1
- package/dist/{inflections-DzBsjJqj.js → inflections-CWdUh2K_.js} +2 -3
- package/dist/{inflections-DzBsjJqj.js.map → inflections-CWdUh2K_.js.map} +1 -1
- package/dist/json-api.d.ts +51 -11
- package/dist/json-api.d.ts.map +1 -1
- package/dist/json-api.js +43 -12
- package/dist/json-api.js.map +1 -1
- package/dist/rest.d.ts +20 -6
- package/dist/rest.d.ts.map +1 -1
- package/dist/rest.js +17 -8
- package/dist/rest.js.map +1 -1
- package/dist/{string-CWmAluxh.js → string-DFhbMVCj.js} +23 -6
- package/dist/string-DFhbMVCj.js.map +1 -0
- package/dist/string.d.ts +19 -4
- package/dist/string.d.ts.map +1 -1
- package/dist/string.js +1 -1
- package/dist/unpkg/dev/-private.js +1 -1
- package/dist/unpkg/dev/active-record.js +17 -8
- package/dist/unpkg/dev/active-record.js.map +1 -1
- package/dist/unpkg/dev/{builder-utils-BkXwUT0x.js → builder-utils-bjXbHa-m.js} +2 -3
- package/dist/unpkg/dev/{builder-utils-BkXwUT0x.js.map → builder-utils-bjXbHa-m.js.map} +1 -1
- package/dist/unpkg/dev/derivations.js +7 -2
- package/dist/unpkg/dev/derivations.js.map +1 -1
- package/dist/unpkg/dev/handlers.js +29 -9
- package/dist/unpkg/dev/handlers.js.map +1 -1
- package/dist/unpkg/dev/index.js +54 -8
- package/dist/unpkg/dev/index.js.map +1 -1
- package/dist/unpkg/dev/{inflections-DzBsjJqj.js → inflections-CWdUh2K_.js} +2 -3
- package/dist/unpkg/dev/{inflections-DzBsjJqj.js.map → inflections-CWdUh2K_.js.map} +1 -1
- package/dist/unpkg/dev/json-api.js +43 -12
- package/dist/unpkg/dev/json-api.js.map +1 -1
- package/dist/unpkg/dev/rest.js +17 -8
- package/dist/unpkg/dev/rest.js.map +1 -1
- package/dist/unpkg/dev/{string-j4ur-TDs.js → string-D9jrClhQ.js} +23 -6
- package/dist/unpkg/dev/string-D9jrClhQ.js.map +1 -0
- package/dist/unpkg/dev/string.js +1 -1
- package/dist/unpkg/dev-deprecated/-private.js +1 -1
- package/dist/unpkg/dev-deprecated/active-record.js +17 -8
- package/dist/unpkg/dev-deprecated/active-record.js.map +1 -1
- package/dist/unpkg/dev-deprecated/{builder-utils-BkXwUT0x.js → builder-utils-bjXbHa-m.js} +2 -3
- package/dist/unpkg/dev-deprecated/{builder-utils-BkXwUT0x.js.map → builder-utils-bjXbHa-m.js.map} +1 -1
- package/dist/unpkg/dev-deprecated/derivations.js +7 -2
- package/dist/unpkg/dev-deprecated/derivations.js.map +1 -1
- package/dist/unpkg/dev-deprecated/handlers.js +29 -9
- package/dist/unpkg/dev-deprecated/handlers.js.map +1 -1
- package/dist/unpkg/dev-deprecated/index.js +54 -8
- package/dist/unpkg/dev-deprecated/index.js.map +1 -1
- package/dist/unpkg/dev-deprecated/{inflections-DzBsjJqj.js → inflections-CWdUh2K_.js} +2 -3
- package/dist/unpkg/dev-deprecated/{inflections-DzBsjJqj.js.map → inflections-CWdUh2K_.js.map} +1 -1
- package/dist/unpkg/dev-deprecated/json-api.js +43 -12
- package/dist/unpkg/dev-deprecated/json-api.js.map +1 -1
- package/dist/unpkg/dev-deprecated/rest.js +17 -8
- package/dist/unpkg/dev-deprecated/rest.js.map +1 -1
- package/dist/unpkg/dev-deprecated/{string-j4ur-TDs.js → string-D9jrClhQ.js} +23 -6
- package/dist/unpkg/dev-deprecated/string-D9jrClhQ.js.map +1 -0
- package/dist/unpkg/dev-deprecated/string.js +1 -1
- package/dist/unpkg/prod/-private.js +1 -1
- package/dist/unpkg/prod/active-record.js +17 -8
- package/dist/unpkg/prod/active-record.js.map +1 -1
- package/dist/unpkg/prod/builder-utils-bjXbHa-m.js +15 -0
- package/dist/unpkg/prod/{builder-utils-BkXwUT0x.js.map → builder-utils-bjXbHa-m.js.map} +1 -1
- package/dist/unpkg/prod/derivations.js +7 -2
- package/dist/unpkg/prod/derivations.js.map +1 -1
- package/dist/unpkg/prod/handlers.js +29 -9
- package/dist/unpkg/prod/handlers.js.map +1 -1
- package/dist/unpkg/prod/index.js +54 -8
- package/dist/unpkg/prod/index.js.map +1 -1
- package/dist/unpkg/prod/inflections-CWdUh2K_.js +79 -0
- package/dist/unpkg/prod/{inflections-DzBsjJqj.js.map → inflections-CWdUh2K_.js.map} +1 -1
- package/dist/unpkg/prod/json-api.js +43 -12
- package/dist/unpkg/prod/json-api.js.map +1 -1
- package/dist/unpkg/prod/rest.js +17 -8
- package/dist/unpkg/prod/rest.js.map +1 -1
- package/dist/unpkg/prod/string-Du9nRZEd.js +300 -0
- package/dist/unpkg/prod/string-Du9nRZEd.js.map +1 -0
- package/dist/unpkg/prod/string.js +1 -1
- package/dist/unpkg/prod-deprecated/-private.js +1 -1
- package/dist/unpkg/prod-deprecated/active-record.js +17 -8
- package/dist/unpkg/prod-deprecated/active-record.js.map +1 -1
- package/dist/unpkg/prod-deprecated/{builder-utils-BkXwUT0x.js → builder-utils-bjXbHa-m.js} +2 -3
- package/dist/unpkg/prod-deprecated/{builder-utils-BkXwUT0x.js.map → builder-utils-bjXbHa-m.js.map} +1 -1
- package/dist/unpkg/prod-deprecated/derivations.js +7 -2
- package/dist/unpkg/prod-deprecated/derivations.js.map +1 -1
- package/dist/unpkg/prod-deprecated/handlers.js +29 -9
- package/dist/unpkg/prod-deprecated/handlers.js.map +1 -1
- package/dist/unpkg/prod-deprecated/index.js +54 -8
- package/dist/unpkg/prod-deprecated/index.js.map +1 -1
- package/dist/unpkg/prod-deprecated/{inflections-DzBsjJqj.js → inflections-CWdUh2K_.js} +2 -3
- package/dist/unpkg/prod-deprecated/{inflections-DzBsjJqj.js.map → inflections-CWdUh2K_.js.map} +1 -1
- package/dist/unpkg/prod-deprecated/json-api.js +43 -12
- package/dist/unpkg/prod-deprecated/json-api.js.map +1 -1
- package/dist/unpkg/prod-deprecated/rest.js +17 -8
- package/dist/unpkg/prod-deprecated/rest.js.map +1 -1
- package/dist/unpkg/prod-deprecated/{string-T1ahjLdj.js → string-Du9nRZEd.js} +23 -6
- package/dist/unpkg/prod-deprecated/string-Du9nRZEd.js.map +1 -0
- package/dist/unpkg/prod-deprecated/string.js +1 -1
- package/logos/README.md +1 -1
- package/package.json +9 -9
- package/dist/index-BeMAuqgZ.d.ts.map +0 -1
- package/dist/string-CWmAluxh.js.map +0 -1
- package/dist/unpkg/dev/string-j4ur-TDs.js.map +0 -1
- package/dist/unpkg/dev-deprecated/string-j4ur-TDs.js.map +0 -1
- package/dist/unpkg/prod/builder-utils-BkXwUT0x.js +0 -16
- package/dist/unpkg/prod/inflections-DzBsjJqj.js +0 -80
- package/dist/unpkg/prod/string-T1ahjLdj.js +0 -283
- package/dist/unpkg/prod/string-T1ahjLdj.js.map +0 -1
- package/dist/unpkg/prod-deprecated/string-T1ahjLdj.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.js","names":["isCompressibleMethod","method","SupportsRequestStreams","duplexAccessed","hasContentType","Request","body","ReadableStream","duplex","headers","has","DEFAULT_CONSTRAINTS","Blob","ArrayBuffer","TypedArray","DataView","String","Object","getPrototypeOf","Uint8Array","AutoCompress","constructor","options","opts","format","constraints","assign","allowStreaming","forceStreaming","request","next","shouldCompress","compress","canCompress","length","size","byteLength","response","Response","stream","pipeThrough","CompressionStream","Headers","set","encodingForFormat","req","resp","blob","then","type","test","Error","Gate","constructor","handler","checkFn","request","context","next","FastBoot","globalThis","addEventListener","sessionStorage","setItem","getTabId","crypto","randomUUID","tabId","getItem","tabClosed","newTabId","TAB_ID","TAB_ASSIGNED","Math","floor","Date","now","addTraceHeader","headers","set","MAX_URL_LENGTH","assertInvalidUrlLength","url","test","Error","length"],"sources":["../../../src/-private/handlers/auto-compress.ts","../../../src/-private/handlers/gated.ts","../../../src/-private/handlers/meta-doc.ts","../../../src/-private/handlers/utils.ts"],"sourcesContent":["import { assert } from '@warp-drive-mirror/core/build-config/macros';\nimport type { Future, Handler, NextFn } from '@warp-drive-mirror/core/request';\nimport type { HTTPMethod, RequestContext } from '@warp-drive-mirror/core/types/request';\n\nfunction isCompressibleMethod(method?: HTTPMethod): boolean {\n return method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE';\n}\n\n/**\n * Whether the browser supports `ReadableStream` as a request body\n * in a `POST` request.\n *\n * @group Constants\n */\nexport const SupportsRequestStreams: boolean = (() => {\n let duplexAccessed = false;\n\n const hasContentType = new Request('', {\n body: new ReadableStream(),\n method: 'POST',\n // @ts-expect-error untyped\n get duplex() {\n duplexAccessed = true;\n return 'half';\n },\n }).headers.has('Content-Type');\n\n return duplexAccessed && !hasContentType;\n})();\n\ninterface Constraints {\n /**\n * The minimum size at which to compress blobs\n *\n * @default 1000\n */\n Blob?: number;\n /**\n * The minimum size at which to compress array buffers\n *\n * @default 1000\n */\n ArrayBuffer?: number;\n /**\n * The minimum size at which to compress typed arrays\n *\n * @default 1000\n */\n TypedArray?: number;\n /**\n * The minimum size at which to compress data views\n *\n * @default 1000\n */\n DataView?: number;\n /**\n * The minimum size at which to compress strings\n *\n * @default 1000\n */\n String?: number;\n}\n\n/**\n * Options for configuring the AutoCompress handler.\n *\n */\ninterface CompressionOptions {\n /**\n * The compression format to use. Must be a valid\n * compression format supported by [CompressionStream](https://developer.mozilla.org/en-US/docs/Web/API/CompressionStream)\n *\n * The default is `gzip`.\n *\n */\n format?: CompressionFormat;\n\n /**\n * Some browsers support `ReadableStream` as a request body. This option\n * enables passing the compression stream as the request body instead of\n * the final compressed body when the browser supports doing so.\n *\n * This comes with several caveats:\n *\n * - the request will be put into `duplex: 'half'` mode. This should be\n * transparent to you, but it is worth noting.\n * - the request mode cannot be `no-cors` as requests with a `ReadableStream`\n * have no content length and thus are a new form of request that triggers\n * cors requirements and a preflight request.\n * - http/1.x is not supported.\n *\n * For additional reading about the restrictions of using `ReadableStream`\n * as a request body, see the [Chromium Documentation](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests#restrictions)\n *\n * Streaming can be enabled per-request in browsers which support it by\n * setting `request.options.allowStreaming` to `true`.\n *\n * Streaming can be forced even when the browser does not support it by setting\n * `request.options.forceStreaming` to `true`. This is useful if later handlers\n * in the chain can handle the request body as a stream.\n *\n * @default false\n */\n allowStreaming?: boolean;\n\n /**\n * If `true`, the request will be forced into streaming mode even\n * if the browser does not support it. This is useful if later handlers\n * in the chain can handle the request body as a stream.\n *\n * @default false\n */\n forceStreaming?: boolean;\n\n /**\n * The constraints for the request body. This is used to determine\n * whether to compress the request body or not.\n *\n * The defaults are:\n *\n * ```ts\n * {\n * Blob: 1000, // blob.size\n * ArrayBuffer: 1000, // buffer.byteLength\n * TypedArray: 1000, // array.byteLength\n * DataView: 1000, // view.byteLength\n * String: 1000, // string.length\n * }\n * ```\n *\n * The following body types are never compressed unless explicitly\n * configured by the request:\n * - `FormData`\n * - `URLSearchParams`\n * - `ReadableStream`\n *\n * A request.options.compress value of `false` will disable\n * compression for a request body of any type. While a value of\n * `true` will enable compression for the request.\n *\n * An undefined value will use the default, a value of `0` will\n * enable compression for all values, and a value of `-1` will\n * disable compression.\n *\n */\n constraints?: Constraints;\n}\n\nconst DEFAULT_CONSTRAINTS = {\n Blob: 1000,\n ArrayBuffer: 1000,\n TypedArray: 1000,\n DataView: 1000,\n String: 1000,\n};\nconst TypedArray = Object.getPrototypeOf(Uint8Array) as typeof Uint8Array;\n\n/**\n * A request handler that automatically compresses the request body\n * if the request body is a string, array buffer, blob, or form data.\n *\n * This uses the [CompressionStream API](https://developer.mozilla.org/en-US/docs/Web/API/CompressionStream)\n *\n * The compression format as well as the kinds of data to compress can be\n * configured using the `format` and `constraints` options.\n *\n * ```diff\n * +import { AutoCompress } from '@ember-data-mirror/request-utils/handlers';\n * import Fetch from '@ember-data-mirror/request/fetch';\n * import RequestManager from '@ember-data-mirror/request';\n * import Store from '@ember-data-mirror/store';\n *\n * class AppStore extends Store {\n * requestManager = new RequestManager()\n * .use([\n * + new AutoCompress(),\n * Fetch\n * ]);\n * }\n * ```\n *\n * @group Handlers\n * @public\n * @since 5.5.0\n */\nexport class AutoCompress implements Handler {\n /**\n * The resolved options this handler was configured with, with all\n * defaults (including `constraints` defaults) applied.\n */\n declare options: Required<CompressionOptions> & {\n /**\n * The resolved size constraints used to decide whether to compress a given request body.\n */\n constraints: Required<Constraints>;\n };\n\n constructor(options: CompressionOptions = {}) {\n const opts = {\n format: options.format ?? 'gzip',\n constraints: Object.assign({}, DEFAULT_CONSTRAINTS, options.constraints),\n allowStreaming: options.allowStreaming ?? false,\n forceStreaming: options.forceStreaming ?? false,\n };\n this.options = opts;\n }\n\n request<T>({ request }: RequestContext, next: NextFn<T>): Promise<T> | Future<T> {\n const { constraints } = this.options;\n const { body } = request;\n\n const shouldCompress =\n !!body &&\n isCompressibleMethod(request.method) &&\n request.options?.compress !== false &&\n // prettier-ignore\n (request.options?.compress ? true\n : typeof body === 'string' || body instanceof String ? canCompress('String', constraints, body.length)\n : body instanceof Blob ? canCompress('Blob', constraints, body.size)\n : body instanceof ArrayBuffer ? canCompress('ArrayBuffer', constraints, body.byteLength)\n : body instanceof DataView ? canCompress('DataView', constraints, body.byteLength)\n : body instanceof TypedArray ? canCompress('TypedArray', constraints, body.byteLength)\n : false);\n\n if (!shouldCompress) return next(request);\n\n // A convenient way to convert all of the supported body types to a readable\n // stream is to use a `Response` object body\n const response = new Response(request.body);\n const stream = response.body?.pipeThrough(new CompressionStream(this.options.format));\n const headers = new Headers(request.headers);\n headers.set('Content-Encoding', encodingForFormat(this.options.format));\n\n //\n // For browsers that support it, `fetch` can receive a `ReadableStream` as\n // the body, so all we need to do is to create a new `ReadableStream` and\n // compress it on the fly\n //\n const forceStreaming = request.options?.forceStreaming ?? this.options.forceStreaming;\n const allowStreaming = request.options?.allowStreaming ?? this.options.allowStreaming;\n if (forceStreaming || (SupportsRequestStreams && allowStreaming)) {\n const req = Object.assign({}, request, {\n body: stream,\n headers,\n });\n if (SupportsRequestStreams) {\n // @ts-expect-error untyped\n req.duplex = 'half';\n }\n\n return next(req);\n\n //\n // For non-chromium browsers, we have to \"pull\" the stream to get the final\n // bytes and supply the final byte array as the new request body.\n //\n }\n\n // we need to pull the stream to get the final bytes\n const resp = new Response(stream);\n return resp.blob().then((blob) => {\n const req = Object.assign({}, request, {\n body: blob,\n headers,\n });\n return next(req);\n }) as Promise<T>;\n }\n}\n\nfunction canCompress(type: keyof Constraints, constraints: Required<Constraints>, size: number): boolean {\n // if we have a value of 0, we can compress anything\n if (constraints[type] === 0) return true;\n if (constraints[type] === -1) return false;\n return size >= constraints[type];\n}\n\nfunction encodingForFormat(format: CompressionFormat): string {\n switch (format) {\n case 'gzip':\n case 'deflate':\n case 'deflate-raw':\n return format;\n default:\n assert(`Unsupported compression format: ${format as unknown as string}`);\n // @ts-expect-error - unreachable code is reachable in production\n return format;\n }\n}\n","import type { Future, Handler, NextFn } from '@warp-drive-mirror/core/request';\nimport type { RequestContext, StructuredDataDocument } from '@warp-drive-mirror/core/types/request';\n\n/**\n * If CheckFn returns true, the wrapped handler will be used.\n * If CheckFn returns false, the wrapped handler will be skipped.\n */\ntype CheckFn = (context: RequestContext) => boolean;\n\n/**\n *\n * @group Handlers\n * @public\n */\nexport class Gate implements Handler {\n /**\n * The wrapped handler to invoke when {@link Gate.checkFn | checkFn} returns `true`.\n */\n declare handler: Handler;\n /**\n * The predicate used to decide whether {@link Gate.handler | handler} should run for a given request.\n */\n declare checkFn: CheckFn;\n\n constructor(handler: Handler, checkFn: CheckFn) {\n this.handler = handler;\n this.checkFn = checkFn;\n }\n\n request<T = unknown>(context: RequestContext, next: NextFn<T>): Promise<T | StructuredDataDocument<T>> | Future<T> {\n if (this.checkFn(context)) {\n return this.handler.request(context, next);\n }\n return next(context.request);\n }\n}\n","import type { StoreRequestContext } from '@warp-drive-mirror/core';\nimport type { Handler, NextFn } from '@warp-drive-mirror/core/request';\nimport type { StructuredDataDocument } from '@warp-drive-mirror/core/types/request';\n\n/**\n * MetaDocHandler processes requests that are marked as meta requests.\n *\n * It treats the response body as \"entirely meta\" transforming\n *\n * ```ts\n * {\n * some: \"key\",\n * another: \"thing\"\n * }\n * ```\n *\n * into\n *\n * ```ts\n * {\n * \t meta: {\n * some: \"key\",\n * another: \"thing\"\n * }\n * }\n * ```\n *\n * To activate this handler, a request should specify\n *\n * ```ts\n * options.isMetaRequest = true\n * ```\n *\n * For instance\n *\n * ```ts\n * store.request({\n * url: '/example',\n * options: {\n * isMetaRequest: true\n * }\n * });\n * ```\n *\n * Errors are not processed by this handler, so if the request fails and the error response\n * is not in {json:api} format additional processing may be needed.\n *\n * @group Handlers\n */\nexport const MetaDocHandler: Handler = {\n request<T>(context: StoreRequestContext, next: NextFn<T>): Promise<T | StructuredDataDocument<T> | Awaited<T>> {\n if (!context.request.options?.isMetaRequest) {\n return next(context.request);\n }\n\n return next(context.request).then((response) => {\n return processResponse<T>(response);\n });\n },\n};\n\nfunction processResponse<T>(response: StructuredDataDocument<T>): T {\n return {\n meta: response.content,\n } as T;\n}\n","import { assert } from '@warp-drive-mirror/core/build-config/macros';\n\nif (typeof FastBoot === 'undefined') {\n globalThis.addEventListener('beforeunload', function () {\n sessionStorage.setItem('tab-closed', 'true');\n });\n}\n\nfunction getTabId() {\n if (typeof sessionStorage === 'undefined') {\n return crypto.randomUUID();\n }\n\n const tabId = sessionStorage.getItem('tab-id');\n if (tabId) {\n const tabClosed = sessionStorage.getItem('tab-closed');\n if (tabClosed === 'true') {\n return tabId;\n }\n\n // fall through to generate a new tab id\n }\n\n const newTabId = crypto.randomUUID();\n sessionStorage.setItem('tab-id', newTabId);\n return newTabId;\n}\n\n/**\n * A unique identifier for the current browser tab\n * useful for observability/tracing and deduping\n * across multiple tabs.\n *\n * @group Constants\n */\nexport const TAB_ID: string = getTabId();\n/**\n * The epoch seconds at which the tab id was generated\n *\n * @group Constants\n */\nexport const TAB_ASSIGNED: number = Math.floor(Date.now() / 1000);\n\n/**\n * Adds the `X-Amzn-Trace-Id` header to support observability\n * tooling around request routing.\n *\n * This makes use of the {@link TAB_ID} and {@link TAB_ASSIGNED}\n * to enable tracking the browser tab of origin across multiple requests.\n *\n * Follows the template: `Root=1-${now}-${uuidv4};TabId=1-${epochSeconds}-${tab-uuid}`\n *\n * @group Utility Functions\n */\nexport function addTraceHeader(headers: Headers): Headers {\n const now = Math.floor(Date.now() / 1000);\n headers.set('X-Amzn-Trace-Id', `Root=1-${now}-${crypto.randomUUID()};TabId=1-${TAB_ASSIGNED}-${TAB_ID}`);\n\n return headers;\n}\n\n/**\n * Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html\n * As of 2024-12-05 the maximum URL length is 8192 bytes.\n *\n * @group Constants\n */\nexport const MAX_URL_LENGTH = 8192;\n\n/**\n * This assertion takes a URL and throws an error if the URL is longer than the maximum URL length.\n *\n * See also {@link MAX_URL_LENGTH}\n *\n * @group Utility Functions\n */\nexport function assertInvalidUrlLength(url: string | undefined): void {\n assert(\n `URL length ${url?.length} exceeds the maximum URL length of ${MAX_URL_LENGTH} bytes.\\n\\nConsider converting this request query a \\`/query\\` endpoint instead of a GET, or upgrade the current endpoint to be able to receive a POST request directly (ideally specifying the header HTTP-Method-Override: QUERY)\\n\\nThe Invalid URL is:\\n\\n${url}`,\n !url || url.length <= MAX_URL_LENGTH\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handlers.js","names":["isCompressibleMethod","method","SupportsRequestStreams","duplexAccessed","hasContentType","Request","body","ReadableStream","duplex","headers","has","DEFAULT_CONSTRAINTS","Blob","ArrayBuffer","TypedArray","DataView","String","Object","getPrototypeOf","Uint8Array","AutoCompress","constructor","options","opts","format","constraints","assign","allowStreaming","forceStreaming","request","next","shouldCompress","compress","canCompress","length","size","byteLength","response","Response","stream","pipeThrough","CompressionStream","Headers","set","encodingForFormat","req","resp","blob","then","type","test","Error","Gate","constructor","handler","checkFn","request","context","next","FastBoot","globalThis","addEventListener","sessionStorage","setItem","getTabId","crypto","randomUUID","tabId","getItem","tabClosed","newTabId","TAB_ID","TAB_ASSIGNED","Math","floor","Date","now","addTraceHeader","headers","set","MAX_URL_LENGTH","assertInvalidUrlLength","url","test","Error","length"],"sources":["../../../src/-private/handlers/auto-compress.ts","../../../src/-private/handlers/gated.ts","../../../src/-private/handlers/meta-doc.ts","../../../src/-private/handlers/utils.ts"],"sourcesContent":["import { assert } from '@warp-drive-mirror/core/build-config/macros';\nimport type { Future, Handler, NextFn } from '@warp-drive-mirror/core/request';\nimport type { HTTPMethod, RequestContext } from '@warp-drive-mirror/core/types/request';\n\nfunction isCompressibleMethod(method?: HTTPMethod): boolean {\n return method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE';\n}\n\n/**\n * Whether the browser supports `ReadableStream` as a request body\n * in a `POST` request.\n *\n * @summary Whether the current browser can send a `ReadableStream` as a `fetch` request body, detected once at\n * module load.\n * @group Constants\n */\nexport const SupportsRequestStreams: boolean = (() => {\n let duplexAccessed = false;\n\n const hasContentType = new Request('', {\n body: new ReadableStream(),\n method: 'POST',\n // @ts-expect-error untyped\n get duplex() {\n duplexAccessed = true;\n return 'half';\n },\n }).headers.has('Content-Type');\n\n return duplexAccessed && !hasContentType;\n})();\n\n/**\n * The minimum body size, per body type, at which {@link AutoCompress} compresses a request\n * body. Passed as {@link CompressionOptions.constraints}.\n *\n * @summary Minimum request body sizes, per body type, at which `AutoCompress` compresses a body.\n * @public\n */\nexport interface Constraints {\n /**\n * The minimum size at which to compress blobs\n *\n * @default 1000\n */\n Blob?: number;\n /**\n * The minimum size at which to compress array buffers\n *\n * @default 1000\n */\n ArrayBuffer?: number;\n /**\n * The minimum size at which to compress typed arrays\n *\n * @default 1000\n */\n TypedArray?: number;\n /**\n * The minimum size at which to compress data views\n *\n * @default 1000\n */\n DataView?: number;\n /**\n * The minimum size at which to compress strings\n *\n * @default 1000\n */\n String?: number;\n}\n\n/**\n * Options for configuring the {@link AutoCompress} handler.\n *\n * @summary Options for `new AutoCompress()`: the compression format, size constraints and whether to stream the body.\n * @public\n */\nexport interface CompressionOptions {\n /**\n * The compression format to use. Must be a valid\n * compression format supported by [CompressionStream](https://developer.mozilla.org/en-US/docs/Web/API/CompressionStream)\n *\n * The default is `gzip`.\n *\n */\n format?: CompressionFormat;\n\n /**\n * Some browsers support `ReadableStream` as a request body. This option\n * enables passing the compression stream as the request body instead of\n * the final compressed body when the browser supports doing so.\n *\n * This comes with several caveats:\n *\n * - the request will be put into `duplex: 'half'` mode. This should be\n * transparent to you, but it is worth noting.\n * - the request mode cannot be `no-cors` as requests with a `ReadableStream`\n * have no content length and thus are a new form of request that triggers\n * cors requirements and a preflight request.\n * - http/1.x is not supported.\n *\n * For additional reading about the restrictions of using `ReadableStream`\n * as a request body, see the [Chromium Documentation](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests#restrictions)\n *\n * Streaming can be enabled per-request in browsers which support it by\n * setting `request.options.allowStreaming` to `true`.\n *\n * Streaming can be forced even when the browser does not support it by setting\n * `request.options.forceStreaming` to `true`. This is useful if later handlers\n * in the chain can handle the request body as a stream.\n *\n * @default false\n */\n allowStreaming?: boolean;\n\n /**\n * If `true`, the request will be forced into streaming mode even\n * if the browser does not support it. This is useful if later handlers\n * in the chain can handle the request body as a stream.\n *\n * @default false\n */\n forceStreaming?: boolean;\n\n /**\n * The constraints for the request body. This is used to determine\n * whether to compress the request body or not.\n *\n * The defaults are:\n *\n * ```ts\n * {\n * Blob: 1000, // blob.size\n * ArrayBuffer: 1000, // buffer.byteLength\n * TypedArray: 1000, // array.byteLength\n * DataView: 1000, // view.byteLength\n * String: 1000, // string.length\n * }\n * ```\n *\n * The following body types are never compressed unless explicitly\n * configured by the request:\n * - `FormData`\n * - `URLSearchParams`\n * - `ReadableStream`\n *\n * A request.options.compress value of `false` will disable\n * compression for a request body of any type. While a value of\n * `true` will enable compression for the request.\n *\n * An undefined value will use the default, a value of `0` will\n * enable compression for all values, and a value of `-1` will\n * disable compression.\n *\n */\n constraints?: Constraints;\n}\n\nconst DEFAULT_CONSTRAINTS = {\n Blob: 1000,\n ArrayBuffer: 1000,\n TypedArray: 1000,\n DataView: 1000,\n String: 1000,\n};\nconst TypedArray = Object.getPrototypeOf(Uint8Array) as typeof Uint8Array;\n\n/**\n * A request handler that automatically compresses the request body\n * if the request body is a string, array buffer, blob, or form data.\n *\n * This uses the [CompressionStream API](https://developer.mozilla.org/en-US/docs/Web/API/CompressionStream)\n *\n * The compression format as well as the kinds of data to compress can be\n * configured using the `format` and `constraints` options.\n *\n * ```diff\n * +import { AutoCompress } from '@ember-data-mirror/request-utils/handlers';\n * import Fetch from '@ember-data-mirror/request/fetch';\n * import RequestManager from '@ember-data-mirror/request';\n * import Store from '@ember-data-mirror/store';\n *\n * class AppStore extends Store {\n * requestManager = new RequestManager()\n * .use([\n * + new AutoCompress(),\n * Fetch\n * ]);\n * }\n * ```\n *\n * @summary Request handler that compresses large `POST`, `PUT`, `PATCH`, and `DELETE` bodies with `CompressionStream`,\n * optionally streaming them.\n * @group Handlers\n * @public\n * @since 5.5.0\n */\nexport class AutoCompress implements Handler {\n /**\n * The resolved options this handler was configured with, with all\n * defaults (including `constraints` defaults) applied.\n */\n declare options: Required<CompressionOptions> & {\n /**\n * The resolved size constraints used to decide whether to compress a given request body.\n */\n constraints: Required<Constraints>;\n };\n\n constructor(options: CompressionOptions = {}) {\n const opts = {\n format: options.format ?? 'gzip',\n constraints: Object.assign({}, DEFAULT_CONSTRAINTS, options.constraints),\n allowStreaming: options.allowStreaming ?? false,\n forceStreaming: options.forceStreaming ?? false,\n };\n this.options = opts;\n }\n\n request<T>({ request }: RequestContext, next: NextFn<T>): Promise<T> | Future<T> {\n const { constraints } = this.options;\n const { body } = request;\n\n const shouldCompress =\n !!body &&\n isCompressibleMethod(request.method) &&\n request.options?.compress !== false &&\n // prettier-ignore\n (request.options?.compress ? true\n : typeof body === 'string' || body instanceof String ? canCompress('String', constraints, body.length)\n : body instanceof Blob ? canCompress('Blob', constraints, body.size)\n : body instanceof ArrayBuffer ? canCompress('ArrayBuffer', constraints, body.byteLength)\n : body instanceof DataView ? canCompress('DataView', constraints, body.byteLength)\n : body instanceof TypedArray ? canCompress('TypedArray', constraints, body.byteLength)\n : false);\n\n if (!shouldCompress) return next(request);\n\n // A convenient way to convert all of the supported body types to a readable\n // stream is to use a `Response` object body\n const response = new Response(request.body);\n const stream = response.body?.pipeThrough(new CompressionStream(this.options.format));\n const headers = new Headers(request.headers);\n headers.set('Content-Encoding', encodingForFormat(this.options.format));\n\n //\n // For browsers that support it, `fetch` can receive a `ReadableStream` as\n // the body, so all we need to do is to create a new `ReadableStream` and\n // compress it on the fly\n //\n const forceStreaming = request.options?.forceStreaming ?? this.options.forceStreaming;\n const allowStreaming = request.options?.allowStreaming ?? this.options.allowStreaming;\n if (forceStreaming || (SupportsRequestStreams && allowStreaming)) {\n const req = Object.assign({}, request, {\n body: stream,\n headers,\n });\n if (SupportsRequestStreams) {\n // @ts-expect-error untyped\n req.duplex = 'half';\n }\n\n return next(req);\n\n //\n // For non-chromium browsers, we have to \"pull\" the stream to get the final\n // bytes and supply the final byte array as the new request body.\n //\n }\n\n // we need to pull the stream to get the final bytes\n const resp = new Response(stream);\n return resp.blob().then((blob) => {\n const req = Object.assign({}, request, {\n body: blob,\n headers,\n });\n return next(req);\n }) as Promise<T>;\n }\n}\n\nfunction canCompress(type: keyof Constraints, constraints: Required<Constraints>, size: number): boolean {\n // if we have a value of 0, we can compress anything\n if (constraints[type] === 0) return true;\n if (constraints[type] === -1) return false;\n return size >= constraints[type];\n}\n\nfunction encodingForFormat(format: CompressionFormat): string {\n switch (format) {\n case 'gzip':\n case 'deflate':\n case 'deflate-raw':\n return format;\n default:\n assert(`Unsupported compression format: ${format as unknown as string}`);\n // @ts-expect-error - unreachable code is reachable in production\n return format;\n }\n}\n","import type { Future, Handler, NextFn } from '@warp-drive-mirror/core/request';\nimport type { RequestContext, StructuredDataDocument } from '@warp-drive-mirror/core/types/request';\n\n/**\n * If CheckFn returns true, the wrapped handler will be used.\n * If CheckFn returns false, the wrapped handler will be skipped.\n */\ntype CheckFn = (context: RequestContext) => boolean;\n\n/**\n *\n * @summary Request handler that wraps another handler and runs it only when a check function returns true for the\n * request.\n * @group Handlers\n * @public\n */\nexport class Gate implements Handler {\n /**\n * The wrapped handler to invoke when {@link Gate.checkFn | checkFn} returns `true`.\n */\n declare handler: Handler;\n /**\n * The predicate used to decide whether {@link Gate.handler | handler} should run for a given request.\n */\n declare checkFn: CheckFn;\n\n constructor(handler: Handler, checkFn: CheckFn) {\n this.handler = handler;\n this.checkFn = checkFn;\n }\n\n request<T = unknown>(context: RequestContext, next: NextFn<T>): Promise<T | StructuredDataDocument<T>> | Future<T> {\n if (this.checkFn(context)) {\n return this.handler.request(context, next);\n }\n return next(context.request);\n }\n}\n","import type { StoreRequestContext } from '@warp-drive-mirror/core';\nimport type { Handler, NextFn } from '@warp-drive-mirror/core/request';\nimport type { StructuredDataDocument } from '@warp-drive-mirror/core/types/request';\n\n/**\n * MetaDocHandler processes requests that are marked as meta requests.\n *\n * It treats the response body as \"entirely meta\" transforming\n *\n * ```ts\n * {\n * some: \"key\",\n * another: \"thing\"\n * }\n * ```\n *\n * into\n *\n * ```ts\n * {\n * \t meta: {\n * some: \"key\",\n * another: \"thing\"\n * }\n * }\n * ```\n *\n * To activate this handler, a request should specify\n *\n * ```ts\n * options.isMetaRequest = true\n * ```\n *\n * For instance\n *\n * ```ts\n * store.request({\n * url: '/example',\n * options: {\n * isMetaRequest: true\n * }\n * });\n * ```\n *\n * Errors are not processed by this handler, so if the request fails and the error response\n * is not in {json:api} format additional processing may be needed.\n *\n * @summary Request handler that wraps the whole response body in `meta` for requests with `options.isMetaRequest` set.\n * @group Handlers\n */\nexport const MetaDocHandler: Handler = {\n request<T>(context: StoreRequestContext, next: NextFn<T>): Promise<T | StructuredDataDocument<T> | Awaited<T>> {\n if (!context.request.options?.isMetaRequest) {\n return next(context.request);\n }\n\n return next(context.request).then((response) => {\n return processResponse<T>(response);\n });\n },\n};\n\nfunction processResponse<T>(response: StructuredDataDocument<T>): T {\n return {\n meta: response.content,\n } as T;\n}\n","import { assert } from '@warp-drive-mirror/core/build-config/macros';\n\nif (typeof FastBoot === 'undefined') {\n globalThis.addEventListener('beforeunload', function () {\n sessionStorage.setItem('tab-closed', 'true');\n });\n}\n\nfunction getTabId() {\n if (typeof sessionStorage === 'undefined') {\n return crypto.randomUUID();\n }\n\n const tabId = sessionStorage.getItem('tab-id');\n if (tabId) {\n const tabClosed = sessionStorage.getItem('tab-closed');\n if (tabClosed === 'true') {\n return tabId;\n }\n\n // fall through to generate a new tab id\n }\n\n const newTabId = crypto.randomUUID();\n sessionStorage.setItem('tab-id', newTabId);\n return newTabId;\n}\n\n/**\n * A unique identifier for the current browser tab\n * useful for observability/tracing and deduping\n * across multiple tabs.\n *\n * @summary Random UUID identifying the current browser tab, kept in `sessionStorage` across reloads, for request\n * tracing.\n * @group Constants\n */\nexport const TAB_ID: string = getTabId();\n/**\n * The epoch seconds at which the tab id was generated\n *\n * @summary Epoch seconds recorded when this module loaded, paired with `TAB_ID` in trace headers.\n * @group Constants\n */\nexport const TAB_ASSIGNED: number = Math.floor(Date.now() / 1000);\n\n/**\n * Adds the `X-Amzn-Trace-Id` header to support observability\n * tooling around request routing.\n *\n * This makes use of the {@link TAB_ID} and {@link TAB_ASSIGNED}\n * to enable tracking the browser tab of origin across multiple requests.\n *\n * Follows the template: `Root=1-${now}-${uuidv4};TabId=1-${epochSeconds}-${tab-uuid}`\n *\n * @summary Sets an `X-Amzn-Trace-Id` header carrying a per-request id and the tab id, so requests can be traced to\n * their browser tab.\n * @group Utility Functions\n */\nexport function addTraceHeader(headers: Headers): Headers {\n const now = Math.floor(Date.now() / 1000);\n headers.set('X-Amzn-Trace-Id', `Root=1-${now}-${crypto.randomUUID()};TabId=1-${TAB_ASSIGNED}-${TAB_ID}`);\n\n return headers;\n}\n\n/**\n * Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html\n * As of 2024-12-05 the maximum URL length is 8192 bytes.\n *\n * @summary The 8192-byte URL length limit, taken from AWS CloudFront, that `assertInvalidUrlLength` checks against.\n * @group Constants\n */\nexport const MAX_URL_LENGTH = 8192;\n\n/**\n * This assertion takes a URL and throws an error if the URL is longer than the maximum URL length.\n *\n * See also {@link MAX_URL_LENGTH}\n *\n * @summary Dev-mode assertion that throws when a URL is longer than `MAX_URL_LENGTH`, suggesting a `POST` or `QUERY`\n * request instead.\n * @group Utility Functions\n */\nexport function assertInvalidUrlLength(url: string | undefined): void {\n assert(\n `URL length ${url?.length} exceeds the maximum URL length of ${MAX_URL_LENGTH} bytes.\\n\\nConsider converting this request query a \\`/query\\` endpoint instead of a GET, or upgrade the current endpoint to be able to receive a POST request directly (ideally specifying the header HTTP-Method-Override: QUERY)\\n\\nThe Invalid URL is:\\n\\n${url}`,\n !url || url.length <= MAX_URL_LENGTH\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { getOrSetGlobal } from "@warp-drive-mirror/core/types/-private";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// src/index.ts
|
|
4
4
|
/**
|
|
5
|
+
* @summary Helpers for building request URLs and sorted query strings, with a configurable global host and namespace.
|
|
5
6
|
* @module
|
|
6
7
|
* @mergeModuleWith <project>
|
|
7
8
|
*/
|
|
@@ -9,6 +10,7 @@ import { getOrSetGlobal } from "@warp-drive-mirror/core/types/-private";
|
|
|
9
10
|
* The global configuration used by {@link buildBaseURL} when a call does
|
|
10
11
|
* not provide its own `host`/`namespace`. Set via {@link setBuildURLConfig}.
|
|
11
12
|
*
|
|
13
|
+
* @summary The global `host` and `namespace` that `buildBaseURL` prefixes onto URLs when a call does not pass its own.
|
|
12
14
|
* @public
|
|
13
15
|
*/
|
|
14
16
|
const CONFIG = getOrSetGlobal("CONFIG", {
|
|
@@ -22,6 +24,10 @@ const CONFIG = getOrSetGlobal("CONFIG", {
|
|
|
22
24
|
* These values may still be overridden by passing
|
|
23
25
|
* them to buildBaseURL directly.
|
|
24
26
|
*
|
|
27
|
+
* The [Basic Usage](/guides/the-manual/cookbook/basic-usage#step-2-configure-some-request-defaults)
|
|
28
|
+
* guide sets these defaults through the `@warp-drive-mirror/utilities/json-api` version of this function,
|
|
29
|
+
* which also sets this global configuration.
|
|
30
|
+
*
|
|
25
31
|
* This method may be called as many times as needed.
|
|
26
32
|
* host values of `''` or `'/'` are equivalent.
|
|
27
33
|
*
|
|
@@ -48,6 +54,7 @@ const CONFIG = getOrSetGlobal("CONFIG", {
|
|
|
48
54
|
* });
|
|
49
55
|
* ```
|
|
50
56
|
*
|
|
57
|
+
* @summary Sets the app-wide default `host` and `namespace` used by `buildBaseURL` and the request builders.
|
|
51
58
|
* @public
|
|
52
59
|
*/
|
|
53
60
|
function setBuildURLConfig(config) {
|
|
@@ -72,47 +79,59 @@ function setBuildURLConfig(config) {
|
|
|
72
79
|
/**
|
|
73
80
|
* {@link buildBaseURL} options for a `findRecord` request.
|
|
74
81
|
*
|
|
82
|
+
* @summary Options passed to `buildBaseURL` to build the URL for fetching one resource by type and id.
|
|
75
83
|
* @public
|
|
76
84
|
*/
|
|
77
85
|
/**
|
|
78
86
|
* {@link buildBaseURL} options for a `query` request.
|
|
79
87
|
*
|
|
88
|
+
* @summary Options passed to `buildBaseURL` to build the URL for querying a collection of one resource type.
|
|
80
89
|
* @public
|
|
81
90
|
*/
|
|
82
91
|
/**
|
|
83
92
|
* {@link buildBaseURL} options for a `findMany` request.
|
|
84
93
|
*
|
|
94
|
+
* @summary Options passed to `buildBaseURL` to build the URL for fetching several resources, pathed by the first
|
|
95
|
+
* identifier's type.
|
|
85
96
|
* @public
|
|
86
97
|
*/
|
|
87
98
|
/**
|
|
88
99
|
* {@link buildBaseURL} options for a `findRelatedCollection` request.
|
|
89
100
|
*
|
|
101
|
+
* @summary Options passed to `buildBaseURL` to build the URL for a record's has-many relationship, ending in the
|
|
102
|
+
* field name.
|
|
90
103
|
* @public
|
|
91
104
|
*/
|
|
92
105
|
/**
|
|
93
106
|
* {@link buildBaseURL} options for a `findRelatedRecord` request.
|
|
94
107
|
*
|
|
108
|
+
* @summary Options passed to `buildBaseURL` to build the URL for a record's belongs-to relationship, ending in the
|
|
109
|
+
* field name.
|
|
95
110
|
* @public
|
|
96
111
|
*/
|
|
97
112
|
/**
|
|
98
113
|
* {@link buildBaseURL} options for a `createRecord` request.
|
|
99
114
|
*
|
|
115
|
+
* @summary Options passed to `buildBaseURL` to build the collection URL a new record of a given type is saved to.
|
|
100
116
|
* @public
|
|
101
117
|
*/
|
|
102
118
|
/**
|
|
103
119
|
* {@link buildBaseURL} options for an `updateRecord` request.
|
|
104
120
|
*
|
|
121
|
+
* @summary Options passed to `buildBaseURL` to build the URL for saving changes to an existing record by type and id.
|
|
105
122
|
* @public
|
|
106
123
|
*/
|
|
107
124
|
/**
|
|
108
125
|
* {@link buildBaseURL} options for a `deleteRecord` request.
|
|
109
126
|
*
|
|
127
|
+
* @summary Options passed to `buildBaseURL` to build the URL for deleting an existing record by type and id.
|
|
110
128
|
* @public
|
|
111
129
|
*/
|
|
112
130
|
/**
|
|
113
131
|
* {@link buildBaseURL} options for building a URL directly from a `resourcePath`
|
|
114
132
|
* without an associated request operation.
|
|
115
133
|
*
|
|
134
|
+
* @summary Options passed to `buildBaseURL` to build a URL from an explicit `resourcePath` with no request `op`.
|
|
116
135
|
* @public
|
|
117
136
|
*/
|
|
118
137
|
/**
|
|
@@ -128,6 +147,7 @@ function setBuildURLConfig(config) {
|
|
|
128
147
|
* - {@link DeleteRecordUrlOptions}
|
|
129
148
|
* - {@link GenericUrlOptions}
|
|
130
149
|
*
|
|
150
|
+
* @summary Any of the option shapes `buildBaseURL` accepts, one per request `op` plus a generic `resourcePath` form.
|
|
131
151
|
* @public
|
|
132
152
|
*/
|
|
133
153
|
const OPERATIONS_WITH_PRIMARY_RECORDS = /* @__PURE__ */ new Set([
|
|
@@ -154,6 +174,9 @@ function resourcePathForType(options) {
|
|
|
154
174
|
* Does not include support for building query params (see `buildQueryParams`)
|
|
155
175
|
* so that it may be composed cleanly with other query-params strategies.
|
|
156
176
|
*
|
|
177
|
+
* See the [Builders](/guides/the-manual/requests/builders) guide for writing your own request
|
|
178
|
+
* builders.
|
|
179
|
+
*
|
|
157
180
|
* Usage:
|
|
158
181
|
*
|
|
159
182
|
* ```ts
|
|
@@ -183,6 +206,7 @@ function resourcePathForType(options) {
|
|
|
183
206
|
* - 'findRecord' 'query' 'findMany' 'findRelatedCollection' 'findRelatedRecord'` 'createRecord' 'updateRecord' 'deleteRecord'
|
|
184
207
|
* - Depending on the value of `op`, `identifier` or `identifiers` will be required.
|
|
185
208
|
*
|
|
209
|
+
* @summary Joins host, namespace, resource path, id, and relationship field into a request URL without query params.
|
|
186
210
|
* @public
|
|
187
211
|
*/
|
|
188
212
|
function buildBaseURL(urlOptions) {
|
|
@@ -284,6 +308,11 @@ function handleInclude(include) {
|
|
|
284
308
|
* filter out keys of an object that have falsy values or point to empty arrays
|
|
285
309
|
* returning a new object with only those keys that have truthy values / non-empty arrays
|
|
286
310
|
*
|
|
311
|
+
* See [Cache Keys for Requests](/guides/the-manual/requests/builders#cache-keys-for-requests) for
|
|
312
|
+
* why builders need stable query params.
|
|
313
|
+
*
|
|
314
|
+
* @summary Returns a copy of an object without keys whose values are `undefined`, `null`, empty strings, or empty
|
|
315
|
+
* arrays.
|
|
287
316
|
* @public
|
|
288
317
|
* @param source object to filter keys with empty values from
|
|
289
318
|
* @return A new object with the keys that contained empty values removed
|
|
@@ -302,8 +331,15 @@ function filterEmpty(source) {
|
|
|
302
331
|
* Sorts query params by both key and value returning a new URLSearchParams
|
|
303
332
|
* object with the keys inserted in sorted order.
|
|
304
333
|
*
|
|
334
|
+
* See [Cache Keys for Requests](/guides/the-manual/requests/builders#cache-keys-for-requests) for
|
|
335
|
+
* why builders need stable query params.
|
|
336
|
+
*
|
|
305
337
|
* Treats `included` specially, splicing it into an array if it is a string and sorting the array.
|
|
306
338
|
*
|
|
339
|
+
* The `params` given are never mutated: neither the object itself nor any array
|
|
340
|
+
* value it holds is reordered or replaced, so passing state that is also
|
|
341
|
+
* rendered (a tracked array of filter values, for instance) is safe.
|
|
342
|
+
*
|
|
307
343
|
* Options:
|
|
308
344
|
* - arrayFormat: 'bracket' | 'indices' | 'repeat' | 'comma'
|
|
309
345
|
*
|
|
@@ -312,6 +348,8 @@ function filterEmpty(source) {
|
|
|
312
348
|
* 'repeat': appends the key for every value e.g. `&ids=1&ids=2`
|
|
313
349
|
* 'comma' (default): appends the key once with a comma separated list of values e.g. `&ids=1,2`
|
|
314
350
|
*
|
|
351
|
+
* @summary Builds a `URLSearchParams` with keys and array values sorted, so equivalent queries produce identical
|
|
352
|
+
* params.
|
|
315
353
|
* @public
|
|
316
354
|
* @return A {@link URLSearchParams} with keys inserted in sorted order
|
|
317
355
|
*/
|
|
@@ -319,7 +357,7 @@ function sortQueryParams(params, options) {
|
|
|
319
357
|
const opts = Object.assign({}, DEFAULT_QUERY_PARAMS_SERIALIZATION_OPTIONS, options);
|
|
320
358
|
const paramsIsObject = !(params instanceof URLSearchParams);
|
|
321
359
|
const urlParams = new URLSearchParams();
|
|
322
|
-
const dictionaryParams = paramsIsObject ? params : {};
|
|
360
|
+
const dictionaryParams = paramsIsObject ? Object.assign({}, params) : {};
|
|
323
361
|
if (!paramsIsObject) params.forEach((value, key) => {
|
|
324
362
|
if (!(key in dictionaryParams)) dictionaryParams[key] = value;
|
|
325
363
|
else {
|
|
@@ -332,25 +370,25 @@ function sortQueryParams(params, options) {
|
|
|
332
370
|
Object.keys(dictionaryParams).sort().forEach((key) => {
|
|
333
371
|
const value = dictionaryParams[key];
|
|
334
372
|
if (Array.isArray(value)) {
|
|
335
|
-
value.
|
|
373
|
+
const sortedValue = value.toSorted();
|
|
336
374
|
switch (opts.arrayFormat) {
|
|
337
375
|
case "indices":
|
|
338
|
-
|
|
376
|
+
sortedValue.forEach((v, i) => {
|
|
339
377
|
urlParams.append(`${key}[${i}]`, String(v));
|
|
340
378
|
});
|
|
341
379
|
return;
|
|
342
380
|
case "bracket":
|
|
343
|
-
|
|
381
|
+
sortedValue.forEach((v) => {
|
|
344
382
|
urlParams.append(`${key}[]`, String(v));
|
|
345
383
|
});
|
|
346
384
|
return;
|
|
347
385
|
case "repeat":
|
|
348
|
-
|
|
386
|
+
sortedValue.forEach((v) => {
|
|
349
387
|
urlParams.append(key, String(v));
|
|
350
388
|
});
|
|
351
389
|
return;
|
|
352
390
|
default:
|
|
353
|
-
urlParams.append(key,
|
|
391
|
+
urlParams.append(key, sortedValue.join(","));
|
|
354
392
|
return;
|
|
355
393
|
}
|
|
356
394
|
} else urlParams.append(key, String(value));
|
|
@@ -360,8 +398,15 @@ function sortQueryParams(params, options) {
|
|
|
360
398
|
/**
|
|
361
399
|
* Sorts query params by both key and value, returning a query params string
|
|
362
400
|
*
|
|
401
|
+
* See [Cache Keys for Requests](/guides/the-manual/requests/builders#cache-keys-for-requests) for
|
|
402
|
+
* why builders need stable query params.
|
|
403
|
+
*
|
|
363
404
|
* Treats `included` specially, splicing it into an array if it is a string and sorting the array.
|
|
364
405
|
*
|
|
406
|
+
* The `params` given are never mutated: neither the object itself nor any array
|
|
407
|
+
* value it holds is reordered or replaced, so passing state that is also
|
|
408
|
+
* rendered (a tracked array of filter values, for instance) is safe.
|
|
409
|
+
*
|
|
365
410
|
* Options:
|
|
366
411
|
* - arrayFormat: 'bracket' | 'indices' | 'repeat' | 'comma'
|
|
367
412
|
*
|
|
@@ -370,6 +415,8 @@ function sortQueryParams(params, options) {
|
|
|
370
415
|
* 'repeat': appends the key for every value e.g. `ids=1&ids=2`
|
|
371
416
|
* 'comma' (default): appends the key once with a comma separated list of values e.g. `ids=1,2`
|
|
372
417
|
*
|
|
418
|
+
* @summary Serializes query params into a string with keys and array values sorted, so equivalent queries share one
|
|
419
|
+
* URL.
|
|
373
420
|
* @public
|
|
374
421
|
* @return A sorted query params string without the leading `?`
|
|
375
422
|
*/
|
|
@@ -377,6 +424,5 @@ function buildQueryParams(params, options) {
|
|
|
377
424
|
return sortQueryParams(params, options).toString();
|
|
378
425
|
}
|
|
379
426
|
|
|
380
|
-
//#endregion
|
|
381
427
|
export { buildBaseURL, buildQueryParams, filterEmpty, setBuildURLConfig, sortQueryParams };
|
|
382
428
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["getOrSetGlobal","CONFIG","host","namespace","setBuildURLConfig","config","test","Error","endsWith","startsWith","OPERATIONS_WITH_PRIMARY_RECORDS","Set","isOperationWithPrimaryRecord","options","has","op","hasResourcePath","resourcePath","length","resourcePathForType","identifiers","type","identifier","buildBaseURL","urlOptions","Object","assign","Array","isArray","every","i","id","idPath","encodeURIComponent","fieldPath","String","join","includes","hasHost","url","filter","Boolean","DEFAULT_QUERY_PARAMS_SERIALIZATION_OPTIONS","arrayFormat","handleInclude","include","split","filterEmpty","source","result","key","value","undefined","sortQueryParams","params","opts","paramsIsObject","URLSearchParams","urlParams","dictionaryParams","forEach","hasExisting","existingValue","push","sortedKeys","keys","sort","v","append","buildQueryParams","toString"],"sources":["../../../src/index.ts"],"sourcesContent":["/**\n * @module\n * @mergeModuleWith <project>\n */\n\nimport { assert } from '@warp-drive-mirror/core/build-config/macros';\nimport { getOrSetGlobal } from '@warp-drive-mirror/core/types/-private';\nimport type { QueryParamsSerializationOptions, QueryParamsSource, Serializable } from '@warp-drive-mirror/core/types/params';\n\n// prevents the final constructed object from needing to add\n// host and namespace which are provided by the final consuming\n// class to the prototype which can result in overwrite errors\n\n/**\n * The global configuration used by {@link buildBaseURL} when a call does\n * not provide its own `host`/`namespace`. Set via {@link setBuildURLConfig}.\n *\n * @public\n */\nexport interface BuildURLConfig {\n /**\n * The scheme, domain and port (if any) to prefix built URLs with, e.g. `'https://api.example.com'`.\n */\n host: string | null;\n /**\n * The path segment to insert between `host` and the resource path, e.g. `'api/v1'`.\n */\n namespace: string | null;\n}\n\nconst CONFIG: BuildURLConfig = getOrSetGlobal('CONFIG', {\n host: '',\n namespace: '',\n});\n\n/**\n * Sets the global configuration for `buildBaseURL`\n * for host and namespace values for the application.\n *\n * These values may still be overridden by passing\n * them to buildBaseURL directly.\n *\n * This method may be called as many times as needed.\n * host values of `''` or `'/'` are equivalent.\n *\n * Except for the value of `/` as host, host should not\n * end with `/`.\n *\n * namespace should not start or end with a `/`.\n *\n * ```ts\n * type BuildURLConfig = {\n * host: string;\n * namespace: string'\n * }\n * ```\n *\n * Example:\n *\n * ```ts\n * import { setBuildURLConfig } from '@warp-drive-mirror/utilities';\n *\n * setBuildURLConfig({\n * host: 'https://api.example.com',\n * namespace: 'api/v1'\n * });\n * ```\n *\n * @public\n */\nexport function setBuildURLConfig(config: BuildURLConfig): void {\n assert(`setBuildURLConfig: You must pass a config object`, config);\n assert(\n `setBuildURLConfig: You must pass a config object with a 'host' or 'namespace' property`,\n 'host' in config || 'namespace' in config\n );\n\n CONFIG.host = config.host || '';\n CONFIG.namespace = config.namespace || '';\n\n assert(\n `buildBaseURL: host must NOT end with '/', received '${CONFIG.host}'`,\n CONFIG.host === '/' || !CONFIG.host.endsWith('/')\n );\n assert(\n `buildBaseURL: namespace must NOT start with '/', received '${CONFIG.namespace}'`,\n !CONFIG.namespace.startsWith('/')\n );\n assert(\n `buildBaseURL: namespace must NOT end with '/', received '${CONFIG.namespace}'`,\n !CONFIG.namespace.endsWith('/')\n );\n}\n\n/**\n * {@link buildBaseURL} options for a `findRecord` request.\n *\n * @public\n */\nexport interface FindRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findRecord';\n /**\n * The type and id of the record to find.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `query` request.\n *\n * @public\n */\nexport interface QueryUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'query';\n /**\n * The type of the records to query.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `findMany` request.\n *\n * @public\n */\nexport interface FindManyUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findMany';\n /**\n * The type and id of each record to find.\n */\n identifiers: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n }[];\n /**\n * The path segment for the resource, defaults to the first identifier's `type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n/**\n * {@link buildBaseURL} options for a `findRelatedCollection` request.\n *\n * @public\n */\nexport interface FindRelatedCollectionUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findRelatedCollection';\n /**\n * The type and id of the record whose relationship is being fetched.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The relationship field name, appended to the resource path.\n */\n fieldPath: string;\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `findRelatedRecord` request.\n *\n * @public\n */\nexport interface FindRelatedResourceUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findRelatedRecord';\n /**\n * The type and id of the record whose relationship is being fetched.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The relationship field name, appended to the resource path.\n */\n fieldPath: string;\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `createRecord` request.\n *\n * @public\n */\nexport interface CreateRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'createRecord';\n /**\n * The type of the record being created.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for an `updateRecord` request.\n *\n * @public\n */\nexport interface UpdateRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'updateRecord';\n /**\n * The type and id of the record being updated.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `deleteRecord` request.\n *\n * @public\n */\nexport interface DeleteRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'deleteRecord';\n /**\n * The type and id of the record being deleted.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for building a URL directly from a `resourcePath`\n * without an associated request operation.\n *\n * @public\n */\nexport interface GenericUrlOptions {\n /**\n * The path segment for the resource.\n */\n resourcePath: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * The union of all `op`-specific option shapes accepted by {@link buildBaseURL}, one of:\n *\n * - {@link FindRecordUrlOptions}\n * - {@link QueryUrlOptions}\n * - {@link FindManyUrlOptions}\n * - {@link FindRelatedCollectionUrlOptions}\n * - {@link FindRelatedResourceUrlOptions}\n * - {@link CreateRecordUrlOptions}\n * - {@link UpdateRecordUrlOptions}\n * - {@link DeleteRecordUrlOptions}\n * - {@link GenericUrlOptions}\n *\n * @public\n */\nexport type UrlOptions =\n | FindRecordUrlOptions\n | QueryUrlOptions\n | FindManyUrlOptions\n | FindRelatedCollectionUrlOptions\n | FindRelatedResourceUrlOptions\n | CreateRecordUrlOptions\n | UpdateRecordUrlOptions\n | DeleteRecordUrlOptions\n | GenericUrlOptions;\n\nconst OPERATIONS_WITH_PRIMARY_RECORDS = new Set([\n 'findRecord',\n 'findRelatedRecord',\n 'findRelatedCollection',\n 'updateRecord',\n 'deleteRecord',\n]);\n\nfunction isOperationWithPrimaryRecord(\n options: UrlOptions\n): options is\n | FindRecordUrlOptions\n | FindRelatedCollectionUrlOptions\n | FindRelatedResourceUrlOptions\n | UpdateRecordUrlOptions\n | DeleteRecordUrlOptions {\n return 'op' in options && OPERATIONS_WITH_PRIMARY_RECORDS.has(options.op);\n}\n\nfunction hasResourcePath(options: UrlOptions): options is GenericUrlOptions {\n return 'resourcePath' in options && typeof options.resourcePath === 'string' && options.resourcePath.length > 0;\n}\n\nfunction resourcePathForType(options: UrlOptions): string {\n assert(\n `resourcePathForType: You must pass a valid op as part of options`,\n 'op' in options && typeof options.op === 'string'\n );\n return options.op === 'findMany' ? options.identifiers[0].type : options.identifier.type;\n}\n\n/**\n * Builds a URL for a request based on the provided options.\n * Does not include support for building query params (see `buildQueryParams`)\n * so that it may be composed cleanly with other query-params strategies.\n *\n * Usage:\n *\n * ```ts\n * import { buildBaseURL } from '@warp-drive-mirror/utilities';\n *\n * const url = buildBaseURL({\n * host: 'https://api.example.com',\n * namespace: 'api/v1',\n * resourcePath: 'emberDevelopers',\n * op: 'query',\n * identifier: { type: 'ember-developer' }\n * });\n *\n * // => 'https://api.example.com/api/v1/emberDevelopers'\n * ```\n *\n * On the surface this may seem like a lot of work to do something simple, but\n * it is designed to be composable with other utilities and interfaces that the\n * average product engineer will never need to see or use.\n *\n * A few notes:\n *\n * - `resourcePath` is optional, but if it is not provided, `identifier.type` will be used.\n * - `host` and `namespace` are optional, but if they are not provided, the values globally\n * configured via `setBuildURLConfig` will be used.\n * - `op` is required and must be one of the following:\n * - 'findRecord' 'query' 'findMany' 'findRelatedCollection' 'findRelatedRecord'` 'createRecord' 'updateRecord' 'deleteRecord'\n * - Depending on the value of `op`, `identifier` or `identifiers` will be required.\n *\n * @public\n */\nexport function buildBaseURL(urlOptions: UrlOptions): string {\n const options = Object.assign(\n {\n host: CONFIG.host,\n namespace: CONFIG.namespace,\n },\n urlOptions\n );\n assert(\n `buildBaseURL: You must pass \\`op\\` as part of options`,\n hasResourcePath(options) || (typeof options.op === 'string' && options.op.length > 0)\n );\n assert(\n `buildBaseURL: You must pass \\`identifier\\` as part of options`,\n hasResourcePath(options) ||\n options.op === 'findMany' ||\n (options.identifier && typeof options.identifier === 'object')\n );\n assert(\n `buildBaseURL: You must pass \\`identifiers\\` as part of options`,\n hasResourcePath(options) ||\n options.op !== 'findMany' ||\n (options.identifiers &&\n Array.isArray(options.identifiers) &&\n options.identifiers.length > 0 &&\n options.identifiers.every((i) => i && typeof i === 'object'))\n );\n assert(\n `buildBaseURL: You must pass valid \\`identifier\\` as part of options, expected 'id'`,\n hasResourcePath(options) ||\n !isOperationWithPrimaryRecord(options) ||\n (typeof options.identifier.id === 'string' && options.identifier.id.length > 0)\n );\n assert(\n `buildBaseURL: You must pass \\`identifiers\\` as part of options`,\n hasResourcePath(options) ||\n options.op !== 'findMany' ||\n options.identifiers.every((i) => typeof i.id === 'string' && i.id.length > 0)\n );\n assert(\n `buildBaseURL: You must pass valid \\`identifier\\` as part of options, expected 'type'`,\n hasResourcePath(options) ||\n options.op === 'findMany' ||\n (typeof options.identifier.type === 'string' && options.identifier.type.length > 0)\n );\n assert(\n `buildBaseURL: You must pass valid \\`identifiers\\` as part of options, expected 'type'`,\n hasResourcePath(options) ||\n options.op !== 'findMany' ||\n (typeof options.identifiers[0].type === 'string' && options.identifiers[0].type.length > 0)\n );\n\n // prettier-ignore\n const idPath: string =\n isOperationWithPrimaryRecord(options) ? encodeURIComponent(options.identifier.id)\n : '';\n const resourcePath = options.resourcePath || resourcePathForType(options);\n const { host, namespace } = options;\n const fieldPath = 'fieldPath' in options ? options.fieldPath : '';\n\n assert(\n `buildBaseURL: You tried to build a url for a ${String(\n 'op' in options ? options.op + ' ' : ''\n )}request to ${resourcePath} but resourcePath must be set or op must be one of \"${[\n 'findRecord',\n 'findRelatedRecord',\n 'findRelatedCollection',\n 'updateRecord',\n 'deleteRecord',\n 'createRecord',\n 'query',\n 'findMany',\n ].join('\",\"')}\".`,\n hasResourcePath(options) ||\n [\n 'findRecord',\n 'query',\n 'findMany',\n 'findRelatedCollection',\n 'findRelatedRecord',\n 'createRecord',\n 'updateRecord',\n 'deleteRecord',\n ].includes(options.op)\n );\n\n assert(`buildBaseURL: host must NOT end with '/', received '${host}'`, host === '/' || !host.endsWith('/'));\n assert(`buildBaseURL: namespace must NOT start with '/', received '${namespace}'`, !namespace.startsWith('/'));\n assert(`buildBaseURL: namespace must NOT end with '/', received '${namespace}'`, !namespace.endsWith('/'));\n assert(\n `buildBaseURL: resourcePath must NOT start with '/', received '${resourcePath}'`,\n !resourcePath.startsWith('/')\n );\n assert(`buildBaseURL: resourcePath must NOT end with '/', received '${resourcePath}'`, !resourcePath.endsWith('/'));\n assert(`buildBaseURL: fieldPath must NOT start with '/', received '${fieldPath}'`, !fieldPath.startsWith('/'));\n assert(`buildBaseURL: fieldPath must NOT end with '/', received '${fieldPath}'`, !fieldPath.endsWith('/'));\n assert(`buildBaseURL: idPath must NOT start with '/', received '${idPath}'`, !idPath.startsWith('/'));\n assert(`buildBaseURL: idPath must NOT end with '/', received '${idPath}'`, !idPath.endsWith('/'));\n\n const hasHost = host !== '' && host !== '/';\n const url = [hasHost ? host : '', namespace, resourcePath, idPath, fieldPath].filter(Boolean).join('/');\n return hasHost ? url : `/${url}`;\n}\n\nconst DEFAULT_QUERY_PARAMS_SERIALIZATION_OPTIONS: QueryParamsSerializationOptions = {\n arrayFormat: 'comma',\n};\n\nfunction handleInclude(include: string | string[]): string[] {\n assert(\n `Expected include to be a string or array, got ${typeof include}`,\n typeof include === 'string' || Array.isArray(include)\n );\n return typeof include === 'string' ? include.split(',') : include;\n}\n\n/**\n * filter out keys of an object that have falsy values or point to empty arrays\n * returning a new object with only those keys that have truthy values / non-empty arrays\n *\n * @public\n * @param source object to filter keys with empty values from\n * @return A new object with the keys that contained empty values removed\n */\nexport function filterEmpty(source: Record<string, Serializable>): Record<string, Serializable> {\n const result: Record<string, Serializable> = {};\n for (const key in source) {\n const value = source[key];\n // Allow `0` and `false` but filter falsy values that indicate \"empty\"\n if (value !== undefined && value !== null && value !== '') {\n if (!Array.isArray(value) || value.length > 0) {\n result[key] = source[key];\n }\n }\n }\n return result;\n}\n\n/**\n * Sorts query params by both key and value returning a new URLSearchParams\n * object with the keys inserted in sorted order.\n *\n * Treats `included` specially, splicing it into an array if it is a string and sorting the array.\n *\n * Options:\n * - arrayFormat: 'bracket' | 'indices' | 'repeat' | 'comma'\n *\n * 'bracket': appends [] to the key for every value e.g. `&ids[]=1&ids[]=2`\n * 'indices': appends [i] to the key for every value e.g. `&ids[0]=1&ids[1]=2`\n * 'repeat': appends the key for every value e.g. `&ids=1&ids=2`\n * 'comma' (default): appends the key once with a comma separated list of values e.g. `&ids=1,2`\n *\n * @public\n * @return A {@link URLSearchParams} with keys inserted in sorted order\n */\nexport function sortQueryParams(params: QueryParamsSource, options?: QueryParamsSerializationOptions): URLSearchParams {\n const opts = Object.assign({}, DEFAULT_QUERY_PARAMS_SERIALIZATION_OPTIONS, options);\n const paramsIsObject = !(params instanceof URLSearchParams);\n const urlParams = new URLSearchParams();\n const dictionaryParams: Record<string, Serializable> = paramsIsObject ? params : {};\n\n if (!paramsIsObject) {\n params.forEach((value, key) => {\n const hasExisting = key in dictionaryParams;\n if (!hasExisting) {\n dictionaryParams[key] = value;\n } else {\n const existingValue = dictionaryParams[key];\n if (Array.isArray(existingValue)) {\n existingValue.push(value);\n } else {\n dictionaryParams[key] = [existingValue, value];\n }\n }\n });\n }\n\n if ('include' in dictionaryParams) {\n dictionaryParams.include = handleInclude(dictionaryParams.include as string | string[]);\n }\n\n const sortedKeys = Object.keys(dictionaryParams).sort();\n sortedKeys.forEach((key) => {\n const value = dictionaryParams[key];\n if (Array.isArray(value)) {\n value.sort();\n switch (opts.arrayFormat) {\n case 'indices':\n value.forEach((v, i) => {\n urlParams.append(`${key}[${i}]`, String(v));\n });\n return;\n case 'bracket':\n value.forEach((v) => {\n urlParams.append(`${key}[]`, String(v));\n });\n return;\n case 'repeat':\n value.forEach((v) => {\n urlParams.append(key, String(v));\n });\n return;\n case 'comma':\n default:\n urlParams.append(key, value.join(','));\n return;\n }\n } else {\n urlParams.append(key, String(value));\n }\n });\n\n return urlParams;\n}\n\n/**\n * Sorts query params by both key and value, returning a query params string\n *\n * Treats `included` specially, splicing it into an array if it is a string and sorting the array.\n *\n * Options:\n * - arrayFormat: 'bracket' | 'indices' | 'repeat' | 'comma'\n *\n * 'bracket': appends [] to the key for every value e.g. `ids[]=1&ids[]=2`\n * 'indices': appends [i] to the key for every value e.g. `ids[0]=1&ids[1]=2`\n * 'repeat': appends the key for every value e.g. `ids=1&ids=2`\n * 'comma' (default): appends the key once with a comma separated list of values e.g. `ids=1,2`\n *\n * @public\n * @return A sorted query params string without the leading `?`\n */\nexport function buildQueryParams(params: QueryParamsSource, options?: QueryParamsSerializationOptions): string {\n return sortQueryParams(params, options).toString();\n}\n"],"mappings":";;;;;;;;;;;;;AA8BA,MAAMC,SAAyBD,eAAe,UAAU;CACtDE,MAAM;CACNC,WAAW;AACb,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCD,SAAgBC,kBAAkBC,QAA8B;CAC9D,EAAAC,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,kDAAkD;CAAA,EAAA,CAAEF,MAAM;CACjE,EAAAC,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,wFAAwF;CAAA,EAAA,CACxF,UAAUF,UAAU,eAAeA,MAAM;CAG3CJ,OAAOC,OAAOG,OAAOH,QAAQ;CAC7BD,OAAOE,YAAYE,OAAOF,aAAa;CAEvC,EAAAG,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,uDAAuDN,OAAOC,KAAI,EAAG;CAAA,EAAA,CACrED,OAAOC,SAAS,OAAO,CAACD,OAAOC,KAAKM,SAAS,GAAG,CAAC;CAEnD,EAAAF,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,8DAA8DN,OAAOE,UAAS,EAAG;CAAA,EAAA,CACjF,CAACF,OAAOE,UAAUM,WAAW,GAAG,CAAC;CAEnC,EAAAH,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,4DAA4DN,OAAOE,UAAS,EAAG;CAAA,EAAA,CAC/E,CAACF,OAAOE,UAAUK,SAAS,GAAG,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwVA,MAAME,kDAAkC,IAAIC,IAAI;CAC9C;CACA;CACA;CACA;CACA;AAAc,CACf;AAED,SAASC,6BACPC,SAMyB;CACzB,OAAO,QAAQA,WAAWH,gCAAgCI,IAAID,QAAQE,EAAE;AAC1E;AAEA,SAASC,gBAAgBH,SAAmD;CAC1E,OAAO,kBAAkBA,WAAW,OAAOA,QAAQI,iBAAiB,YAAYJ,QAAQI,aAAaC,SAAS;AAChH;AAEA,SAASC,oBAAoBN,SAA6B;CACxD,EAAAP,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,kEAAkE;CAAA,EAAA,CAClE,QAAQM,WAAW,OAAOA,QAAQE,OAAO,QAAQ;CAEnD,OAAOF,QAAQE,OAAO,aAAaF,QAAQO,YAAY,EAAE,CAACC,OAAOR,QAAQS,WAAWD;AACtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgBE,aAAaC,YAAgC;CAC3D,MAAMX,UAAUY,OAAOC,OACrB;EACExB,MAAMD,OAAOC;EACbC,WAAWF,OAAOE;CACpB,GACAqB,UACF;CACA,EAAAlB,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,uDAAuD;CAAA,EAAA,CACvDS,gBAAgBH,OAAO,KAAM,OAAOA,QAAQE,OAAO,YAAYF,QAAQE,GAAGG,SAAS,CAAE;CAEvF,EAAAZ,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,+DAA+D;CAAA,EAAA,CAC/DS,gBAAgBH,OAAO,KACrBA,QAAQE,OAAO,cACdF,QAAQS,cAAc,OAAOT,QAAQS,eAAe,QAAS;CAElE,EAAAhB,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,gEAAgE;CAAA,EAAA,CAChES,gBAAgBH,OAAO,KACrBA,QAAQE,OAAO,cACdF,QAAQO,eACPO,MAAMC,QAAQf,QAAQO,WAAW,KACjCP,QAAQO,YAAYF,SAAS,KAC7BL,QAAQO,YAAYS,OAAOC,MAAMA,KAAK,OAAOA,MAAM,QAAQ,CAAE;CAEnE,EAAAxB,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,oFAAoF;CAAA,EAAA,CACpFS,gBAAgBH,OAAO,KACrB,CAACD,6BAA6BC,OAAO,KACpC,OAAOA,QAAQS,WAAWS,OAAO,YAAYlB,QAAQS,WAAWS,GAAGb,SAAS,CAAE;CAEnF,EAAAZ,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,gEAAgE;CAAA,EAAA,CAChES,gBAAgBH,OAAO,KACrBA,QAAQE,OAAO,cACfF,QAAQO,YAAYS,OAAOC,MAAM,OAAOA,EAAEC,OAAO,YAAYD,EAAEC,GAAGb,SAAS,CAAC,CAAC;CAEjF,EAAAZ,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,sFAAsF;CAAA,EAAA,CACtFS,gBAAgBH,OAAO,KACrBA,QAAQE,OAAO,cACd,OAAOF,QAAQS,WAAWD,SAAS,YAAYR,QAAQS,WAAWD,KAAKH,SAAS,CAAE;CAEvF,EAAAZ,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,uFAAuF;CAAA,EAAA,CACvFS,gBAAgBH,OAAO,KACrBA,QAAQE,OAAO,cACd,OAAOF,QAAQO,YAAY,EAAE,CAACC,SAAS,YAAYR,QAAQO,YAAY,EAAE,CAACC,KAAKH,SAAS,CAAE;CAI/F,MAAMc,SACFpB,6BAA6BC,OAAO,IAAIoB,mBAAmBpB,QAAQS,WAAWS,EAAE,IAC9E;CACN,MAAMd,eAAeJ,QAAQI,gBAAgBE,oBAAoBN,OAAO;CACxE,MAAM,EAAEX,MAAMC,cAAcU;CAC5B,MAAMqB,YAAY,eAAerB,UAAUA,QAAQqB,YAAY;CAE/D,EAAA5B,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,gDAAgD4B,OAC9C,QAAQtB,UAAUA,QAAQE,KAAK,MAAM,EACvC,EAAC,aAAcE,aAAY,sDAAuD;GAChF;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EAAU,CACX,CAACmB,KAAK,OAAK,EAAC,GAAI;CAAA,EAAA,CACjBpB,gBAAgBH,OAAO,KACrB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAc,CACf,CAACwB,SAASxB,QAAQE,EAAE,CAAC;CAG1B,EAAAT,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,uDAAuDL,KAAI,EAAG;CAAA,EAAA,CAAEA,SAAS,OAAO,CAACA,KAAKM,SAAS,GAAG,CAAC;CAC1G,EAAAF,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,8DAA8DJ,UAAS,EAAG;CAAA,EAAA,CAAE,CAACA,UAAUM,WAAW,GAAG,CAAC;CAC7G,EAAAH,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,4DAA4DJ,UAAS,EAAG;CAAA,EAAA,CAAE,CAACA,UAAUK,SAAS,GAAG,CAAC;CACzG,EAAAF,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,iEAAiEU,aAAY,EAAG;CAAA,EAAA,CAChF,CAACA,aAAaR,WAAW,GAAG,CAAC;CAE/B,EAAAH,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,+DAA+DU,aAAY,EAAG;CAAA,EAAA,CAAE,CAACA,aAAaT,SAAS,GAAG,CAAC;CAClH,EAAAF,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,8DAA8D2B,UAAS,EAAG;CAAA,EAAA,CAAE,CAACA,UAAUzB,WAAW,GAAG,CAAC;CAC7G,EAAAH,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,4DAA4D2B,UAAS,EAAG;CAAA,EAAA,CAAE,CAACA,UAAU1B,SAAS,GAAG,CAAC;CACzG,EAAAF,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,2DAA2DyB,OAAM,EAAG;CAAA,EAAA,CAAE,CAACA,OAAOvB,WAAW,GAAG,CAAC;CACpG,EAAAH,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MAAO,yDAAyDyB,OAAM,EAAG;CAAA,EAAA,CAAE,CAACA,OAAOxB,SAAS,GAAG,CAAC;CAEhG,MAAM8B,UAAUpC,SAAS,MAAMA,SAAS;CACxC,MAAMqC,MAAM;EAACD,UAAUpC,OAAO;EAAIC;EAAWc;EAAce;EAAQE;CAAS,CAAC,CAACM,OAAOC,OAAO,CAAC,CAACL,KAAK,GAAG;CACtG,OAAOE,UAAUC,MAAM,IAAIA;AAC7B;AAEA,MAAMG,6CAA8E,EAClFC,aAAa,QACf;AAEA,SAASC,cAAcC,SAAsC;CAC3D,EAAAvC,SAAA;EAAA,IAAA,CAAAA,MAAA,MAAA,IAAAC,MACE,iDAAiD,OAAOsC,SAAS;CAAA,EAAA,CACjE,OAAOA,YAAY,YAAYlB,MAAMC,QAAQiB,OAAO,CAAC;CAEvD,OAAO,OAAOA,YAAY,WAAWA,QAAQC,MAAM,GAAG,IAAID;AAC5D;;;;;;;;;AAUA,SAAgBE,YAAYC,QAAoE;CAC9F,MAAMC,SAAuC,CAAC;CAC9C,KAAK,MAAMC,OAAOF,QAAQ;EACxB,MAAMG,QAAQH,OAAOE;EAErB,IAAIC,UAAUC,UAAaD,UAAU,QAAQA,UAAU,IACrD;OAAI,CAACxB,MAAMC,QAAQuB,KAAK,KAAKA,MAAMjC,SAAS,GAC1C+B,OAAOC,OAAOF,OAAOE;EACvB;CAEJ;CACA,OAAOD;AACT;;;;;;;;;;;;;;;;;;AAmBA,SAAgBI,gBAAgBC,QAA2BzC,SAA4D;CACrH,MAAM0C,OAAO9B,OAAOC,OAAO,CAAC,GAAGgB,4CAA4C7B,OAAO;CAClF,MAAM2C,iBAAiB,EAAEF,kBAAkBG;CAC3C,MAAMC,YAAY,IAAID,gBAAgB;CACtC,MAAME,mBAAiDH,iBAAiBF,SAAS,CAAC;CAElF,IAAI,CAACE,gBACHF,OAAOM,SAAST,OAAOD,QAAQ;EAE7B,IAAI,EADgBA,OAAOS,mBAEzBA,iBAAiBT,OAAOC;OACnB;GACL,MAAMW,gBAAgBH,iBAAiBT;GACvC,IAAIvB,MAAMC,QAAQkC,aAAa,GAC7BA,cAAcC,KAAKZ,KAAK;QAExBQ,iBAAiBT,OAAO,CAACY,eAAeX,KAAK;EAEjD;CACF,CAAC;CAGH,IAAI,aAAaQ,kBACfA,iBAAiBd,UAAUD,cAAce,iBAAiBd,OAA4B;CAIxFmB,AADmBvC,OAAOwC,KAAKN,gBAAgB,CAAC,CAACO,KACjDF,CAAU,CAACJ,SAASV,QAAQ;EAC1B,MAAMC,QAAQQ,iBAAiBT;EAC/B,IAAIvB,MAAMC,QAAQuB,KAAK,GAAG;GACxBA,MAAMe,KAAK;GACX,QAAQX,KAAKZ,aAAb;IACE,KAAK;KACHQ,MAAMS,SAASO,GAAGrC,MAAM;MACtB4B,UAAUU,OAAO,GAAGlB,IAAG,GAAIpB,EAAC,IAAKK,OAAOgC,CAAC,CAAC;KAC5C,CAAC;KACD;IACF,KAAK;KACHhB,MAAMS,SAASO,MAAM;MACnBT,UAAUU,OAAO,GAAGlB,IAAG,KAAMf,OAAOgC,CAAC,CAAC;KACxC,CAAC;KACD;IACF,KAAK;KACHhB,MAAMS,SAASO,MAAM;MACnBT,UAAUU,OAAOlB,KAAKf,OAAOgC,CAAC,CAAC;KACjC,CAAC;KACD;IAEF;KACET,UAAUU,OAAOlB,KAAKC,MAAMf,KAAK,GAAG,CAAC;KACrC;GACJ;EACF,OACEsB,UAAUU,OAAOlB,KAAKf,OAAOgB,KAAK,CAAC;CAEvC,CAAC;CAED,OAAOO;AACT;;;;;;;;;;;;;;;;;AAkBA,SAAgBW,iBAAiBf,QAA2BzC,SAAmD;CAC7G,OAAOwC,gBAAgBC,QAAQzC,OAAO,CAAC,CAACyD,SAAS;AACnD"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["getOrSetGlobal","CONFIG","host","namespace","setBuildURLConfig","config","test","Error","endsWith","startsWith","OPERATIONS_WITH_PRIMARY_RECORDS","Set","isOperationWithPrimaryRecord","options","has","op","hasResourcePath","resourcePath","length","resourcePathForType","identifiers","type","identifier","buildBaseURL","urlOptions","Object","assign","Array","isArray","every","i","id","idPath","encodeURIComponent","fieldPath","String","join","includes","hasHost","url","filter","Boolean","DEFAULT_QUERY_PARAMS_SERIALIZATION_OPTIONS","arrayFormat","handleInclude","include","split","filterEmpty","source","result","key","value","undefined","sortQueryParams","params","opts","paramsIsObject","URLSearchParams","urlParams","dictionaryParams","forEach","hasExisting","existingValue","push","sortedKeys","keys","sort","sortedValue","toSorted","v","append","buildQueryParams","toString"],"sources":["../../../src/index.ts"],"sourcesContent":["/**\n * @summary Helpers for building request URLs and sorted query strings, with a configurable global host and namespace.\n * @module\n * @mergeModuleWith <project>\n */\n\nimport { assert } from '@warp-drive-mirror/core/build-config/macros';\nimport { getOrSetGlobal } from '@warp-drive-mirror/core/types/-private';\nimport type { QueryParamsSerializationOptions, QueryParamsSource, Serializable } from '@warp-drive-mirror/core/types/params';\n\n// prevents the final constructed object from needing to add\n// host and namespace which are provided by the final consuming\n// class to the prototype which can result in overwrite errors\n\n/**\n * The global configuration used by {@link buildBaseURL} when a call does\n * not provide its own `host`/`namespace`. Set via {@link setBuildURLConfig}.\n *\n * @summary The global `host` and `namespace` that `buildBaseURL` prefixes onto URLs when a call does not pass its own.\n * @public\n */\nexport interface BuildURLConfig {\n /**\n * The scheme, domain and port (if any) to prefix built URLs with, e.g. `'https://api.example.com'`.\n */\n host: string | null;\n /**\n * The path segment to insert between `host` and the resource path, e.g. `'api/v1'`.\n */\n namespace: string | null;\n}\n\nconst CONFIG: BuildURLConfig = getOrSetGlobal('CONFIG', {\n host: '',\n namespace: '',\n});\n\n/**\n * Sets the global configuration for `buildBaseURL`\n * for host and namespace values for the application.\n *\n * These values may still be overridden by passing\n * them to buildBaseURL directly.\n *\n * The [Basic Usage](/guides/the-manual/cookbook/basic-usage#step-2-configure-some-request-defaults)\n * guide sets these defaults through the `@warp-drive-mirror/utilities/json-api` version of this function,\n * which also sets this global configuration.\n *\n * This method may be called as many times as needed.\n * host values of `''` or `'/'` are equivalent.\n *\n * Except for the value of `/` as host, host should not\n * end with `/`.\n *\n * namespace should not start or end with a `/`.\n *\n * ```ts\n * type BuildURLConfig = {\n * host: string;\n * namespace: string'\n * }\n * ```\n *\n * Example:\n *\n * ```ts\n * import { setBuildURLConfig } from '@warp-drive-mirror/utilities';\n *\n * setBuildURLConfig({\n * host: 'https://api.example.com',\n * namespace: 'api/v1'\n * });\n * ```\n *\n * @summary Sets the app-wide default `host` and `namespace` used by `buildBaseURL` and the request builders.\n * @public\n */\nexport function setBuildURLConfig(config: BuildURLConfig): void {\n assert(`setBuildURLConfig: You must pass a config object`, config);\n assert(\n `setBuildURLConfig: You must pass a config object with a 'host' or 'namespace' property`,\n 'host' in config || 'namespace' in config\n );\n\n CONFIG.host = config.host || '';\n CONFIG.namespace = config.namespace || '';\n\n assert(\n `buildBaseURL: host must NOT end with '/', received '${CONFIG.host}'`,\n CONFIG.host === '/' || !CONFIG.host.endsWith('/')\n );\n assert(\n `buildBaseURL: namespace must NOT start with '/', received '${CONFIG.namespace}'`,\n !CONFIG.namespace.startsWith('/')\n );\n assert(\n `buildBaseURL: namespace must NOT end with '/', received '${CONFIG.namespace}'`,\n !CONFIG.namespace.endsWith('/')\n );\n}\n\n/**\n * {@link buildBaseURL} options for a `findRecord` request.\n *\n * @summary Options passed to `buildBaseURL` to build the URL for fetching one resource by type and id.\n * @public\n */\nexport interface FindRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findRecord';\n /**\n * The type and id of the record to find.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `query` request.\n *\n * @summary Options passed to `buildBaseURL` to build the URL for querying a collection of one resource type.\n * @public\n */\nexport interface QueryUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'query';\n /**\n * The type of the records to query.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `findMany` request.\n *\n * @summary Options passed to `buildBaseURL` to build the URL for fetching several resources, pathed by the first\n * identifier's type.\n * @public\n */\nexport interface FindManyUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findMany';\n /**\n * The type and id of each record to find.\n */\n identifiers: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n }[];\n /**\n * The path segment for the resource, defaults to the first identifier's `type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n/**\n * {@link buildBaseURL} options for a `findRelatedCollection` request.\n *\n * @summary Options passed to `buildBaseURL` to build the URL for a record's has-many relationship, ending in the\n * field name.\n * @public\n */\nexport interface FindRelatedCollectionUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findRelatedCollection';\n /**\n * The type and id of the record whose relationship is being fetched.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The relationship field name, appended to the resource path.\n */\n fieldPath: string;\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `findRelatedRecord` request.\n *\n * @summary Options passed to `buildBaseURL` to build the URL for a record's belongs-to relationship, ending in the\n * field name.\n * @public\n */\nexport interface FindRelatedResourceUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'findRelatedRecord';\n /**\n * The type and id of the record whose relationship is being fetched.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The relationship field name, appended to the resource path.\n */\n fieldPath: string;\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `createRecord` request.\n *\n * @summary Options passed to `buildBaseURL` to build the collection URL a new record of a given type is saved to.\n * @public\n */\nexport interface CreateRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'createRecord';\n /**\n * The type of the record being created.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for an `updateRecord` request.\n *\n * @summary Options passed to `buildBaseURL` to build the URL for saving changes to an existing record by type and id.\n * @public\n */\nexport interface UpdateRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'updateRecord';\n /**\n * The type and id of the record being updated.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for a `deleteRecord` request.\n *\n * @summary Options passed to `buildBaseURL` to build the URL for deleting an existing record by type and id.\n * @public\n */\nexport interface DeleteRecordUrlOptions {\n /**\n * The request operation this URL is for.\n */\n op: 'deleteRecord';\n /**\n * The type and id of the record being deleted.\n */\n identifier: {\n /**\n * The resource type.\n */\n type: string;\n /**\n * The resource id.\n */\n id: string;\n };\n /**\n * The path segment for the resource, defaults to `identifier.type` if not provided.\n */\n resourcePath?: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * {@link buildBaseURL} options for building a URL directly from a `resourcePath`\n * without an associated request operation.\n *\n * @summary Options passed to `buildBaseURL` to build a URL from an explicit `resourcePath` with no request `op`.\n * @public\n */\nexport interface GenericUrlOptions {\n /**\n * The path segment for the resource.\n */\n resourcePath: string;\n /**\n * Overrides the globally configured host for this call only.\n */\n host?: string;\n /**\n * Overrides the globally configured namespace for this call only.\n */\n namespace?: string;\n}\n\n/**\n * The union of all `op`-specific option shapes accepted by {@link buildBaseURL}, one of:\n *\n * - {@link FindRecordUrlOptions}\n * - {@link QueryUrlOptions}\n * - {@link FindManyUrlOptions}\n * - {@link FindRelatedCollectionUrlOptions}\n * - {@link FindRelatedResourceUrlOptions}\n * - {@link CreateRecordUrlOptions}\n * - {@link UpdateRecordUrlOptions}\n * - {@link DeleteRecordUrlOptions}\n * - {@link GenericUrlOptions}\n *\n * @summary Any of the option shapes `buildBaseURL` accepts, one per request `op` plus a generic `resourcePath` form.\n * @public\n */\nexport type UrlOptions =\n | FindRecordUrlOptions\n | QueryUrlOptions\n | FindManyUrlOptions\n | FindRelatedCollectionUrlOptions\n | FindRelatedResourceUrlOptions\n | CreateRecordUrlOptions\n | UpdateRecordUrlOptions\n | DeleteRecordUrlOptions\n | GenericUrlOptions;\n\nconst OPERATIONS_WITH_PRIMARY_RECORDS = new Set([\n 'findRecord',\n 'findRelatedRecord',\n 'findRelatedCollection',\n 'updateRecord',\n 'deleteRecord',\n]);\n\nfunction isOperationWithPrimaryRecord(\n options: UrlOptions\n): options is\n | FindRecordUrlOptions\n | FindRelatedCollectionUrlOptions\n | FindRelatedResourceUrlOptions\n | UpdateRecordUrlOptions\n | DeleteRecordUrlOptions {\n return 'op' in options && OPERATIONS_WITH_PRIMARY_RECORDS.has(options.op);\n}\n\nfunction hasResourcePath(options: UrlOptions): options is GenericUrlOptions {\n return 'resourcePath' in options && typeof options.resourcePath === 'string' && options.resourcePath.length > 0;\n}\n\nfunction resourcePathForType(options: UrlOptions): string {\n assert(\n `resourcePathForType: You must pass a valid op as part of options`,\n 'op' in options && typeof options.op === 'string'\n );\n return options.op === 'findMany' ? options.identifiers[0].type : options.identifier.type;\n}\n\n/**\n * Builds a URL for a request based on the provided options.\n * Does not include support for building query params (see `buildQueryParams`)\n * so that it may be composed cleanly with other query-params strategies.\n *\n * See the [Builders](/guides/the-manual/requests/builders) guide for writing your own request\n * builders.\n *\n * Usage:\n *\n * ```ts\n * import { buildBaseURL } from '@warp-drive-mirror/utilities';\n *\n * const url = buildBaseURL({\n * host: 'https://api.example.com',\n * namespace: 'api/v1',\n * resourcePath: 'emberDevelopers',\n * op: 'query',\n * identifier: { type: 'ember-developer' }\n * });\n *\n * // => 'https://api.example.com/api/v1/emberDevelopers'\n * ```\n *\n * On the surface this may seem like a lot of work to do something simple, but\n * it is designed to be composable with other utilities and interfaces that the\n * average product engineer will never need to see or use.\n *\n * A few notes:\n *\n * - `resourcePath` is optional, but if it is not provided, `identifier.type` will be used.\n * - `host` and `namespace` are optional, but if they are not provided, the values globally\n * configured via `setBuildURLConfig` will be used.\n * - `op` is required and must be one of the following:\n * - 'findRecord' 'query' 'findMany' 'findRelatedCollection' 'findRelatedRecord'` 'createRecord' 'updateRecord' 'deleteRecord'\n * - Depending on the value of `op`, `identifier` or `identifiers` will be required.\n *\n * @summary Joins host, namespace, resource path, id, and relationship field into a request URL without query params.\n * @public\n */\nexport function buildBaseURL(urlOptions: UrlOptions): string {\n const options = Object.assign(\n {\n host: CONFIG.host,\n namespace: CONFIG.namespace,\n },\n urlOptions\n );\n assert(\n `buildBaseURL: You must pass \\`op\\` as part of options`,\n hasResourcePath(options) || (typeof options.op === 'string' && options.op.length > 0)\n );\n assert(\n `buildBaseURL: You must pass \\`identifier\\` as part of options`,\n hasResourcePath(options) ||\n options.op === 'findMany' ||\n (options.identifier && typeof options.identifier === 'object')\n );\n assert(\n `buildBaseURL: You must pass \\`identifiers\\` as part of options`,\n hasResourcePath(options) ||\n options.op !== 'findMany' ||\n (options.identifiers &&\n Array.isArray(options.identifiers) &&\n options.identifiers.length > 0 &&\n options.identifiers.every((i) => i && typeof i === 'object'))\n );\n assert(\n `buildBaseURL: You must pass valid \\`identifier\\` as part of options, expected 'id'`,\n hasResourcePath(options) ||\n !isOperationWithPrimaryRecord(options) ||\n (typeof options.identifier.id === 'string' && options.identifier.id.length > 0)\n );\n assert(\n `buildBaseURL: You must pass \\`identifiers\\` as part of options`,\n hasResourcePath(options) ||\n options.op !== 'findMany' ||\n options.identifiers.every((i) => typeof i.id === 'string' && i.id.length > 0)\n );\n assert(\n `buildBaseURL: You must pass valid \\`identifier\\` as part of options, expected 'type'`,\n hasResourcePath(options) ||\n options.op === 'findMany' ||\n (typeof options.identifier.type === 'string' && options.identifier.type.length > 0)\n );\n assert(\n `buildBaseURL: You must pass valid \\`identifiers\\` as part of options, expected 'type'`,\n hasResourcePath(options) ||\n options.op !== 'findMany' ||\n (typeof options.identifiers[0].type === 'string' && options.identifiers[0].type.length > 0)\n );\n\n // prettier-ignore\n const idPath: string =\n isOperationWithPrimaryRecord(options) ? encodeURIComponent(options.identifier.id)\n : '';\n const resourcePath = options.resourcePath || resourcePathForType(options);\n const { host, namespace } = options;\n const fieldPath = 'fieldPath' in options ? options.fieldPath : '';\n\n assert(\n `buildBaseURL: You tried to build a url for a ${String(\n 'op' in options ? options.op + ' ' : ''\n )}request to ${resourcePath} but resourcePath must be set or op must be one of \"${[\n 'findRecord',\n 'findRelatedRecord',\n 'findRelatedCollection',\n 'updateRecord',\n 'deleteRecord',\n 'createRecord',\n 'query',\n 'findMany',\n ].join('\",\"')}\".`,\n hasResourcePath(options) ||\n [\n 'findRecord',\n 'query',\n 'findMany',\n 'findRelatedCollection',\n 'findRelatedRecord',\n 'createRecord',\n 'updateRecord',\n 'deleteRecord',\n ].includes(options.op)\n );\n\n assert(`buildBaseURL: host must NOT end with '/', received '${host}'`, host === '/' || !host.endsWith('/'));\n assert(`buildBaseURL: namespace must NOT start with '/', received '${namespace}'`, !namespace.startsWith('/'));\n assert(`buildBaseURL: namespace must NOT end with '/', received '${namespace}'`, !namespace.endsWith('/'));\n assert(\n `buildBaseURL: resourcePath must NOT start with '/', received '${resourcePath}'`,\n !resourcePath.startsWith('/')\n );\n assert(`buildBaseURL: resourcePath must NOT end with '/', received '${resourcePath}'`, !resourcePath.endsWith('/'));\n assert(`buildBaseURL: fieldPath must NOT start with '/', received '${fieldPath}'`, !fieldPath.startsWith('/'));\n assert(`buildBaseURL: fieldPath must NOT end with '/', received '${fieldPath}'`, !fieldPath.endsWith('/'));\n assert(`buildBaseURL: idPath must NOT start with '/', received '${idPath}'`, !idPath.startsWith('/'));\n assert(`buildBaseURL: idPath must NOT end with '/', received '${idPath}'`, !idPath.endsWith('/'));\n\n const hasHost = host !== '' && host !== '/';\n const url = [hasHost ? host : '', namespace, resourcePath, idPath, fieldPath].filter(Boolean).join('/');\n return hasHost ? url : `/${url}`;\n}\n\nconst DEFAULT_QUERY_PARAMS_SERIALIZATION_OPTIONS: QueryParamsSerializationOptions = {\n arrayFormat: 'comma',\n};\n\nfunction handleInclude(include: string | string[]): string[] {\n assert(\n `Expected include to be a string or array, got ${typeof include}`,\n typeof include === 'string' || Array.isArray(include)\n );\n return typeof include === 'string' ? include.split(',') : include;\n}\n\n/**\n * filter out keys of an object that have falsy values or point to empty arrays\n * returning a new object with only those keys that have truthy values / non-empty arrays\n *\n * See [Cache Keys for Requests](/guides/the-manual/requests/builders#cache-keys-for-requests) for\n * why builders need stable query params.\n *\n * @summary Returns a copy of an object without keys whose values are `undefined`, `null`, empty strings, or empty\n * arrays.\n * @public\n * @param source object to filter keys with empty values from\n * @return A new object with the keys that contained empty values removed\n */\nexport function filterEmpty(source: Record<string, Serializable>): Record<string, Serializable> {\n const result: Record<string, Serializable> = {};\n for (const key in source) {\n const value = source[key];\n // Allow `0` and `false` but filter falsy values that indicate \"empty\"\n if (value !== undefined && value !== null && value !== '') {\n if (!Array.isArray(value) || value.length > 0) {\n result[key] = source[key];\n }\n }\n }\n return result;\n}\n\n/**\n * Sorts query params by both key and value returning a new URLSearchParams\n * object with the keys inserted in sorted order.\n *\n * See [Cache Keys for Requests](/guides/the-manual/requests/builders#cache-keys-for-requests) for\n * why builders need stable query params.\n *\n * Treats `included` specially, splicing it into an array if it is a string and sorting the array.\n *\n * The `params` given are never mutated: neither the object itself nor any array\n * value it holds is reordered or replaced, so passing state that is also\n * rendered (a tracked array of filter values, for instance) is safe.\n *\n * Options:\n * - arrayFormat: 'bracket' | 'indices' | 'repeat' | 'comma'\n *\n * 'bracket': appends [] to the key for every value e.g. `&ids[]=1&ids[]=2`\n * 'indices': appends [i] to the key for every value e.g. `&ids[0]=1&ids[1]=2`\n * 'repeat': appends the key for every value e.g. `&ids=1&ids=2`\n * 'comma' (default): appends the key once with a comma separated list of values e.g. `&ids=1,2`\n *\n * @summary Builds a `URLSearchParams` with keys and array values sorted, so equivalent queries produce identical\n * params.\n * @public\n * @return A {@link URLSearchParams} with keys inserted in sorted order\n */\nexport function sortQueryParams(params: QueryParamsSource, options?: QueryParamsSerializationOptions): URLSearchParams {\n const opts = Object.assign({}, DEFAULT_QUERY_PARAMS_SERIALIZATION_OPTIONS, options);\n const paramsIsObject = !(params instanceof URLSearchParams);\n const urlParams = new URLSearchParams();\n // shallow copy so that normalizing `include` below does not write back onto\n // the caller's object. Array values are still shared with the caller, so\n // everything downstream must sort copies rather than sorting in place.\n const dictionaryParams: Record<string, Serializable> = paramsIsObject ? Object.assign({}, params) : {};\n\n if (!paramsIsObject) {\n params.forEach((value, key) => {\n const hasExisting = key in dictionaryParams;\n if (!hasExisting) {\n dictionaryParams[key] = value;\n } else {\n const existingValue = dictionaryParams[key];\n if (Array.isArray(existingValue)) {\n existingValue.push(value);\n } else {\n dictionaryParams[key] = [existingValue, value];\n }\n }\n });\n }\n\n if ('include' in dictionaryParams) {\n dictionaryParams.include = handleInclude(dictionaryParams.include as string | string[]);\n }\n\n const sortedKeys = Object.keys(dictionaryParams).sort();\n sortedKeys.forEach((key) => {\n const value = dictionaryParams[key];\n if (Array.isArray(value)) {\n const sortedValue = value.toSorted();\n switch (opts.arrayFormat) {\n case 'indices':\n sortedValue.forEach((v, i) => {\n urlParams.append(`${key}[${i}]`, String(v));\n });\n return;\n case 'bracket':\n sortedValue.forEach((v) => {\n urlParams.append(`${key}[]`, String(v));\n });\n return;\n case 'repeat':\n sortedValue.forEach((v) => {\n urlParams.append(key, String(v));\n });\n return;\n case 'comma':\n default:\n urlParams.append(key, sortedValue.join(','));\n return;\n }\n } else {\n urlParams.append(key, String(value));\n }\n });\n\n return urlParams;\n}\n\n/**\n * Sorts query params by both key and value, returning a query params string\n *\n * See [Cache Keys for Requests](/guides/the-manual/requests/builders#cache-keys-for-requests) for\n * why builders need stable query params.\n *\n * Treats `included` specially, splicing it into an array if it is a string and sorting the array.\n *\n * The `params` given are never mutated: neither the object itself nor any array\n * value it holds is reordered or replaced, so passing state that is also\n * rendered (a tracked array of filter values, for instance) is safe.\n *\n * Options:\n * - arrayFormat: 'bracket' | 'indices' | 'repeat' | 'comma'\n *\n * 'bracket': appends [] to the key for every value e.g. `ids[]=1&ids[]=2`\n * 'indices': appends [i] to the key for every value e.g. `ids[0]=1&ids[1]=2`\n * 'repeat': appends the key for every value e.g. `ids=1&ids=2`\n * 'comma' (default): appends the key once with a comma separated list of values e.g. `ids=1,2`\n *\n * @summary Serializes query params into a string with keys and array values sorted, so equivalent queries share one\n * URL.\n * @public\n * @return A sorted query params string without the leading `?`\n */\nexport function buildQueryParams(params: QueryParamsSource, options?: QueryParamsSerializationOptions): string {\n return sortQueryParams(params, options).toString();\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/-private/string/inflections.ts
|
|
2
2
|
const defaultRules = {
|
|
3
3
|
plurals: [
|
|
4
4
|
[/$/, "s"],
|
|
@@ -75,6 +75,5 @@ const defaultRules = {
|
|
|
75
75
|
]
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
//#endregion
|
|
79
78
|
export { defaultRules as t };
|
|
80
|
-
//# sourceMappingURL=inflections-
|
|
79
|
+
//# sourceMappingURL=inflections-CWdUh2K_.js.map
|
package/dist/unpkg/dev-deprecated/{inflections-DzBsjJqj.js.map → inflections-CWdUh2K_.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inflections-
|
|
1
|
+
{"version":3,"file":"inflections-CWdUh2K_.js","names":[],"sources":["../../../src/-private/string/inflections.ts"],"sourcesContent":["export type RulesArray = Array<[RegExp, string]>;\ntype DefaultRulesType = {\n plurals: RulesArray;\n singular: RulesArray;\n irregularPairs: Array<[string, string]>;\n uncountable: string[];\n};\n\nexport const defaultRules: DefaultRulesType = {\n plurals: [\n [/$/, 's'],\n [/s$/i, 's'],\n [/^(ax|test)is$/i, '$1es'],\n [/(octop|vir)us$/i, '$1i'],\n [/(octop|vir)i$/i, '$1i'],\n [/(alias|status|bonus)$/i, '$1es'],\n [/(tu|bu)s$/i, '$1ses'],\n [/(buffal|tomat)o$/i, '$1oes'],\n [/([ti])um$/i, '$1a'],\n [/([ti])a$/i, '$1a'],\n [/sis$/i, 'ses'],\n [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],\n [/(hive)$/i, '$1s'],\n [/([^aeiouy]|qu)y$/i, '$1ies'],\n [/(x|ch|ss|sh)$/i, '$1es'],\n [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],\n [/^(m|l)ouse$/i, '$1ice'],\n [/^(m|l)ice$/i, '$1ice'],\n [/^(ox)$/i, '$1en'],\n [/^(oxen)$/i, '$1'],\n [/(quiz)$/i, '$1zes'],\n ],\n\n singular: [\n [/s$/i, ''],\n [/(ss)$/i, '$1'],\n [/(n)ews$/i, '$1ews'],\n [/([ti])a$/i, '$1um'],\n [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],\n [/(^analy)(sis|ses)$/i, '$1sis'],\n [/([^f])ves$/i, '$1fe'],\n [/(hive)s$/i, '$1'],\n [/(tive)s$/i, '$1'],\n [/([lr])ves$/i, '$1f'],\n [/([^aeiouy]|qu)ies$/i, '$1y'],\n [/(s)eries$/i, '$1eries'],\n [/(m)ovies$/i, '$1ovie'],\n [/(x|ch|ss|sh)es$/i, '$1'],\n [/^(m|l)ice$/i, '$1ouse'],\n [/(tus|bus)(es)?$/i, '$1'],\n [/(o)es$/i, '$1'],\n [/(shoe)s$/i, '$1'],\n [/(cris|test)(is|es)$/i, '$1is'],\n [/^(a)x[ie]s$/i, '$1xis'],\n [/(octop|vir)(us|i)$/i, '$1us'],\n [/(alias|status|bonus)(es)?$/i, '$1'],\n [/^(ox)en/i, '$1'],\n [/(vert|ind)ices$/i, '$1ex'],\n [/(matr)ices$/i, '$1ix'],\n [/(quiz)zes$/i, '$1'],\n [/(database)s$/i, '$1'],\n ],\n\n irregularPairs: [\n ['person', 'people'],\n ['man', 'men'],\n ['child', 'children'],\n ['sex', 'sexes'],\n ['move', 'moves'],\n ['cow', 'kine'],\n ['zombie', 'zombies'],\n ],\n\n uncountable: ['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep', 'jeans', 'police'],\n};\n"],"mappings":";AAQA,MAAa,eAAiC;CAC5C,SAAS;EACP,CAAC,KAAK,GAAG;EACT,CAAC,OAAO,GAAG;EACX,CAAC,kBAAkB,MAAM;EACzB,CAAC,mBAAmB,KAAK;EACzB,CAAC,kBAAkB,KAAK;EACxB,CAAC,0BAA0B,MAAM;EACjC,CAAC,cAAc,OAAO;EACtB,CAAC,qBAAqB,OAAO;EAC7B,CAAC,cAAc,KAAK;EACpB,CAAC,aAAa,KAAK;EACnB,CAAC,SAAS,KAAK;EACf,CAAC,0BAA0B,SAAS;EACpC,CAAC,YAAY,KAAK;EAClB,CAAC,qBAAqB,OAAO;EAC7B,CAAC,kBAAkB,MAAM;EACzB,CAAC,8BAA8B,QAAQ;EACvC,CAAC,gBAAgB,OAAO;EACxB,CAAC,eAAe,OAAO;EACvB,CAAC,WAAW,MAAM;EAClB,CAAC,aAAa,IAAI;EAClB,CAAC,YAAY,OAAO;CACtB;CAEA,UAAU;EACR,CAAC,OAAO,EAAE;EACV,CAAC,UAAU,IAAI;EACf,CAAC,YAAY,OAAO;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,wEAAwE,OAAO;EAChF,CAAC,uBAAuB,OAAO;EAC/B,CAAC,eAAe,MAAM;EACtB,CAAC,aAAa,IAAI;EAClB,CAAC,aAAa,IAAI;EAClB,CAAC,eAAe,KAAK;EACrB,CAAC,uBAAuB,KAAK;EAC7B,CAAC,cAAc,SAAS;EACxB,CAAC,cAAc,QAAQ;EACvB,CAAC,oBAAoB,IAAI;EACzB,CAAC,eAAe,QAAQ;EACxB,CAAC,oBAAoB,IAAI;EACzB,CAAC,WAAW,IAAI;EAChB,CAAC,aAAa,IAAI;EAClB,CAAC,wBAAwB,MAAM;EAC/B,CAAC,gBAAgB,OAAO;EACxB,CAAC,uBAAuB,MAAM;EAC9B,CAAC,+BAA+B,IAAI;EACpC,CAAC,YAAY,IAAI;EACjB,CAAC,oBAAoB,MAAM;EAC3B,CAAC,gBAAgB,MAAM;EACvB,CAAC,eAAe,IAAI;EACpB,CAAC,iBAAiB,IAAI;CACxB;CAEA,gBAAgB;EACd,CAAC,UAAU,QAAQ;EACnB,CAAC,OAAO,KAAK;EACb,CAAC,SAAS,UAAU;EACpB,CAAC,OAAO,OAAO;EACf,CAAC,QAAQ,OAAO;EAChB,CAAC,OAAO,MAAM;EACd,CAAC,UAAU,SAAS;CACtB;CAEA,aAAa;EAAC;EAAa;EAAe;EAAQ;EAAS;EAAW;EAAU;EAAQ;EAAS;EAAS;CAAQ;AACpH"}
|