From 12acd722985be49094cb74865d96e7cb76c537c3 Mon Sep 17 00:00:00 2001 From: zirkelc Date: Fri, 11 Oct 2024 09:07:17 +0200 Subject: [PATCH] feat: s3 dynamic keys --- .changeset/modern-pets-decide.md | 5 + .vscode/settings.json | 8 +- biome.json | 2 +- examples/s3-store/index.ts | 7 +- package.json | 2 +- packages/store-s3/README.md | 2 +- packages/store-s3/src/store.ts | 63 ++++-- packages/store-s3/src/utils.ts | 1 - packages/store-s3/tests/store.unit.test.ts | 230 ++++++++++++++++----- pnpm-lock.yaml | 122 +++++++---- 10 files changed, 323 insertions(+), 119 deletions(-) create mode 100644 .changeset/modern-pets-decide.md diff --git a/.changeset/modern-pets-decide.md b/.changeset/modern-pets-decide.md new file mode 100644 index 0000000..122424c --- /dev/null +++ b/.changeset/modern-pets-decide.md @@ -0,0 +1,5 @@ +--- +"middy-store-s3": patch +--- + +feat: allow dynamic keys from payload diff --git a/.vscode/settings.json b/.vscode/settings.json index 08d2670..61ede4f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,11 @@ "typescript.tsdk": "node_modules/typescript/lib", "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "quickfix.biome": "explicit" - // "source.organizeImports.biome": "explicit" + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "editor.defaultFormatter": "biomejs.biome", + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" } } diff --git a/biome.json b/biome.json index cce79aa..c2e05d4 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", "vcs": { "clientKind": "git", "enabled": true, diff --git a/examples/s3-store/index.ts b/examples/s3-store/index.ts index d2c0168..0999b79 100644 --- a/examples/s3-store/index.ts +++ b/examples/s3-store/index.ts @@ -20,6 +20,7 @@ import { S3Store } from "middy-store-s3"; const context = {} as Context; type Payload = { + id: string; random: string; }; @@ -44,13 +45,13 @@ try { ); } -const s3Store = new S3Store({ +const s3Store = new S3Store({ /* Config is optional */ config: { region }, /* Bucket is required */ bucket, /* Key is optional and defaults to randomUUID() */ - key: () => randomUUID(), + key: ({ payload }) => payload.id, }); const handler1 = middy() @@ -61,6 +62,8 @@ const handler1 = middy() ) .handler(async (input) => { return { + /* Generate a random ID to be used as the key in S3 */ + id: randomUUID(), /* Generate a random payload of size 512kb */ random: randomBytes(Sizes.kb(512) /* 512kb */).toString("hex"), }; diff --git a/package.json b/package.json index d434128..d375a63 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "changeset:publish": "changeset publish" }, "devDependencies": { - "@biomejs/biome": "^1.8.3", + "@biomejs/biome": "^1.9.3", "@changesets/changelog-github": "^0.5.0", "@changesets/cli": "^2.27.7", "@changesets/get-github-info": "^0.6.0", diff --git a/packages/store-s3/README.md b/packages/store-s3/README.md index 9d93e65..ecfdc02 100644 --- a/packages/store-s3/README.md +++ b/packages/store-s3/README.md @@ -24,7 +24,7 @@ const handler = middy() new S3Store({ config: { region: "us-east-1" }, bucket: "bucket", - key: () => randomUUID(), + key: ({ payload }) => randomUUID(), format: "arn", }), ], diff --git a/packages/store-s3/src/store.ts b/packages/store-s3/src/store.ts index cb770ea..df7ff22 100644 --- a/packages/store-s3/src/store.ts +++ b/packages/store-s3/src/store.ts @@ -34,35 +34,74 @@ export interface S3ObjectReference { region?: string; } -export interface S3StoreOptions { +type S3KeyArgs = { payload: TPayload }; + +/** + * The options for the `S3Store`. + */ +export interface S3StoreOptions { + /** + * The S3 client configuration. + * Can be a static object or a function that returns the configuration. + */ config?: S3ClientConfig | (() => S3ClientConfig); + /** + * The name of the bucket to store the payload in. + * Can be a static string or a function that returns the bucket name. + */ bucket: string | (() => string); - key?: string | (() => string); + /** + * The key to store the payload in the bucket. + * Can be a static string or a function that receives the payload as an argument and returns a string. + * Defaults to `randomUUID()`. + * + * @example + * ```typescript + * { + * key: ({ payload }) => `${payload.id}` + * } + * ``` + */ + key?: string | ((args: S3KeyArgs) => string); + /** + * The format of the S3 reference. + * Defaults to the `url-s3-global-path` format: `s3:///<...keys>`. + */ format?: S3ReferenceFormat; + /** + * The maximum payload size in bytes that can be stored in S3. + * Defaults to `Infinity`. + */ maxSize?: number; + /** + * The logger function to use for logging. + * Defaults to no logging. + */ logger?: Logger; } export const STORE_NAME = "s3" as const; -export class S3Store implements StoreInterface { +export class S3Store + implements StoreInterface +{ readonly name = STORE_NAME; #config: () => S3ClientConfig; #bucket: () => string; - #key: () => string; + #key: (args: S3KeyArgs) => string; #logger: (...args: any[]) => void; #maxSize: number; #format: S3ReferenceFormat; - constructor(opts: S3StoreOptions) { + constructor(opts: S3StoreOptions) { this.#maxSize = opts.maxSize ?? Sizes.INFINITY; this.#logger = opts.logger ?? (() => {}); this.#format = opts.format ?? "url-s3-global-path"; this.#config = resolvableFn(opts.config ?? {}); this.#bucket = resolvableFn(opts.bucket); - this.#key = resolvableFn(opts.key ?? randomUUID); + this.#key = resolvableFn(opts.key ?? (() => randomUUID())); } canLoad(args: LoadArgs): boolean { @@ -84,7 +123,7 @@ export class S3Store implements StoreInterface { } } - async load(args: LoadArgs): Promise { + async load(args: LoadArgs): Promise { this.#logger("Loading payload"); const client = this.getClient(); @@ -101,7 +140,7 @@ export class S3Store implements StoreInterface { this.#logger(`Loaded payload from bucket ${bucket} and key ${key}`); - return payload; + return payload as TPayload; } canStore(args: StoreArgs): boolean { @@ -120,12 +159,12 @@ export class S3Store implements StoreInterface { return true; } - public async store(args: StoreArgs): Promise { + public async store(args: StoreArgs): Promise { this.#logger("Storing payload"); const { payload } = args; const bucket = this.getBucket(); - const key = this.getKey(); + const key = this.getKey({ payload }); const client = this.getClient(); this.#logger(`Storing payload to bucket ${bucket} and key ${key}`); @@ -165,8 +204,8 @@ export class S3Store implements StoreInterface { return bucket; } - private getKey(): string { - const key = this.#key(); + private getKey(args: S3KeyArgs): string { + const key = this.#key(args); if (!key) { this.#logger("Invalid key", { key }); throw new Error(`Invalid key: ${key}`); diff --git a/packages/store-s3/src/utils.ts b/packages/store-s3/src/utils.ts index 6ffe163..60e2ee7 100644 --- a/packages/store-s3/src/utils.ts +++ b/packages/store-s3/src/utils.ts @@ -1,4 +1,3 @@ -import { S3Client } from "@aws-sdk/client-s3"; import { type S3Object, type S3UrlFormat, diff --git a/packages/store-s3/tests/store.unit.test.ts b/packages/store-s3/tests/store.unit.test.ts index 35788b7..6f08166 100644 --- a/packages/store-s3/tests/store.unit.test.ts +++ b/packages/store-s3/tests/store.unit.test.ts @@ -1,6 +1,14 @@ import { ReadableStream } from "node:stream/web"; import { type GetObjectCommandOutput, S3Client } from "@aws-sdk/client-s3"; -import { beforeAll, describe, expect, test, vi } from "vitest"; +import { + type MockInstance, + beforeAll, + beforeEach, + describe, + expect, + test, + vi, +} from "vitest"; import { type S3ObjectReference, S3Store } from "../src/store.js"; const region = "us-east-1"; @@ -62,15 +70,18 @@ describe("S3Store.load", () => { }), ); - describe("should deserialize content by type", () => { + describe("deserialize content by type", () => { const s3Store = new S3Store({ config, bucket, key }); + test("text/plain", async () => { mockClient("foo", "text/plain"); await expect(s3Store.load({ reference: urlReference })).resolves.toEqual( "foo", ); + }); - mockClient("foo", "text/plain;charset=utf-8"); + test("text/plain; charset=utf-8", async () => { + mockClient("foo", "text/plain; charset=utf-8"); await expect(s3Store.load({ reference: urlReference })).resolves.toEqual( "foo", ); @@ -81,17 +92,19 @@ describe("S3Store.load", () => { await expect(s3Store.load({ reference: urlReference })).resolves.toEqual({ foo: "bar", }); + }); + test("application/json; charset=utf-8", async () => { mockClient( JSON.stringify({ foo: "bar" }), - "application/json;charset=utf-8", + "application/json; charset=utf-8", ); await expect(s3Store.load({ reference: urlReference })).resolves.toEqual({ foo: "bar", }); }); - test("unsupported type", async () => { + test("should throw if content type is unsupported", async () => { mockClient("foo", "unsupported/type"); await expect(async () => s3Store.load({ reference: urlReference }), @@ -174,72 +187,181 @@ describe("S3Store.canStore", () => { }); describe("S3Store.store", () => { - const s3Store = new S3Store({ config, bucket, key }); - const mockClient = () => vi .spyOn(S3Client.prototype, "send") .mockImplementation(() => Promise.resolve({})); - test("should serialize content by type", async () => { - const spy = mockClient(); + describe("build s3 key", () => { const byteSize = 1_000; + const payload = { foo: "bar" }; - spy.mockClear(); - await s3Store.store({ byteSize, payload: "foo" }); - expect(spy.mock.calls[0][0].input).toEqual({ - Bucket: "bucket", - Key: "key", - Body: "foo", - ContentType: "text/plain", + test("should use static key", async () => { + await expect( + new S3Store({ + config, + bucket, + key: "foo", + }).store({ + byteSize, + payload, + }), + ).resolves.toEqual(`s3://bucket/foo`); }); - spy.mockClear(); - await s3Store.store({ byteSize, payload: { foo: "bar" } }); - expect(spy.mock.calls[0][0].input).toEqual({ - Bucket: "bucket", - Key: "key", - Body: '{"foo":"bar"}', - ContentType: "application/json", + test("should default to randomUUID", async () => { + await expect( + new S3Store({ + config, + bucket, + key: undefined, + }).store({ + byteSize, + payload, + }), + ).resolves.toMatch(/^s3:\/\/bucket\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/); + }); + + test("should build key from payload", async () => { + await expect( + new S3Store({ + config, + bucket, + key: ({ payload }) => Object.keys(payload).join("/"), + }).store({ + byteSize, + payload, + }), + ).resolves.toEqual(`s3://bucket/foo`); }); - await expect(() => - s3Store.store({ byteSize, payload: undefined }), - ).rejects.toThrowError(); + test("should throw if key is invalid", async () => { + await expect(() => + new S3Store({ + config, + bucket, + key: "", + }).store({ + byteSize, + payload, + }), + ).rejects.toThrowError(); + + await expect(() => + new S3Store({ + config, + bucket, + key: ({ payload }) => "", + }).store({ + byteSize, + payload, + }), + ).rejects.toThrowError(); + + await expect(() => + new S3Store({ + config, + bucket, + key: ({ payload }) => null as any, + }).store({ + byteSize, + payload, + }), + ).rejects.toThrowError(); + + await expect(() => + new S3Store({ + config, + bucket, + key: ({ payload }) => undefined as any, + }).store({ + byteSize, + payload, + }), + ).rejects.toThrowError(); + }); }); - test("should put object and return reference", async () => { + describe("serialize content by type", () => { + const s3Store = new S3Store({ config, bucket, key }); const byteSize = 1_000; - const payload = { foo: "bar" }; + let spy: MockInstance; - await expect( - new S3Store({ config, bucket, key, format: "arn" }).store({ - byteSize, - payload, - }), - ).resolves.toEqual(`arn:aws:s3:::${bucket}/${key}`); + beforeEach(() => { + spy = mockClient(); + }); - await expect( - new S3Store({ config, bucket, key, format: "object" }).store({ - byteSize, - payload, - }), - ).resolves.toEqual({ store: "s3", bucket, key, region }); + test("text/plain", async () => { + await s3Store.store({ byteSize, payload: "foo" }); + expect(spy.mock.calls[0][0].input).toEqual({ + Bucket: "bucket", + Key: "key", + Body: "foo", + ContentType: "text/plain", + }); + }); - await expect( - new S3Store({ config, bucket, key, format: "url-s3-global-path" }).store({ - byteSize, - payload, - }), - ).resolves.toEqual(`s3://${bucket}/${key}`); + test("application/json", async () => { + await s3Store.store({ byteSize, payload: { foo: "bar" } }); + expect(spy.mock.calls[0][0].input).toEqual({ + Bucket: "bucket", + Key: "key", + Body: '{"foo":"bar"}', + ContentType: "application/json", + }); + }); - await expect( - new S3Store({ config, bucket, key, format: "url-s3-region-path" }).store({ - byteSize, - payload, - }), - ).resolves.toEqual( - `s3://s3.${config.region}.amazonaws.com/${bucket}/${key}`, - ); + test("should throw if payload is undefined", async () => { + await expect(() => + s3Store.store({ byteSize, payload: undefined }), + ).rejects.toThrowError(); + }); + }); + + describe("reference", () => { + test("should put object and return reference", async () => { + const byteSize = 1_000; + const payload = { foo: "bar" }; + + await expect( + new S3Store({ config, bucket, key, format: "arn" }).store({ + byteSize, + payload, + }), + ).resolves.toEqual(`arn:aws:s3:::${bucket}/${key}`); + + await expect( + new S3Store({ config, bucket, key, format: "object" }).store({ + byteSize, + payload, + }), + ).resolves.toEqual({ store: "s3", bucket, key, region }); + + await expect( + new S3Store({ + config, + bucket, + key, + format: "url-s3-global-path", + }).store({ + byteSize, + payload, + }), + ).resolves.toEqual(`s3://${bucket}/${key}`); + + await expect( + new S3Store({ + config, + bucket, + key, + format: "url-s3-region-path", + }).store({ + byteSize, + payload, + }), + ).resolves.toEqual( + `s3://s3.${config.region}.amazonaws.com/${bucket}/${key}`, + ); + }); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7287441..e80bf34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: devDependencies: '@biomejs/biome': - specifier: ^1.8.3 - version: 1.8.3 + specifier: ^1.9.3 + version: 1.9.3 '@changesets/changelog-github': specifier: ^0.5.0 version: 0.5.0 @@ -55,7 +55,7 @@ importers: version: 15.2.7 tsup: specifier: ^8.1.0 - version: 8.1.2(postcss@8.4.39)(tsx@4.16.2)(typescript@5.5.3)(yaml@2.4.5) + version: 8.1.2(postcss@8.4.47)(tsx@4.16.2)(typescript@5.5.3)(yaml@2.5.1) tsx: specifier: ^4.16.2 version: 4.16.2 @@ -359,55 +359,55 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@biomejs/biome@1.8.3': - resolution: {integrity: sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==} + '@biomejs/biome@1.9.3': + resolution: {integrity: sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@1.8.3': - resolution: {integrity: sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==} + '@biomejs/cli-darwin-arm64@1.9.3': + resolution: {integrity: sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@1.8.3': - resolution: {integrity: sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==} + '@biomejs/cli-darwin-x64@1.9.3': + resolution: {integrity: sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@1.8.3': - resolution: {integrity: sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==} + '@biomejs/cli-linux-arm64-musl@1.9.3': + resolution: {integrity: sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@1.8.3': - resolution: {integrity: sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==} + '@biomejs/cli-linux-arm64@1.9.3': + resolution: {integrity: sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@1.8.3': - resolution: {integrity: sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==} + '@biomejs/cli-linux-x64-musl@1.9.3': + resolution: {integrity: sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@1.8.3': - resolution: {integrity: sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==} + '@biomejs/cli-linux-x64@1.9.3': + resolution: {integrity: sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@1.8.3': - resolution: {integrity: sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==} + '@biomejs/cli-win32-arm64@1.9.3': + resolution: {integrity: sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@1.8.3': - resolution: {integrity: sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==} + '@biomejs/cli-win32-x64@1.9.3': + resolution: {integrity: sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -2034,6 +2034,9 @@ packages: picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2077,6 +2080,10 @@ packages: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + preferred-pm@3.1.4: resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} engines: {node: '>=10'} @@ -2231,6 +2238,10 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -2615,6 +2626,11 @@ packages: engines: {node: '>= 14'} hasBin: true + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3156,39 +3172,39 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@biomejs/biome@1.8.3': + '@biomejs/biome@1.9.3': optionalDependencies: - '@biomejs/cli-darwin-arm64': 1.8.3 - '@biomejs/cli-darwin-x64': 1.8.3 - '@biomejs/cli-linux-arm64': 1.8.3 - '@biomejs/cli-linux-arm64-musl': 1.8.3 - '@biomejs/cli-linux-x64': 1.8.3 - '@biomejs/cli-linux-x64-musl': 1.8.3 - '@biomejs/cli-win32-arm64': 1.8.3 - '@biomejs/cli-win32-x64': 1.8.3 - - '@biomejs/cli-darwin-arm64@1.8.3': + '@biomejs/cli-darwin-arm64': 1.9.3 + '@biomejs/cli-darwin-x64': 1.9.3 + '@biomejs/cli-linux-arm64': 1.9.3 + '@biomejs/cli-linux-arm64-musl': 1.9.3 + '@biomejs/cli-linux-x64': 1.9.3 + '@biomejs/cli-linux-x64-musl': 1.9.3 + '@biomejs/cli-win32-arm64': 1.9.3 + '@biomejs/cli-win32-x64': 1.9.3 + + '@biomejs/cli-darwin-arm64@1.9.3': optional: true - '@biomejs/cli-darwin-x64@1.8.3': + '@biomejs/cli-darwin-x64@1.9.3': optional: true - '@biomejs/cli-linux-arm64-musl@1.8.3': + '@biomejs/cli-linux-arm64-musl@1.9.3': optional: true - '@biomejs/cli-linux-arm64@1.8.3': + '@biomejs/cli-linux-arm64@1.9.3': optional: true - '@biomejs/cli-linux-x64-musl@1.8.3': + '@biomejs/cli-linux-x64-musl@1.9.3': optional: true - '@biomejs/cli-linux-x64@1.8.3': + '@biomejs/cli-linux-x64@1.9.3': optional: true - '@biomejs/cli-win32-arm64@1.8.3': + '@biomejs/cli-win32-arm64@1.9.3': optional: true - '@biomejs/cli-win32-x64@1.8.3': + '@biomejs/cli-win32-x64@1.9.3': optional: true '@changesets/apply-release-plan@7.0.4': @@ -4951,6 +4967,9 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.0: + optional: true + picomatch@2.3.1: {} pidtree@0.6.0: {} @@ -4963,13 +4982,13 @@ snapshots: dependencies: find-up: 4.1.0 - postcss-load-config@6.0.1(postcss@8.4.39)(tsx@4.16.2)(yaml@2.4.5): + postcss-load-config@6.0.1(postcss@8.4.47)(tsx@4.16.2)(yaml@2.5.1): dependencies: lilconfig: 3.1.2 optionalDependencies: - postcss: 8.4.39 + postcss: 8.4.47 tsx: 4.16.2 - yaml: 2.4.5 + yaml: 2.5.1 postcss@8.4.39: dependencies: @@ -4977,6 +4996,13 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postcss@8.4.47: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.1 + optional: true + preferred-pm@3.1.4: dependencies: find-up: 5.0.0 @@ -5141,6 +5167,9 @@ snapshots: source-map-js@1.2.0: {} + source-map-js@1.2.1: + optional: true + source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 @@ -5349,7 +5378,7 @@ snapshots: tslib@2.6.3: {} - tsup@8.1.2(postcss@8.4.39)(tsx@4.16.2)(typescript@5.5.3)(yaml@2.4.5): + tsup@8.1.2(postcss@8.4.47)(tsx@4.16.2)(typescript@5.5.3)(yaml@2.5.1): dependencies: bundle-require: 5.0.0(esbuild@0.23.0) cac: 6.7.14 @@ -5360,14 +5389,14 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 6.0.1(postcss@8.4.39)(tsx@4.16.2)(yaml@2.4.5) + postcss-load-config: 6.0.1(postcss@8.4.47)(tsx@4.16.2)(yaml@2.5.1) resolve-from: 5.0.0 rollup: 4.18.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.39 + postcss: 8.4.47 typescript: 5.5.3 transitivePeerDependencies: - jiti @@ -5550,6 +5579,9 @@ snapshots: yaml@2.4.5: {} + yaml@2.5.1: + optional: true + yocto-queue@0.1.0: {} zip-stream@4.1.1: