diff --git a/.changeset/migrate-ava-to-vitest.md b/.changeset/migrate-ava-to-vitest.md new file mode 100644 index 0000000..0b54aa6 --- /dev/null +++ b/.changeset/migrate-ava-to-vitest.md @@ -0,0 +1,5 @@ +--- +"@drupal-kit/core": patch +--- + +Migrate test runner from AVA to Vitest diff --git a/AGENTS.md b/AGENTS.md index 5950643..6d58e22 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,9 +34,9 @@ The update script installs Node 20 via nvm and prepends it to PATH in `~/.bashrc Turborepo handles dependency ordering: `@drupal-kit/core` must build before downstream plugins. Always use `pnpm build` (via turbo) rather than building individual packages manually, unless you know the dependency graph. -### Tests use MSW mocks +### Tests use Vitest and MSW mocks -All tests mock HTTP via MSW (Mock Service Worker). No real Drupal instance or network access is required for the test suite. +Packages run tests with Vitest (`vitest run`). Shared config lives in `@drupal-kit/config-vitest`. All HTTP is mocked via MSW (Mock Service Worker). No real Drupal instance or network access is required for the test suite. ### Shared skills diff --git a/packages/config/vitest/create-vitest-config.ts b/packages/config/vitest/create-vitest-config.ts new file mode 100644 index 0000000..d1fb3c0 --- /dev/null +++ b/packages/config/vitest/create-vitest-config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "vitest/config"; + +export function createVitestConfig() { + return defineConfig({ + test: { + environment: "node", + include: ["tests/**/*.test.ts"], + sequence: { + concurrent: false, + }, + snapshotFormat: { + printBasicPrototype: false, + }, + }, + }); +} diff --git a/packages/config/vitest/package.json b/packages/config/vitest/package.json new file mode 100644 index 0000000..1b6b2bc --- /dev/null +++ b/packages/config/vitest/package.json @@ -0,0 +1,14 @@ +{ + "name": "@drupal-kit/config-vitest", + "version": "0.13.4", + "type": "module", + "private": true, + "devDependencies": { + "@drupal-kit/config-typescript": "workspace:0.13.4", + "typescript": "5.4.5", + "vitest": "^3.2.4" + }, + "exports": { + "./vitest": "./create-vitest-config.ts" + } +} diff --git a/packages/config/vitest/tsconfig.json b/packages/config/vitest/tsconfig.json new file mode 100644 index 0000000..532514f --- /dev/null +++ b/packages/config/vitest/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../typescript/base.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["*.ts"] +} diff --git a/packages/consumers/package.json b/packages/consumers/package.json index 97ef488..f14f95c 100644 --- a/packages/consumers/package.json +++ b/packages/consumers/package.json @@ -8,39 +8,29 @@ }, "devDependencies": { "@drupal-kit/config-typescript": "workspace:0.13.4", + "@drupal-kit/config-vitest": "workspace:0.13.4", "@drupal-kit/eslint-config": "workspace:0.13.4", "@drupal-kit/types": "workspace:0.13.4", "@rollup/plugin-typescript": "^11.1.1", "@swc/core": "^1.6.5", - "ava": "^6.1.3", "esbuild": "^0.27.3", "msw": "^2.14.6", "rollup": "^4.59.0", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1", "ts-node": "^10.9.2", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "^3.2.4" }, "scripts": { "build": "rollup -c", "lint": "eslint --ext .ts,.tsx src --max-warnings 0", - "test": "NODE_NO_WARNINGS=1 ava", + "test": "NODE_NO_WARNINGS=1 vitest run", + "test:watch": "NODE_NO_WARNINGS=1 vitest", "typecheck": "pnpm run '/(typecheck:.*)/'", "typecheck:src": "tsc --project ./tsconfig.json", "typecheck:tests": "tsc --project ./tsconfig.tests.json" }, - "ava": { - "files": [ - "tests/**/*.test.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader", - "ts-node/esm" - ] - }, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/consumers/tests/plugin.test.ts b/packages/consumers/tests/plugin.test.ts index 0bf24cb..7e87a2d 100644 --- a/packages/consumers/tests/plugin.test.ts +++ b/packages/consumers/tests/plugin.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; import { Drupalkit, DrupalkitOptions } from "@drupal-kit/core"; @@ -7,15 +7,15 @@ import { DrupalkitConsumers } from "../src/index.js"; const server = setupServer(); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); @@ -36,12 +36,12 @@ const createDrupalkit = ( }); }; -test.serial("Add consumer id to request", async (t) => { - t.plan(2); +test("Add consumer id to request", async () => { + expect.assertions(2); server.use( http.get("*", ({ request }) => { - t.is(request.headers.get("X-Consumer-ID"), CONSUMER_ID); + expect(request.headers.get("X-Consumer-ID")).toBe(CONSUMER_ID); return HttpResponse.text(); }), @@ -56,38 +56,35 @@ test.serial("Add consumer id to request", async (t) => { method: "GET", }); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial( - "Do not add consumer id header if no value is supplied", - async (t) => { - t.plan(1); +test("Do not add consumer id header if no value is supplied", async () => { + expect.assertions(1); - server.use( - http.get("*", ({ request }) => { - t.is(request.headers.get("X-Consumer-ID"), null); + server.use( + http.get("*", ({ request }) => { + expect(request.headers.get("X-Consumer-ID")).toBe(null); - return HttpResponse.text(); - }), - ); + return HttpResponse.text(); + }), + ); - const drupalkit = createDrupalkit({ - baseUrl: BASE_URL, - }); + const drupalkit = createDrupalkit({ + baseUrl: BASE_URL, + }); - await drupalkit.request("/", { - method: "GET", - }); - }, -); + await drupalkit.request("/", { + method: "GET", + }); +}); -test.serial("Add consumer id with custom header name", async (t) => { - t.plan(1); +test("Add consumer id with custom header name", async () => { + expect.assertions(1); server.use( http.get("*", ({ request }) => { - t.is(request.headers.get("X-Custom-Consumer-ID"), CONSUMER_ID); + expect(request.headers.get("X-Custom-Consumer-ID")).toBe(CONSUMER_ID); return HttpResponse.text(); }), @@ -104,58 +101,52 @@ test.serial("Add consumer id with custom header name", async (t) => { }); }); -test.serial( - "Add consumer id to request - via deprecated consumerUUID", - async (t) => { - t.plan(2); +test("Add consumer id to request - via deprecated consumerUUID", async () => { + expect.assertions(2); - server.use( - http.get("*", ({ request }) => { - t.is(request.headers.get("X-Consumer-ID"), CONSUMER_ID); + server.use( + http.get("*", ({ request }) => { + expect(request.headers.get("X-Consumer-ID")).toBe(CONSUMER_ID); - return HttpResponse.text(); - }), - ); + return HttpResponse.text(); + }), + ); - const drupalkit = createDrupalkit({ - baseUrl: BASE_URL, - consumerUUID: CONSUMER_ID, - }); + const drupalkit = createDrupalkit({ + baseUrl: BASE_URL, + consumerUUID: CONSUMER_ID, + }); - const result = await drupalkit.request("/", { - method: "GET", - }); + const result = await drupalkit.request("/", { + method: "GET", + }); - t.assert(result.ok); - }, -); - -test.serial( - "Do not overwrite already existing consumer id header", - async (t) => { - const otherId = "other"; - t.plan(2); - - server.use( - http.get("*", ({ request }) => { - t.is(request.headers.get("X-Consumer-ID"), otherId); - - return HttpResponse.text(); - }), - ); - - const drupalkit = createDrupalkit({ - baseUrl: BASE_URL, - consumerUUID: CONSUMER_ID, - }); - - const result = await drupalkit.request("/", { - method: "GET", - headers: { - "X-Consumer-ID": otherId, - }, - }); - - t.assert(result.ok); - }, -); + expect(result.ok).toBeTruthy(); +}); + +test("Do not overwrite already existing consumer id header", async () => { + const otherId = "other"; + expect.assertions(2); + + server.use( + http.get("*", ({ request }) => { + expect(request.headers.get("X-Consumer-ID")).toBe(otherId); + + return HttpResponse.text(); + }), + ); + + const drupalkit = createDrupalkit({ + baseUrl: BASE_URL, + consumerUUID: CONSUMER_ID, + }); + + const result = await drupalkit.request("/", { + method: "GET", + headers: { + "X-Consumer-ID": otherId, + }, + }); + + expect(result.ok).toBeTruthy(); +}); diff --git a/packages/consumers/vitest.config.ts b/packages/consumers/vitest.config.ts new file mode 100644 index 0000000..e587732 --- /dev/null +++ b/packages/consumers/vitest.config.ts @@ -0,0 +1,3 @@ +import { createVitestConfig } from "@drupal-kit/config-vitest/vitest"; + +export default createVitestConfig(); diff --git a/packages/core/package.json b/packages/core/package.json index 6190c60..76e4eba 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -11,42 +11,32 @@ }, "devDependencies": { "@drupal-kit/config-typescript": "workspace:0.13.4", + "@drupal-kit/config-vitest": "workspace:0.13.4", "@drupal-kit/eslint-config": "workspace:0.13.4", "@drupal-kit/types": "workspace:0.13.4", "@rollup/plugin-typescript": "^11.1.1", "@swc/core": "^1.6.5", "@types/qs": "~6.9.15", "@wunderwerk/node-pkg-version-export": "1.0.0-beta.1", - "ava": "^6.1.3", "esbuild": "^0.27.3", "msw": "^2.14.6", "rollup": "^4.59.0", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1", "ts-node": "^10.9.2", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "^3.2.4" }, "scripts": { "build": "rollup -c", "export-version": "node-pkg-version-export src/version.ts", "lint": "eslint --ext .ts,.tsx src --max-warnings 0", - "test": "NODE_NO_WARNINGS=1 ava", + "test": "NODE_NO_WARNINGS=1 vitest run", + "test:watch": "NODE_NO_WARNINGS=1 vitest watch", "typecheck": "pnpm run '/(typecheck:.*)/'", "typecheck:src": "tsc --project ./tsconfig.json", "typecheck:tests": "tsc --project ./tsconfig.tests.json" }, - "ava": { - "files": [ - "tests/**/*.test.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader", - "ts-node/esm" - ] - }, "engines": { "node": ">=18" }, diff --git a/packages/core/src/DrupalkitError.ts b/packages/core/src/DrupalkitError.ts index b348198..aa1a859 100644 --- a/packages/core/src/DrupalkitError.ts +++ b/packages/core/src/DrupalkitError.ts @@ -62,10 +62,17 @@ export class DrupalkitError extends Error { super(message); // Maintains proper stack trace (only available on V8) - // @ts-expect-error - Error.captureStackTrace is not available in all browsers. - if (Error.captureStackTrace) { - // @ts-expect-error - Error.captureStackTrace is not available in all browsers. - Error.captureStackTrace(this, this.constructor); + const ErrorWithCapture = Error as ErrorConstructor & { + captureStackTrace?: ( + error: Error, + constructor: new (...args: unknown[]) => object, + ) => void; + }; + if (ErrorWithCapture.captureStackTrace) { + ErrorWithCapture.captureStackTrace( + this, + this.constructor as new (...args: unknown[]) => object, + ); } this.name = "HttpError"; diff --git a/packages/core/tests/core.test.ts b/packages/core/tests/core.test.ts index 76dcc14..55fa26b 100644 --- a/packages/core/tests/core.test.ts +++ b/packages/core/tests/core.test.ts @@ -1,16 +1,14 @@ -import test from "ava"; +import { expect, test } from "vitest"; import { Drupalkit } from "../src/index.js"; -test("Instanciate drupalkit", (t) => { +test("Instanciate drupalkit", () => { new Drupalkit({ baseUrl: "https://drupal-headless-boilerplate.ddev.site", }); - - t.pass(); }); -test("Deep merge plugins", (t) => { +test("Deep merge plugins", () => { const pluginOne = () => { return { oneUniq: true, @@ -33,21 +31,21 @@ test("Deep merge plugins", (t) => { const instance = new EnhancedDrupalkit({ baseUrl: "some-url" }); - t.assert(Object.keys(instance).includes("oneUniq")); - t.assert(Object.keys(instance).includes("twoUniq")); - t.assert(Object.keys(instance.shared).includes("propFromOne")); - t.assert(Object.keys(instance.shared).includes("propFromTwo")); + expect(Object.keys(instance).includes("oneUniq")).toBeTruthy(); + expect(Object.keys(instance).includes("twoUniq")).toBeTruthy(); + expect(Object.keys(instance.shared).includes("propFromOne")).toBeTruthy(); + expect(Object.keys(instance.shared).includes("propFromTwo")).toBeTruthy(); }); -test("Set agent", (t) => { +test("Set agent", () => { const instance = new Drupalkit({ baseUrl: "https://drupal-headless-boilerplate.ddev.site", }); - t.is(instance.agent, `drupal-kit/0.0.0-development`); + expect(instance.agent).toBe(`drupal-kit/0.0.0-development`); }); -test("Build url with query", (t) => { +test("Build url with query", () => { const instance = new Drupalkit({ baseUrl: "https://drupal-headless-boilerplate.ddev.site", }); @@ -59,8 +57,7 @@ test("Build url with query", (t) => { }, }); - t.is( - url, + expect(url).toBe( `https://drupal-headless-boilerplate.ddev.site/api/some-endpoint?limit=10&page=5`, ); }); diff --git a/packages/core/tests/requests.test.ts b/packages/core/tests/requests.test.ts index 97155a5..caa6e29 100644 --- a/packages/core/tests/requests.test.ts +++ b/packages/core/tests/requests.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; @@ -19,19 +19,19 @@ const server = setupServer( http.get("*/network-error", () => HttpResponse.error()), ); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); -test.serial("Make simple GET request", async (t) => { +test("Make simple GET request", async () => { const drupalkit = new Drupalkit({ baseUrl: BASE_URL, }); @@ -41,19 +41,19 @@ test.serial("Make simple GET request", async (t) => { headers: {}, }); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); const response = result.unwrap(); - t.is(response.status, 200); - t.assert(response.url.includes("/demo-endpoint")); - t.assert(response.url.startsWith(BASE_URL)); - t.deepEqual(response.data, DemoEndpointResponse); - t.assert("content-type" in response.headers); + expect(response.status).toBe(200); + expect(response.url.includes("/demo-endpoint")).toBeTruthy(); + expect(response.url.startsWith(BASE_URL)).toBeTruthy(); + expect(response.data).toEqual(DemoEndpointResponse); + expect("content-type" in response.headers).toBeTruthy(); }); -test.serial("Make request with payload", async (t) => { - t.plan(3); +test("Make request with payload", async () => { + expect.assertions(3); const headers = { "X-Custom": "value", @@ -65,8 +65,8 @@ test.serial("Make request with payload", async (t) => { server.use( http.post("*/demo-endpoint", async ({ request }) => { const payload = await request.json(); - t.deepEqual(payload, body); - t.deepEqual(request.headers.get("X-Custom"), headers["X-Custom"]); + expect(payload).toEqual(body); + expect(request.headers.get("X-Custom")).toEqual(headers["X-Custom"]); return HttpResponse.text(); }), @@ -82,10 +82,10 @@ test.serial("Make request with payload", async (t) => { body, }); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Add response data to result", async (t) => { +test("Add response data to result", async () => { const drupalkit = new Drupalkit({ baseUrl: BASE_URL, }); @@ -96,10 +96,10 @@ test.serial("Add response data to result", async (t) => { const { headers } = result.unwrap(); - t.is(headers["content-type"], "application/json"); + expect(headers["content-type"]).toBe("application/json"); }); -test.serial("Do not add response data for 204 and 205 responses", async (t) => { +test("Do not add response data for 204 and 205 responses", async () => { server.use( http.get("*/demo-endpoint-204", async () => { return HttpResponse.text(null, { status: 204 }); @@ -114,10 +114,10 @@ test.serial("Do not add response data for 204 and 205 responses", async (t) => { method: "GET", }); - t.is(result.unwrap().data, undefined); + expect(result.unwrap().data).toBe(undefined); }); -test.serial("Return drupalkit errors", async (t) => { +test("Return drupalkit errors", async () => { const drupalkit = new Drupalkit({ baseUrl: BASE_URL, }); @@ -127,15 +127,15 @@ test.serial("Return drupalkit errors", async (t) => { headers: {}, }); - t.assert(result.err); + expect(result.err).toBeTruthy(); const err = result.expectErr("Must be error"); - t.assert(err instanceof DrupalkitError); - t.is(err.statusCode, 404); + expect(err instanceof DrupalkitError).toBeTruthy(); + expect(err.statusCode).toBe(404); }); -test.serial("Append locale to url", async (t) => { +test("Append locale to url", async () => { http.get("*/en/demo-endpoint", () => { return HttpResponse.json(DemoEndpointResponse); }); @@ -153,10 +153,10 @@ test.serial("Append locale to url", async (t) => { const response = result.unwrap(); - t.assert(response.url.includes("/en/demo-endpoint")); + expect(response.url.includes("/en/demo-endpoint")).toBeTruthy(); }); -test.serial("Append overwritten locale to url", async (t) => { +test("Append overwritten locale to url", async () => { http.get("*/en/demo-endpoint", () => { return HttpResponse.json(DemoEndpointResponse); }); @@ -174,26 +174,26 @@ test.serial("Append overwritten locale to url", async (t) => { const response = result.unwrap(); - t.assert(response.url.includes("/en/demo-endpoint")); + expect(response.url.includes("/en/demo-endpoint")).toBeTruthy(); }); -test.serial("Execute hooks", async (t) => { - t.plan(5); +test("Execute hooks", async () => { + expect.assertions(5); const drupalkit = new Drupalkit({ baseUrl: BASE_URL, }); drupalkit.hook.before("request", (options) => { - t.is(options.baseUrl, BASE_URL); + expect(options.baseUrl).toBe(BASE_URL); }); drupalkit.hook.after("request", (result, options) => { - t.is(options.baseUrl, BASE_URL); + expect(options.baseUrl).toBe(BASE_URL); - t.is(result.status, 200); - t.deepEqual(result.data, DemoEndpointResponse); - t.is(result.headers["content-type"], "application/json"); + expect(result.status).toBe(200); + expect(result.data).toEqual(DemoEndpointResponse); + expect(result.headers["content-type"]).toBe("application/json"); }); await drupalkit.request("/demo-endpoint", { @@ -202,15 +202,15 @@ test.serial("Execute hooks", async (t) => { }); }); -test.serial("Add auth header if present", async (t) => { - t.plan(3); +test("Add auth header if present", async () => { + expect.assertions(3); const authHeaderValue = "Bearer 00000"; server.use( http.get( "*/demo-endpoint", ({ request }) => { - t.is(request.headers.get("authorization"), authHeaderValue); + expect(request.headers.get("authorization")).toBe(authHeaderValue); return HttpResponse.text(); }, @@ -235,7 +235,7 @@ test.serial("Add auth header if present", async (t) => { http.get( "*/demo-endpoint", ({ request }) => { - t.is(request.headers.get("authorization"), null); + expect(request.headers.get("authorization")).toBe(null); return HttpResponse.text(); }, @@ -256,7 +256,7 @@ test.serial("Add auth header if present", async (t) => { http.get( "*/demo-endpoint", ({ request }) => { - t.is(request.headers.get("authorization"), null); + expect(request.headers.get("authorization")).toBe(null); return HttpResponse.text(); }, @@ -269,7 +269,7 @@ test.serial("Add auth header if present", async (t) => { }); }); -test.serial("Handle network errors", async (t) => { +test("Handle network errors", async () => { const drupalkit = new Drupalkit({ baseUrl: BASE_URL, }); @@ -278,15 +278,15 @@ test.serial("Handle network errors", async (t) => { method: "GET", }); - t.assert(result.err); + expect(result.err).toBeTruthy(); const error = result.expectErr("Must be error"); - t.assert(error instanceof DrupalkitError); - t.is(error.response, undefined); + expect(error instanceof DrupalkitError).toBeTruthy(); + expect(error.response).toBe(undefined); }); -test.serial("Allow options overrides", async (t) => { - t.plan(3); +test("Allow options overrides", async () => { + expect.assertions(3); server.use( http.get("*/en/demo-endpoint", () => { @@ -300,8 +300,8 @@ test.serial("Allow options overrides", async (t) => { }); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "force-cache"); - t.deepEqual(options.next, { + expect(options.cache).toBe("force-cache"); + expect(options.next).toEqual({ revalidate: 5, tags: ["tag1", "user:3"], }); @@ -327,5 +327,5 @@ test.serial("Allow options overrides", async (t) => { const response = result.unwrap(); - t.assert(response.url.includes("/en/demo-endpoint")); + expect(response.url.includes("/en/demo-endpoint")).toBeTruthy(); }); diff --git a/packages/core/tests/utils.test.ts b/packages/core/tests/utils.test.ts index ea5d0fe..8a4cd45 100644 --- a/packages/core/tests/utils.test.ts +++ b/packages/core/tests/utils.test.ts @@ -1,15 +1,15 @@ -import test from "ava"; +import { expect, test } from "vitest"; import { trimSlashesFromSegment } from "../src/utils.js"; -test("Trim slashes from segment", (t) => { - t.is(trimSlashesFromSegment("/"), ""); - t.is(trimSlashesFromSegment("/test"), "test"); - t.is(trimSlashesFromSegment("test/"), "test"); - t.is(trimSlashesFromSegment("test/test"), "test/test"); - t.is(trimSlashesFromSegment("//test"), "test"); - t.is(trimSlashesFromSegment("///test"), "test"); - t.is(trimSlashesFromSegment("///test/"), "test"); - t.is(trimSlashesFromSegment("test//"), "test"); - t.is(trimSlashesFromSegment("test//test"), "test//test"); +test("Trim slashes from segment", () => { + expect(trimSlashesFromSegment("/")).toBe(""); + expect(trimSlashesFromSegment("/test")).toBe("test"); + expect(trimSlashesFromSegment("test/")).toBe("test"); + expect(trimSlashesFromSegment("test/test")).toBe("test/test"); + expect(trimSlashesFromSegment("//test")).toBe("test"); + expect(trimSlashesFromSegment("///test")).toBe("test"); + expect(trimSlashesFromSegment("///test/")).toBe("test"); + expect(trimSlashesFromSegment("test//")).toBe("test"); + expect(trimSlashesFromSegment("test//test")).toBe("test//test"); }); diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts new file mode 100644 index 0000000..e587732 --- /dev/null +++ b/packages/core/vitest.config.ts @@ -0,0 +1,3 @@ +import { createVitestConfig } from "@drupal-kit/config-vitest/vitest"; + +export default createVitestConfig(); diff --git a/packages/jsonapi/package.json b/packages/jsonapi/package.json index 670af94..41a629e 100644 --- a/packages/jsonapi/package.json +++ b/packages/jsonapi/package.json @@ -12,39 +12,29 @@ }, "devDependencies": { "@drupal-kit/config-typescript": "workspace:0.13.4", + "@drupal-kit/config-vitest": "workspace:0.13.4", "@drupal-kit/eslint-config": "workspace:0.13.4", "@drupal-kit/types": "workspace:0.13.4", "@rollup/plugin-typescript": "^11.1.1", "@swc/core": "^1.6.5", - "ava": "^6.1.3", "esbuild": "^0.27.3", "msw": "^2.14.6", "rollup": "^4.59.0", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1", "ts-node": "^10.9.2", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "^3.2.4" }, "scripts": { "build": "rollup -c && node scripts/fix-type-declaration.mjs", "lint": "eslint --ext .ts,.tsx src --max-warnings 0", - "test": "NODE_NO_WARNINGS=1 ava", + "test": "NODE_NO_WARNINGS=1 vitest run", + "test:watch": "NODE_NO_WARNINGS=1 vitest", "typecheck": "pnpm run '/(typecheck:.*)/'", "typecheck:src": "tsc --project ./tsconfig.json", "typecheck:tests": "tsc --project ./tsconfig.tests.json" }, - "ava": { - "files": [ - "tests/**/*.test.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader", - "ts-node/esm" - ] - }, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/jsonapi/tests/DrupalkitJsonApiError.test.ts b/packages/jsonapi/tests/DrupalkitJsonApiError.test.ts index 74f9b09..bea5700 100644 --- a/packages/jsonapi/tests/DrupalkitJsonApiError.test.ts +++ b/packages/jsonapi/tests/DrupalkitJsonApiError.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { expect, test } from "vitest"; import { DrupalkitError, UNKNOWN_ERROR_PREFIX } from "@drupal-kit/core"; import { DrupalkitJsonApiError } from "../src/index.js"; @@ -13,18 +13,18 @@ const request = { baseUrl: "https://example.com", }; -test("Instanciate from DrupalkitError", (t) => { +test("Instanciate from DrupalkitError", () => { const error = new DrupalkitError("test-error", 400, { request, }); const jaError = DrupalkitJsonApiError.fromDrupalkitError(error); - t.assert(jaError instanceof DrupalkitJsonApiError); - t.assert(jaError instanceof DrupalkitError); + expect(jaError instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(jaError instanceof DrupalkitError).toBeTruthy(); }); -test("Extract errors from JSON:API response", (t) => { +test("Extract errors from JSON:API response", () => { const error = new DrupalkitError("test-error", 400, { request, response: { @@ -39,20 +39,22 @@ test("Extract errors from JSON:API response", (t) => { const jaError = DrupalkitJsonApiError.fromDrupalkitError(error); - t.assert(jaError.hasValidationErrors()); - t.deepEqual(jaError.getInvalidFields(), [ + expect(jaError.hasValidationErrors()).toBeTruthy(); + expect(jaError.getInvalidFields()).toEqual([ "field_firstname", "field_lastname", ]); - t.is(jaError.getErrorsByStatus(400).length, 1); - t.is(jaError.getErrorsByStatus(422).length, 2); + expect(jaError.getErrorsByStatus(400).length).toBe(1); + expect(jaError.getErrorsByStatus(422).length).toBe(2); - t.snapshot(jaError.getErrorsByStatus(400), "bad-request-errors"); - t.snapshot(jaError.getErrorsByStatus(422), "unprocessable-entity-errors"); + expect(jaError.getErrorsByStatus(400)).toMatchSnapshot("bad-request-errors"); + expect(jaError.getErrorsByStatus(422)).toMatchSnapshot( + "unprocessable-entity-errors", + ); }); -test("Unknown error message is replaced with JSON:API error detail", (t) => { +test("Unknown error message is replaced with JSON:API error detail", () => { const unknownMessage = `${UNKNOWN_ERROR_PREFIX} {"some":"data"}`; const error = new DrupalkitError(unknownMessage, 400, { request, @@ -76,10 +78,10 @@ test("Unknown error message is replaced with JSON:API error detail", (t) => { const jaError = DrupalkitJsonApiError.fromDrupalkitError(error); - t.is(jaError.message, "The specific error detail from JSON:API"); + expect(jaError.message).toBe("The specific error detail from JSON:API"); }); -test("Unknown error message falls back to JSON:API error title when detail is missing", (t) => { +test("Unknown error message falls back to JSON:API error title when detail is missing", () => { const unknownMessage = `${UNKNOWN_ERROR_PREFIX} {"some":"data"}`; const error = new DrupalkitError(unknownMessage, 400, { request, @@ -102,10 +104,10 @@ test("Unknown error message falls back to JSON:API error title when detail is mi const jaError = DrupalkitJsonApiError.fromDrupalkitError(error); - t.is(jaError.message, "Bad Request Title"); + expect(jaError.message).toBe("Bad Request Title"); }); -test("Unknown error message is kept when no JSON:API errors are present", (t) => { +test("Unknown error message is kept when no JSON:API errors are present", () => { const unknownMessage = `${UNKNOWN_ERROR_PREFIX} {"some":"data"}`; const error = new DrupalkitError(unknownMessage, 500, { request, @@ -121,10 +123,10 @@ test("Unknown error message is kept when no JSON:API errors are present", (t) => const jaError = DrupalkitJsonApiError.fromDrupalkitError(error); - t.is(jaError.message, unknownMessage); + expect(jaError.message).toBe(unknownMessage); }); -test("Non-unknown error message is preserved even with JSON:API errors", (t) => { +test("Non-unknown error message is preserved even with JSON:API errors", () => { const customMessage = "Custom error message"; const error = new DrupalkitError(customMessage, 400, { request, @@ -148,5 +150,5 @@ test("Non-unknown error message is preserved even with JSON:API errors", (t) => const jaError = DrupalkitJsonApiError.fromDrupalkitError(error); - t.is(jaError.message, customMessage); + expect(jaError.message).toBe(customMessage); }); diff --git a/packages/jsonapi/tests/__snapshots__/DrupalkitJsonApiError.test.ts.snap b/packages/jsonapi/tests/__snapshots__/DrupalkitJsonApiError.test.ts.snap index 64ac406..3f67d02 100644 --- a/packages/jsonapi/tests/__snapshots__/DrupalkitJsonApiError.test.ts.snap +++ b/packages/jsonapi/tests/__snapshots__/DrupalkitJsonApiError.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`DrupalkitJsonApiError should extract errors from JSON:API response: bad-request-errors 1`] = ` +exports[`Extract errors from JSON:API response > bad-request-errors 1`] = ` [ { "detail": "\`wrong-field\` is not a valid relationship field name. Possible values: node_type, revision_uid, uid, menu_link, field_media.", @@ -18,7 +18,7 @@ exports[`DrupalkitJsonApiError should extract errors from JSON:API response: bad ] `; -exports[`DrupalkitJsonApiError should extract errors from JSON:API response: unprocessable-entity-errors 1`] = ` +exports[`Extract errors from JSON:API response > unprocessable-entity-errors 1`] = ` [ { "detail": "field_firstname: Dieser Wert sollte nicht leer sein.", diff --git a/packages/jsonapi/tests/__snapshots__/menu.test.ts.snap b/packages/jsonapi/tests/__snapshots__/menu.test.ts.snap new file mode 100644 index 0000000..1475be4 --- /dev/null +++ b/packages/jsonapi/tests/__snapshots__/menu.test.ts.snap @@ -0,0 +1,48 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Get menu items 1`] = ` +{ + "data": [ + { + "attributes": { + "description": null, + "enabled": true, + "expanded": false, + "menu_name": "my_menu", + "meta": { + "entity_id": "2", + }, + "options": [], + "parent": "", + "provider": "menu_link_content", + "route": { + "name": "entity.node.canonical", + "parameters": { + "node": "14", + }, + }, + "title": "Test-Page", + "url": "/test-page", + "weight": 0, + }, + "id": "menu_link_content:1c6d3476-61b1-4b7e-a580-6fa63bdac5d1", + "type": "menu_link_content--menu_link_content", + }, + ], + "jsonapi": { + "meta": { + "links": { + "self": { + "href": "http://jsonapi.org/format/1.0/", + }, + }, + }, + "version": "1.0", + }, + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/menu_items/my_menu", + }, + }, +} +`; diff --git a/packages/jsonapi/tests/__snapshots__/resources.test.ts.snap b/packages/jsonapi/tests/__snapshots__/resources.test.ts.snap index cda0d2c..fa80a8e 100644 --- a/packages/jsonapi/tests/__snapshots__/resources.test.ts.snap +++ b/packages/jsonapi/tests/__snapshots__/resources.test.ts.snap @@ -1,14 +1,14 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`resources should build json api url: simple 1`] = `"https://my-drupal.com/jsonapi/node/article"`; +exports[`Build JSON:API url 1`] = `"https://my-drupal.com/jsonapi/node/article"`; -exports[`resources should build json api url: with-locale 1`] = `"https://my-drupal.com/es/jsonapi/node/article"`; +exports[`Build JSON:API url 2`] = `"https://my-drupal.com/jsonapi/node/article?filter%5Bgroup%5D%5Bgroup%5D%5Bconjunction%5D=OR&filter%5Bstatus%5D%5Bcondition%5D%5Bpath%5D=status&filter%5Bstatus%5D%5Bcondition%5D%5Bvalue%5D=1&filter%5Bstatus%5D%5Bcondition%5D%5BmemberOf%5D=test&include=one%2Ctwo&sort=name&revision=5"`; -exports[`resources should build json api url: with-locale-and-query 1`] = `"https://my-drupal.com/es/jsonapi/node/article?filter%5Bgroup%5D%5Bgroup%5D%5Bconjunction%5D=OR&filter%5Bstatus%5D%5Bcondition%5D%5Bpath%5D=status&filter%5Bstatus%5D%5Bcondition%5D%5Bvalue%5D=1&filter%5Bstatus%5D%5Bcondition%5D%5BmemberOf%5D=test&include=one%2Ctwo&sort=name&revision=5"`; +exports[`Build JSON:API url 3`] = `"https://my-drupal.com/es/jsonapi/node/article"`; -exports[`resources should build json api url: with-query 1`] = `"https://my-drupal.com/jsonapi/node/article?filter%5Bgroup%5D%5Bgroup%5D%5Bconjunction%5D=OR&filter%5Bstatus%5D%5Bcondition%5D%5Bpath%5D=status&filter%5Bstatus%5D%5Bcondition%5D%5Bvalue%5D=1&filter%5Bstatus%5D%5Bcondition%5D%5BmemberOf%5D=test&include=one%2Ctwo&sort=name&revision=5"`; +exports[`Build JSON:API url 4`] = `"https://my-drupal.com/es/jsonapi/node/article?filter%5Bgroup%5D%5Bgroup%5D%5Bconjunction%5D=OR&filter%5Bstatus%5D%5Bcondition%5D%5Bpath%5D=status&filter%5Bstatus%5D%5Bcondition%5D%5Bvalue%5D=1&filter%5Bstatus%5D%5Bcondition%5D%5BmemberOf%5D=test&include=one%2Ctwo&sort=name&revision=5"`; -exports[`resources should create new resource: create 1`] = ` +exports[`Create new resource 1`] = ` { "data": { "attributes": { @@ -134,9 +134,83 @@ exports[`resources should create new resource: create 1`] = ` } `; -exports[`resources should get json api resource with query parameters: single-query-url 1`] = `"https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f?include=uid&resourceVersion=id%3A3"`; +exports[`Create new resource 2`] = ` +{ + "body": { + "format": "ckeditor", + "processed": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + "summary": "Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.", + "value": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-27T09:38:39+00:00", + "default_langcode": true, + "drupal_internal__nid": 3, + "drupal_internal__vid": 3, + "field_media": { + "id": "6530afc8-5635-45b7-a3d9-920d7e10c39e", + "resourceIdObjMeta": { + "drupal_internal__target_id": 8, + }, + "type": "media--image", + }, + "id": "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/dairy-free-and-delicious-milk-chocolate", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/milchfreie-und-koestliche-milchschokolade", + "langcode": "de", + "pid": 4, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Milchfreie und köstliche Milchschokolade", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, +} +`; -exports[`resources should get json api resource: get-single 1`] = ` +exports[`Get JSON:API index 1`] = `"{"jsonapi":{"version":"1.0","meta":{"links":{"self":{"href":"http://jsonapi.org/format/1.0/"}}}},"data":[],"links":{"file--file":{"href":"https://my-drupal.com/jsonapi/file/file"},"media--image":{"href":"https://my-drupal.com/jsonapi/media/image"},"node--article":{"href":"https://my-drupal.com/jsonapi/node/article"},"node--page":{"href":"https://my-drupal.com/jsonapi/node/page"},"self":{"href":"https://my-drupal.com/jsonapi"},"user--user":{"href":"https://my-drupal.com/jsonapi/user/user"}}}"`; + +exports[`Get JSON:API resource 1`] = ` { "data": { "attributes": { @@ -262,9 +336,9 @@ exports[`resources should get json api resource: get-single 1`] = ` } `; -exports[`resources should get jsonapi index: jsonapi-index 1`] = `"{"jsonapi":{"version":"1.0","meta":{"links":{"self":{"href":"http://jsonapi.org/format/1.0/"}}}},"data":[],"links":{"file--file":{"href":"https://my-drupal.com/jsonapi/file/file"},"media--image":{"href":"https://my-drupal.com/jsonapi/media/image"},"node--article":{"href":"https://my-drupal.com/jsonapi/node/article"},"node--page":{"href":"https://my-drupal.com/jsonapi/node/page"},"self":{"href":"https://my-drupal.com/jsonapi"},"user--user":{"href":"https://my-drupal.com/jsonapi/user/user"}}}"`; +exports[`Get JSON:API resource with query parameters 1`] = `"https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f?include=uid&resourceVersion=id%3A3"`; -exports[`resources should get localized json api resource: get-single-localized 1`] = ` +exports[`Get localized JSON:API resource 1`] = ` { "data": { "attributes": { @@ -390,9 +464,7 @@ exports[`resources should get localized json api resource: get-single-localized } `; -exports[`resources should get many resources with query: many-query-url 1`] = `"https://my-drupal.com/jsonapi/node/article?filter%5Bstatus%5D=true&page%5Boffset%5D=0"`; - -exports[`resources should get many resources: get-collection 1`] = ` +exports[`Get many resources 1`] = ` { "data": [ { @@ -1262,7 +1334,654 @@ exports[`resources should get many resources: get-collection 1`] = ` } `; -exports[`resources should update resource: update 1`] = ` +exports[`Simplify many resources 1`] = ` +[ + { + "body": { + "format": "ckeditor", + "processed": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + "summary": "Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.", + "value": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-27T09:38:39+00:00", + "default_langcode": true, + "drupal_internal__nid": 3, + "drupal_internal__vid": 3, + "field_media": { + "id": "6530afc8-5635-45b7-a3d9-920d7e10c39e", + "resourceIdObjMeta": { + "drupal_internal__target_id": 8, + }, + "type": "media--image", + }, + "id": "58e198f6-41af-48b0-b889-596adcb9622e", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e?resourceVersion=id%3A3", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/dairy-free-and-delicious-milk-chocolate", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/milchfreie-und-koestliche-milchschokolade", + "langcode": "de", + "pid": 4, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Milchfreie und köstliche Milchschokolade", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, + { + "body": { + "format": "ckeditor", + "processed": "

Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen. Bei den Tricks, die alle Supermärkte anwenden, handelt es sich manchmal um sinnvolle Maßnahmen, die jeder Einzelhändler ergreifen sollte, um seinen Umsatz zu steigern - aber einige sind vielleicht subtiler und weniger offensichtlich, als Sie vielleicht denken.

Da Artikel und Dokumentarfilme, die das Bewusstsein der Verbraucher schärfen, dieses Thema häufig aufgreifen, ist es wahrscheinlich, dass es den Einzelhändlern immer schwerer fällt, mit den offensichtlichsten Tricks durchzukommen. Wir werden immer versiertere Verbraucher, und es gibt wahrscheinlich nicht viel, was uns entgeht. Aber hier sind ein paar Tricks des Einzelhandels, die Sie beachten sollten, wenn Sie im Supermarkt Ihre wöchentlichen Vorräte aufstocken.

", + "summary": "Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen.", + "value": "

Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen. Bei den Tricks, die alle Supermärkte anwenden, handelt es sich manchmal um sinnvolle Maßnahmen, die jeder Einzelhändler ergreifen sollte, um seinen Umsatz zu steigern - aber einige sind vielleicht subtiler und weniger offensichtlich, als Sie vielleicht denken.

Da Artikel und Dokumentarfilme, die das Bewusstsein der Verbraucher schärfen, dieses Thema häufig aufgreifen, ist es wahrscheinlich, dass es den Einzelhändlern immer schwerer fällt, mit den offensichtlichsten Tricks durchzukommen. Wir werden immer versiertere Verbraucher, und es gibt wahrscheinlich nicht viel, was uns entgeht. Aber hier sind ein paar Tricks des Einzelhandels, die Sie beachten sollten, wenn Sie im Supermarkt Ihre wöchentlichen Vorräte aufstocken.

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-27T10:01:50+00:00", + "default_langcode": true, + "drupal_internal__nid": 4, + "drupal_internal__vid": 4, + "field_media": { + "id": "b45b811e-f377-413e-bc76-7c59f14e6b4a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 2, + }, + "type": "media--image", + }, + "id": "331b2b21-41f9-46bf-a043-4856441ec83f", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f?resourceVersion=id%3A4", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/real-deal-supermarket-savvy-shopping", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/das-richtige-angebot-fuer-den-einkauf-im-supermarkt", + "langcode": "de", + "pid": 5, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Das richtige Angebot für den Einkauf im Supermarkt", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, + { + "body": { + "format": "ckeditor", + "processed": "

Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.

Außenbereich

Minze

Minze ist eine großartige Pflanze, denn sie ist winterhart und kann in fast jedem Boden wachsen. Minze kann wild wachsen, also halten Sie sie in einem Topf, sonst könnte sie sich ausbreiten und Ihren ganzen Garten erobern.

Salbei

Wie die Minze ist auch der Salbei eine sehr wüchsige Pflanze, die sich in Ihrem Garten ausbreitet, wenn Sie es zulassen. Die hocharomatische Salbeipflanze kann in einem Topf oder einem Blumenbeet in gut durchlässigem Boden gepflanzt werden. Die beste Art, das Kraut aufzubewahren, ist, die Blätter an der Sonne zu trocknen und in einem kühlen, dunklen Schrank in einem verschlossenen Behälter aufzubewahren.

Rosmarin

Rosmarinpflanzen wachsen zu schönen Sträuchern heran. Rosmarin lässt sich leicht aus Stecklingen ziehen, aber er verträgt keinen Frost. Stellen Sie daher Töpfe oder gepflanzte Sträucher in der Nähe des Hauses auf, um sie vor der Kälte zu schützen. Er wächst gut in Töpfen, da er trockene Böden mag, kann aber auch gut in der Erde überleben. Wenn Sie den Rosmarin beschneiden, um ihn in eine bessere Form zu bringen, bewahren Sie die Zweige auf und hängen Sie sie kopfüber auf, um das Aroma zu bewahren und sie für Lebensmittel zu verwenden.

", + "summary": "Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.", + "value": "

Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.

Außenbereich

Minze

Minze ist eine großartige Pflanze, denn sie ist winterhart und kann in fast jedem Boden wachsen. Minze kann wild wachsen, also halten Sie sie in einem Topf, sonst könnte sie sich ausbreiten und Ihren ganzen Garten erobern.

Salbei

Wie die Minze ist auch der Salbei eine sehr wüchsige Pflanze, die sich in Ihrem Garten ausbreitet, wenn Sie es zulassen. Die hocharomatische Salbeipflanze kann in einem Topf oder einem Blumenbeet in gut durchlässigem Boden gepflanzt werden. Die beste Art, das Kraut aufzubewahren, ist, die Blätter an der Sonne zu trocknen und in einem kühlen, dunklen Schrank in einem verschlossenen Behälter aufzubewahren.

Rosmarin

Rosmarinpflanzen wachsen zu schönen Sträuchern heran. Rosmarin lässt sich leicht aus Stecklingen ziehen, aber er verträgt keinen Frost. Stellen Sie daher Töpfe oder gepflanzte Sträucher in der Nähe des Hauses auf, um sie vor der Kälte zu schützen. Er wächst gut in Töpfen, da er trockene Böden mag, kann aber auch gut in der Erde überleben. Wenn Sie den Rosmarin beschneiden, um ihn in eine bessere Form zu bringen, bewahren Sie die Zweige auf und hängen Sie sie kopfüber auf, um das Aroma zu bewahren und sie für Lebensmittel zu verwenden.

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-30T08:11:24+00:00", + "default_langcode": true, + "drupal_internal__nid": 5, + "drupal_internal__vid": 5, + "field_media": { + "id": "e8871454-98b9-4421-988b-b63b7e749566", + "resourceIdObjMeta": { + "drupal_internal__target_id": 7, + }, + "type": "media--image", + }, + "id": "c0250ce7-7b5b-4b41-8557-647c858b29a2", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2?resourceVersion=id%3A5", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/give-it-go-and-grow-your-own-herbs", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/probieren-sie-es-aus-und-bauen-sie-ihre-eigenen-kraeuter", + "langcode": "de", + "pid": 6, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Probieren Sie es aus und bauen Sie Ihre eigenen Kräuter an!", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, + { + "body": { + "format": "ckeditor", + "processed": "

Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.

Wer kocht nicht gern mit diesem vielseitigen Wurzelgemüse? Wir braten sie, kochen sie, mischen sie in Suppen und reiben sie in Salate. Die bescheidene Karotte ist eines unserer Lieblingsgemüse und wird schon seit Tausenden von Jahren angebaut. Damals gab es aber eher lila, rote, gelbe oder weiße Karotten und nicht die orangefarbenen, die wir heute so gut kennen.

Was ist also passiert? Seit wann ist Orange die bevorzugte Farbe?

Es waren die Niederländer im 17. Jahrhundert, die die orangefarbene Sorte kultivierten und populär machten, wahrscheinlich wegen ihrer leuchtenden Farbe und ihres höheren Gehalts an Beta-Carotin. Es wird auch vermutet, dass sie als Hommage an Wilhelm von Oranien angebaut wurde, der den Kampf um die Unabhängigkeit der Niederlande anführte.

Aus welchem Grund auch immer, die orangefarbene Sorte hat sich durchgesetzt, aber halten Sie auf Bauernmärkten und in Lebensmittelgeschäften Ausschau nach den "Heritage"-Sorten, deren Mischung aus Violett, Gelb, Orange und Weiß sich besonders gut zum Kochen eignet und als Beilage absolut großartig aussieht.

", + "summary": "Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.", + "value": "

Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.

Wer kocht nicht gern mit diesem vielseitigen Wurzelgemüse? Wir braten sie, kochen sie, mischen sie in Suppen und reiben sie in Salate. Die bescheidene Karotte ist eines unserer Lieblingsgemüse und wird schon seit Tausenden von Jahren angebaut. Damals gab es aber eher lila, rote, gelbe oder weiße Karotten und nicht die orangefarbenen, die wir heute so gut kennen.

Was ist also passiert? Seit wann ist Orange die bevorzugte Farbe?

Es waren die Niederländer im 17. Jahrhundert, die die orangefarbene Sorte kultivierten und populär machten, wahrscheinlich wegen ihrer leuchtenden Farbe und ihres höheren Gehalts an Beta-Carotin. Es wird auch vermutet, dass sie als Hommage an Wilhelm von Oranien angebaut wurde, der den Kampf um die Unabhängigkeit der Niederlande anführte.

Aus welchem Grund auch immer, die orangefarbene Sorte hat sich durchgesetzt, aber halten Sie auf Bauernmärkten und in Lebensmittelgeschäften Ausschau nach den "Heritage"-Sorten, deren Mischung aus Violett, Gelb, Orange und Weiß sich besonders gut zum Kochen eignet und als Beilage absolut großartig aussieht.

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-30T08:19:20+00:00", + "default_langcode": true, + "drupal_internal__nid": 6, + "drupal_internal__vid": 6, + "field_media": { + "id": "5560b9b2-b088-44e8-9213-18864ee7c8ae", + "resourceIdObjMeta": { + "drupal_internal__target_id": 5, + }, + "type": "media--image", + }, + "id": "708bbe72-76d1-4e20-919c-e2785e0d7e93", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93?resourceVersion=id%3A6", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/lets-hear-it-carrots", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/ein-applaus-fuer-karotten", + "langcode": "de", + "pid": 7, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Ein Applaus für Karotten", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, + { + "body": { + "format": "ckeditor", + "processed": "

Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? Bevor Sie die langweiligen Haferflocken in die Tonne kloppen, sollten Sie sich von unseren Topping-Ideen inspirieren lassen, die Ihre Haferflocken in nur wenigen Minuten von fade zu kreativ und köstlich machen.

Ob Haferflocken, Haferbrei, heiße Müsli oder einfach nur Haferflocken - Haferflocken sind auf der ganzen Welt beliebt, aber in letzter Zeit bekommt der einfache Hafer ein neues Gesicht. Vorbei sind die Zeiten der faden Frühstücksschalen mit dickem Brei, denn Haferflocken werden jetzt als köstliche Mahlzeit serviert, die zu jeder Tageszeit passt. Trendige Haferflockenriegel, die man sich selbst zusammenstellen kann, tauchen immer häufiger auf und bieten das ultimative Haferflockenerlebnis. Die Gäste können wählen, ob die Haferflocken mit Milch oder Wasser zubereitet werden sollen, und dann nach Belieben süße, herzhafte oder sogar pikante Beläge hinzufügen.

Klingt toll, nicht wahr? Bevor Sie also die Tür des Schranks mit dem unscheinbaren Glas Haferflocken schließen, sollten Sie einige dieser leckeren Garnier-Ideen ausprobieren, um Ihre eigenen Haferflocken neu zu gestalten.

", + "summary": "Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? ", + "value": "

Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? Bevor Sie die langweiligen Haferflocken in die Tonne kloppen, sollten Sie sich von unseren Topping-Ideen inspirieren lassen, die Ihre Haferflocken in nur wenigen Minuten von fade zu kreativ und köstlich machen.

Ob Haferflocken, Haferbrei, heiße Müsli oder einfach nur Haferflocken - Haferflocken sind auf der ganzen Welt beliebt, aber in letzter Zeit bekommt der einfache Hafer ein neues Gesicht. Vorbei sind die Zeiten der faden Frühstücksschalen mit dickem Brei, denn Haferflocken werden jetzt als köstliche Mahlzeit serviert, die zu jeder Tageszeit passt. Trendige Haferflockenriegel, die man sich selbst zusammenstellen kann, tauchen immer häufiger auf und bieten das ultimative Haferflockenerlebnis. Die Gäste können wählen, ob die Haferflocken mit Milch oder Wasser zubereitet werden sollen, und dann nach Belieben süße, herzhafte oder sogar pikante Beläge hinzufügen.

Klingt toll, nicht wahr? Bevor Sie also die Tür des Schranks mit dem unscheinbaren Glas Haferflocken schließen, sollten Sie einige dieser leckeren Garnier-Ideen ausprobieren, um Ihre eigenen Haferflocken neu zu gestalten.

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-30T08:26:39+00:00", + "default_langcode": true, + "drupal_internal__nid": 7, + "drupal_internal__vid": 7, + "field_media": { + "id": "f1af4452-15a1-46ec-9b7e-871212a0ee4c", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "media--image", + }, + "id": "bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3?resourceVersion=id%3A7", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/give-your-oatmeal-ultimate-makeover", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/verleihen-sie-ihren-haferflocken-das-ultimative-makeover", + "langcode": "de", + "pid": 8, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Verleihen Sie Ihren Haferflocken das ultimative Makeover", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, + { + "body": { + "format": "ckeditor", + "processed": "

Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? Denn was wäre eine Cocktailparty ohne die tollen Gläser mit gemischten Früchten, die bunten Farben und natürlich die kleinen Schirmchen? Tragen Sie Ihren Teil zum Umweltschutz bei, indem Sie auf Plastikstrohhalme verzichten und sich stattdessen ein paar tolle Alternativen aus Glas, Metall oder sogar Bambus besorgen. Vergessen Sie nicht die klassischen Cocktailgläser und die coolen Becher, die diese Rezepte erst so richtig zum Leben erwecken.

Also, schnappen Sie sich den Cocktailmixer, lassen Sie die Spirituosen weg und schütteln Sie diese köstlich erfrischenden Mocktails, die weder Ihnen noch Ihren Gästen an Geschmack oder Stil mangeln werden.

Mango-Köstlich

Dieser köstliche Drink hat einen raffinierten Geschmack, der ideal für Erwachsene ist, und die Farbe passt perfekt zu einem spätsommerlichen BBQ.

Anleitung

Schütteln Sie ein paar Gurkenscheiben und etwas Honig in einem Cocktailshaker gut durch. Einen Schuss Mangopüree und den Saft einer Limette hinzufügen. Etwas Eis hinzufügen und kräftig schütteln. Durch ein Sieb in ein schickes Glas gießen und mit Ginger Beer auffüllen. Ein paar Limettenscheiben, und schon kann es losgehen.

", + "summary": "Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? ", + "value": "

Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? Denn was wäre eine Cocktailparty ohne die tollen Gläser mit gemischten Früchten, die bunten Farben und natürlich die kleinen Schirmchen? Tragen Sie Ihren Teil zum Umweltschutz bei, indem Sie auf Plastikstrohhalme verzichten und sich stattdessen ein paar tolle Alternativen aus Glas, Metall oder sogar Bambus besorgen. Vergessen Sie nicht die klassischen Cocktailgläser und die coolen Becher, die diese Rezepte erst so richtig zum Leben erwecken.

Also, schnappen Sie sich den Cocktailmixer, lassen Sie die Spirituosen weg und schütteln Sie diese köstlich erfrischenden Mocktails, die weder Ihnen noch Ihren Gästen an Geschmack oder Stil mangeln werden.

Mango-Köstlich

Dieser köstliche Drink hat einen raffinierten Geschmack, der ideal für Erwachsene ist, und die Farbe passt perfekt zu einem spätsommerlichen BBQ.

Anleitung

Schütteln Sie ein paar Gurkenscheiben und etwas Honig in einem Cocktailshaker gut durch. Einen Schuss Mangopüree und den Saft einer Limette hinzufügen. Etwas Eis hinzufügen und kräftig schütteln. Durch ein Sieb in ein schickes Glas gießen und mit Ginger Beer auffüllen. Ein paar Limettenscheiben, und schon kann es losgehen.

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-30T08:24:15+00:00", + "default_langcode": true, + "drupal_internal__nid": 8, + "drupal_internal__vid": 8, + "field_media": { + "id": "de14673c-55ac-41ff-823d-3047e9ce2e72", + "resourceIdObjMeta": { + "drupal_internal__target_id": 3, + }, + "type": "media--image", + }, + "id": "7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad?resourceVersion=id%3A8", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/skip-spirits-delicious-mocktails", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/ueberspringen-sie-die-spirituosen-mit-koestlichen-mocktails", + "langcode": "de", + "pid": 9, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Überspringen Sie die Spirituosen mit köstlichen Mocktails", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, + { + "body": { + "format": "ckeditor", + "processed": "

Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. Und wer würde bei so kuriosen Namen wie Pfifferling, Zigeunerpilz, Füllhorn oder Waldhenne nicht gerne mehr über das Kochen mit dem mächtigen Pilz erfahren?

Eines der besten Dinge an Pilzen ist ihre Vielseitigkeit. Sie können gebraten, geröstet, gegrillt, gedünstet oder sogar in der Mikrowelle gegart werden. Und sie können als Hauptzutat für ein Gericht serviert oder einfach als Teil der Mischung hinzugefügt werden. Das macht Pilze zu einer idealen Wahl für die Zubereitung absolut köstlicher vegetarischer Gerichte.

Werfen wir also einen Blick auf einige unserer Lieblingspilzsorten. Vielleicht haben Sie noch nie mit ihnen gekocht, aber das sollte Sie nicht abschrecken. Mit ihren köstlichen, unverwechselbaren Aromen können Sie Suppen, Vorspeisen und Soßen ganz einfach verwandeln und erstaunliche Nudel- oder Pfannengerichte kreieren.

Probieren Sie die lieblichen Shiitake. Sie werden in der asiatischen Küche verwendet und können getrocknet und rehydriert gekauft werden, um einen kräftigen, tiefen Geschmack zu erhalten. Oder Sie kaufen ihn frisch und fügen ihn zu Suppen und Pfannengerichten hinzu. Dieser Pilz hat nicht nur einen intensiven Geschmack, sondern sieht auch schön aus. Das tiefe Braun und die glatten Formen geben Ihrem Gericht eine besondere Textur. In getrockneter Form und rehydriert sind sie die perfekte Ergänzung für eine tiefe und würzige Brühe für ein Risotto.

Der prächtige sonnige Pfifferling mit seinem gelben Fruchtfleisch hat einen fruchtigen Geschmack - aber es ist erwähnenswert, dass es viele Nachahmer gibt, und man sollte darauf achten, dass man die richtigen isst. Diese sehen in einem Omelett oder einer asiatischen Suppe großartig aus, um die Gelbtöne zu ergänzen.

Die braune Morchel bietet einen fleischigen und unverwechselbaren Geschmack, und Sie werden wahrscheinlich lieben, wie außergewöhnlich sie in einer Mahlzeit aussieht. Die Morchel ist ein beliebter Pilz im Frühjahr, wenn ihr Angebot besonders groß ist.

Eine Delikatesse ist der Enoki mit seinen winzigen weißen Köpfen, die in Büscheln wachsen. Sie können sogar roh in Salaten verzehrt werden. Schließlich können Sie den beliebten Austernpilz wählen. Sie heißen so, weil sie nicht wie ein Pilz aussehen, sondern den Innereien einer Auster ähneln, und ihr süßer Geschmack ist köstlich.

", + "summary": "Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. ", + "value": "

Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. Und wer würde bei so kuriosen Namen wie Pfifferling, Zigeunerpilz, Füllhorn oder Waldhenne nicht gerne mehr über das Kochen mit dem mächtigen Pilz erfahren?

Eines der besten Dinge an Pilzen ist ihre Vielseitigkeit. Sie können gebraten, geröstet, gegrillt, gedünstet oder sogar in der Mikrowelle gegart werden. Und sie können als Hauptzutat für ein Gericht serviert oder einfach als Teil der Mischung hinzugefügt werden. Das macht Pilze zu einer idealen Wahl für die Zubereitung absolut köstlicher vegetarischer Gerichte.

Werfen wir also einen Blick auf einige unserer Lieblingspilzsorten. Vielleicht haben Sie noch nie mit ihnen gekocht, aber das sollte Sie nicht abschrecken. Mit ihren köstlichen, unverwechselbaren Aromen können Sie Suppen, Vorspeisen und Soßen ganz einfach verwandeln und erstaunliche Nudel- oder Pfannengerichte kreieren.

Probieren Sie die lieblichen Shiitake. Sie werden in der asiatischen Küche verwendet und können getrocknet und rehydriert gekauft werden, um einen kräftigen, tiefen Geschmack zu erhalten. Oder Sie kaufen ihn frisch und fügen ihn zu Suppen und Pfannengerichten hinzu. Dieser Pilz hat nicht nur einen intensiven Geschmack, sondern sieht auch schön aus. Das tiefe Braun und die glatten Formen geben Ihrem Gericht eine besondere Textur. In getrockneter Form und rehydriert sind sie die perfekte Ergänzung für eine tiefe und würzige Brühe für ein Risotto.

Der prächtige sonnige Pfifferling mit seinem gelben Fruchtfleisch hat einen fruchtigen Geschmack - aber es ist erwähnenswert, dass es viele Nachahmer gibt, und man sollte darauf achten, dass man die richtigen isst. Diese sehen in einem Omelett oder einer asiatischen Suppe großartig aus, um die Gelbtöne zu ergänzen.

Die braune Morchel bietet einen fleischigen und unverwechselbaren Geschmack, und Sie werden wahrscheinlich lieben, wie außergewöhnlich sie in einer Mahlzeit aussieht. Die Morchel ist ein beliebter Pilz im Frühjahr, wenn ihr Angebot besonders groß ist.

Eine Delikatesse ist der Enoki mit seinen winzigen weißen Köpfen, die in Büscheln wachsen. Sie können sogar roh in Salaten verzehrt werden. Schließlich können Sie den beliebten Austernpilz wählen. Sie heißen so, weil sie nicht wie ein Pilz aussehen, sondern den Innereien einer Auster ähneln, und ihr süßer Geschmack ist köstlich.

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-30T08:17:34+00:00", + "default_langcode": true, + "drupal_internal__nid": 9, + "drupal_internal__vid": 9, + "field_media": { + "id": "8f8895dc-d6e4-469f-8a89-f6729cdfed0f", + "resourceIdObjMeta": { + "drupal_internal__target_id": 4, + }, + "type": "media--image", + }, + "id": "5e462a38-de37-4769-add2-998db91fd839", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839?resourceVersion=id%3A9", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/umami-guide-our-favorite-mushrooms", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/der-umami-fuehrer-fuer-unsere-lieblingspilze", + "langcode": "de", + "pid": 10, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Der Umami-Führer für unsere Lieblingspilze", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, + { + "body": { + "format": "ckeditor", + "processed": "

Sie halten sich an das Rezept, wiegen alle Zutaten ab, verwenden die richtige Ofentemperatur und nehmen sich Zeit - und trotzdem wird Ihr Gebäck am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar. Wir haben eine Liste mit den frustrierendsten Backproblemen und den Techniken, die Sie anwenden können, um sie zu vermeiden, zusammengestellt.

Ihr Kuchen ist nicht aufgegangen

Öffnen Sie auf keinen Fall die Ofentür mitten in der Backzeit. Die Hitze zischt heraus und zerstört die chemische Reaktion, die in Ihrem Kuchen stattfindet. Ihr Backtriebmittel braucht die Hitze, um aktiviert zu werden, und Sie könnten es abtöten.

Ihr Kuchen ist zäh und hart

Wenn Ihr heller Biskuit die Konsistenz eines Reservereifens hat, haben Sie Ihren Teig möglicherweise zu stark gemischt. Behalten Sie die Leichtigkeit Ihres Kuchenteigs bei, indem Sie die Zutaten vorsichtig und behutsam unterheben, aber nur so weit, bis sie gerade vermischt sind. Wenn Sie zu viel unterheben, tritt das Gluten im Mehl aus und Sie backen eher Brot als Kuchen.

", + "summary": "Man hält sich an das Rezept, wiegt alle Zutaten ab, wählt die richtige Ofentemperatur und nimmt sich Zeit, und trotzdem wird das Backwerk am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar.", + "value": "

Sie halten sich an das Rezept, wiegen alle Zutaten ab, verwenden die richtige Ofentemperatur und nehmen sich Zeit - und trotzdem wird Ihr Gebäck am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar. Wir haben eine Liste mit den frustrierendsten Backproblemen und den Techniken, die Sie anwenden können, um sie zu vermeiden, zusammengestellt.

Ihr Kuchen ist nicht aufgegangen

Öffnen Sie auf keinen Fall die Ofentür mitten in der Backzeit. Die Hitze zischt heraus und zerstört die chemische Reaktion, die in Ihrem Kuchen stattfindet. Ihr Backtriebmittel braucht die Hitze, um aktiviert zu werden, und Sie könnten es abtöten.

Ihr Kuchen ist zäh und hart

Wenn Ihr heller Biskuit die Konsistenz eines Reservereifens hat, haben Sie Ihren Teig möglicherweise zu stark gemischt. Behalten Sie die Leichtigkeit Ihres Kuchenteigs bei, indem Sie die Zutaten vorsichtig und behutsam unterheben, aber nur so weit, bis sie gerade vermischt sind. Wenn Sie zu viel unterheben, tritt das Gluten im Mehl aus und Sie backen eher Brot als Kuchen.

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-30T08:21:41+00:00", + "default_langcode": true, + "drupal_internal__nid": 10, + "drupal_internal__vid": 10, + "field_media": { + "id": "e1a3b027-8983-44a7-bbea-403898ba0260", + "resourceIdObjMeta": { + "drupal_internal__target_id": 6, + }, + "type": "media--image", + }, + "id": "79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30?resourceVersion=id%3A10", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/baking-mishaps-our-troubleshooting-tips", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/missgeschicke-beim-backen-unsere-tipps-zur-fehlerbehebung", + "langcode": "de", + "pid": 11, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Missgeschicke beim Backen - unsere Tipps zur Fehlerbehebung", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + }, +] +`; + +exports[`Simplify single resource 1`] = ` +{ + "body": { + "format": "ckeditor", + "processed": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + "summary": "Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.", + "value": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-27T09:38:39+00:00", + "default_langcode": true, + "drupal_internal__nid": 3, + "drupal_internal__vid": 3, + "field_media": { + "id": "6530afc8-5635-45b7-a3d9-920d7e10c39e", + "resourceIdObjMeta": { + "drupal_internal__target_id": 8, + }, + "type": "media--image", + }, + "id": "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/dairy-free-and-delicious-milk-chocolate", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/milchfreie-und-koestliche-milchschokolade", + "langcode": "de", + "pid": 4, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Milchfreie und köstliche Milchschokolade", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, +} +`; + +exports[`Update resource 1`] = ` { "data": { "attributes": { @@ -1387,3 +2106,77 @@ exports[`resources should update resource: update 1`] = ` }, } `; + +exports[`Update resource 2`] = ` +{ + "body": { + "format": "ckeditor", + "processed": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + "summary": "Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.", + "value": "

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

", + }, + "changed": "2023-02-23T11:10:08+00:00", + "content_translation_outdated": false, + "content_translation_source": "und", + "created": "2023-01-27T09:38:39+00:00", + "default_langcode": true, + "drupal_internal__nid": 3, + "drupal_internal__vid": 3, + "field_media": { + "id": "6530afc8-5635-45b7-a3d9-920d7e10c39e", + "resourceIdObjMeta": { + "drupal_internal__target_id": 8, + }, + "type": "media--image", + }, + "id": "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f", + "langcode": "de", + "links": { + "self": { + "href": "https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f", + }, + }, + "meta": { + "localeInfo": [ + { + "langcode": "en", + "path": "/articles/dairy-free-and-delicious-milk-chocolate", + }, + ], + }, + "node_type": null, + "path": { + "alias": "/articles/milchfreie-und-koestliche-milchschokolade", + "langcode": "de", + "pid": 4, + }, + "promote": true, + "relationshipNames": [ + "node_type", + "revision_uid", + "uid", + "field_media", + ], + "revision_log": null, + "revision_timestamp": "2023-02-23T11:10:08+00:00", + "revision_translation_affected": true, + "revision_uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, + "status": true, + "sticky": false, + "title": "Milchfreie und köstliche Milchschokolade", + "type": "node--article", + "uid": { + "id": "f0d79a17-a6b1-4c34-ba1b-ae298826f68a", + "resourceIdObjMeta": { + "drupal_internal__target_id": 1, + }, + "type": "user--user", + }, +} +`; diff --git a/packages/jsonapi/tests/core.test.ts b/packages/jsonapi/tests/core.test.ts index cceab31..9f0ea2e 100644 --- a/packages/jsonapi/tests/core.test.ts +++ b/packages/jsonapi/tests/core.test.ts @@ -1,14 +1,14 @@ -import test from "ava"; +import { expect, test } from "vitest"; import { Drupalkit } from "@drupal-kit/core"; import { DrupalkitJsonApi } from "../src/index.js"; -test("Instanciate with plugin", (t) => { +test("Instanciate with plugin", () => { const EnhancedDrupalkit = Drupalkit.plugin(DrupalkitJsonApi); const drupalkit = new EnhancedDrupalkit({ baseUrl: "https://example.com", }); - t.assert(drupalkit.hasOwnProperty("jsonApi")); + expect(drupalkit.hasOwnProperty("jsonApi")).toBeTruthy(); }); diff --git a/packages/jsonapi/tests/menu.test.ts b/packages/jsonapi/tests/menu.test.ts index 95f2c0c..31e33c3 100644 --- a/packages/jsonapi/tests/menu.test.ts +++ b/packages/jsonapi/tests/menu.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; import { Drupalkit } from "@drupal-kit/core"; @@ -23,19 +23,19 @@ const createDrupalkit = ({ baseUrl = BASE_URL }: { baseUrl?: string } = {}) => { const server = setupServer(); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); -test.serial("Get menu items", async (t) => { +test("Get menu items", async () => { const drupalkit = createDrupalkit(); server.use( @@ -52,21 +52,21 @@ test.serial("Get menu items", async (t) => { const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); }); -test.serial("Get menu items with custom request options", async (t) => { - t.plan(2); +test("Get menu items with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.get("*/jsonapi/menu_items/my_menu", ({ request }) => { - t.is(request.headers.get("x-custom"), "1"); + expect(request.headers.get("x-custom")).toBe("1"); return HttpResponse.json(JsonApiMenuItems, { headers: { @@ -84,7 +84,7 @@ test.serial("Get menu items with custom request options", async (t) => { }); }); -test.serial("Get menu items for non-existant menu", async (t) => { +test("Get menu items for non-existant menu", async () => { const drupalkit = createDrupalkit(); server.use( @@ -102,6 +102,6 @@ test.serial("Get menu items for non-existant menu", async (t) => { const err = result.expectErr("Expect error"); - t.assert(err instanceof DrupalkitJsonApiError); - t.is(err.statusCode, 404); + expect(err instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(err.statusCode).toBe(404); }); diff --git a/packages/jsonapi/tests/resources.test.ts b/packages/jsonapi/tests/resources.test.ts index c2e4912..98b5aff 100644 --- a/packages/jsonapi/tests/resources.test.ts +++ b/packages/jsonapi/tests/resources.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { DrupalJsonApiParams } from "drupal-jsonapi-params"; import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; @@ -30,19 +30,19 @@ const createDrupalkit = ({ baseUrl = BASE_URL }: { baseUrl?: string } = {}) => { const server = setupServer(); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); -test("Build JSON:API url", (t) => { +test("Build JSON:API url", () => { const drupalkit = createDrupalkit(); const queryParams = new DrupalJsonApiParams(); queryParams @@ -56,33 +56,33 @@ test("Build JSON:API url", (t) => { // Simple url. let url = drupalkit.jsonApi.buildJsonApiUrl("node/article"); - t.snapshot(url); + expect(url).toMatchSnapshot(); // With query. url = drupalkit.jsonApi.buildJsonApiUrl("node/article", { query: queryParams.getQueryObject(), }); - t.snapshot(url); + expect(url).toMatchSnapshot(); // With locale. url = drupalkit.jsonApi.buildJsonApiUrl("node/article", { localeOverride: "es", }); - t.snapshot(url); + expect(url).toMatchSnapshot(); // With locale and query. url = drupalkit.jsonApi.buildJsonApiUrl("node/article", { localeOverride: "es", query: queryParams.getQueryObject(), }); - t.snapshot(url); + expect(url).toMatchSnapshot(); }); /** * getIndex(). */ -test.serial("Get JSON:API index", async (t) => { +test("Get JSON:API index", async () => { const drupalkit = createDrupalkit(); server.use( @@ -99,10 +99,10 @@ test.serial("Get JSON:API index", async (t) => { const res = index.unwrap(); - t.snapshot(JSON.stringify(res)); - t.assert(res.hasOwnProperty("jsonapi")); - t.assert(res.hasOwnProperty("data")); - t.assert(res.hasOwnProperty("links")); + expect(JSON.stringify(res)).toMatchSnapshot(); + expect(res.hasOwnProperty("jsonapi")).toBeTruthy(); + expect(res.hasOwnProperty("data")).toBeTruthy(); + expect(res.hasOwnProperty("links")).toBeTruthy(); // With error server.resetHandlers(); @@ -120,21 +120,21 @@ test.serial("Get JSON:API index", async (t) => { index = await drupalkit.jsonApi.getIndex(); const err = index.expectErr("Expect error"); - t.assert(err instanceof DrupalkitJsonApiError); + expect(err instanceof DrupalkitJsonApiError).toBeTruthy(); }); -test.serial("Get JSON:API index with options", async (t) => { - t.plan(2); +test("Get JSON:API index with options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.get("*/jsonapi", ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(JsonApiIndex, { headers: { @@ -156,7 +156,7 @@ test.serial("Get JSON:API index with options", async (t) => { * .resource() - "readSingle". */ -test.serial("Get JSON:API resource", async (t) => { +test("Get JSON:API resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; @@ -179,22 +179,22 @@ test.serial("Get JSON:API resource", async (t) => { ); const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); }); -test.serial("Get JSON:API resource with options", async (t) => { - t.plan(2); +test("Get JSON:API resource with options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.get("*/jsonapi/node/article/" + uuid, ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(JsonApiArticleDetail, { headers: { @@ -219,7 +219,7 @@ test.serial("Get JSON:API resource with options", async (t) => { ); }); -test.serial("Simplify single resource", async (t) => { +test("Simplify single resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; @@ -244,18 +244,18 @@ test.serial("Simplify single resource", async (t) => { const res = result.unwrap(); const data = drupalkit.jsonApi.simplifyResourceResponse(res); - t.snapshot(data); + expect(data).toMatchSnapshot(); }); -test.serial("Get localized JSON:API resource", async (t) => { +test("Get localized JSON:API resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; - t.plan(2); + expect.assertions(2); server.use( http.get("*/jsonapi/node/article/" + uuid, ({ request }) => { - t.assert(request.url.toString().includes("/en/jsonapi")); + expect(request.url.toString().includes("/en/jsonapi")).toBeTruthy(); return HttpResponse.json(JsonApiArticleDetail, { headers: { @@ -277,11 +277,11 @@ test.serial("Get localized JSON:API resource", async (t) => { ); const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); }); -test.serial("Get JSON:API resource with query parameters", async (t) => { - t.plan(2); +test("Get JSON:API resource with query parameters", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; @@ -292,7 +292,7 @@ test.serial("Get JSON:API resource with query parameters", async (t) => { server.use( http.get("*/jsonapi/node/article/" + uuid, ({ request }) => { - t.snapshot(request.url.toString()); + expect(request.url.toString()).toMatchSnapshot(); return HttpResponse.json(JsonApiArticleDetail, { headers: { @@ -311,10 +311,10 @@ test.serial("Get JSON:API resource with query parameters", async (t) => { }, ); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error when getting single resource", async (t) => { +test("Handle error when getting single resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; @@ -343,15 +343,15 @@ test.serial("Handle error when getting single resource", async (t) => { const err = result.expectErr("Expect error"); - t.assert(err instanceof DrupalkitJsonApiError); - t.is(err.statusCode, 400); + expect(err instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(err.statusCode).toBe(400); }); /** * .resource() - "readMany". */ -test.serial("Get many resources", async (t) => { +test("Get many resources", async () => { const drupalkit = createDrupalkit(); server.use( @@ -371,22 +371,22 @@ test.serial("Get many resources", async (t) => { ); const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); }); -test.serial("Get many resources with custom request options", async (t) => { - t.plan(3); +test("Get many resources with custom request options", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.get("*/jsonapi/node/article", ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); - t.assert(request.url.toString().includes("/en/jsonapi")); + expect(request.headers.get("X-Custom")).toBe("1"); + expect(request.url.toString().includes("/en/jsonapi")).toBeTruthy(); return HttpResponse.json(JsonApiArticleCollection, { headers: { @@ -410,7 +410,7 @@ test.serial("Get many resources with custom request options", async (t) => { ); }); -test.serial("Simplify many resources", async (t) => { +test("Simplify many resources", async () => { const drupalkit = createDrupalkit(); server.use( @@ -432,11 +432,11 @@ test.serial("Simplify many resources", async (t) => { const res = result.unwrap(); const data = drupalkit.jsonApi.simplifyResourceResponse(res); - t.assert(data.length === 8); - t.snapshot(data); + expect(data.length === 8).toBeTruthy(); + expect(data).toMatchSnapshot(); }); -test.serial("Handle error when getting many resource", async (t) => { +test("Handle error when getting many resource", async () => { const drupalkit = createDrupalkit(); server.use( @@ -457,11 +457,11 @@ test.serial("Handle error when getting many resource", async (t) => { const err = result.expectErr("Expect error"); - t.assert(err instanceof DrupalkitJsonApiError); - t.is(err.statusCode, 400); + expect(err instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(err.statusCode).toBe(400); }); -test.serial("Handle network error", async (t) => { +test("Handle network error", async () => { const drupalkit = createDrupalkit(); server.use( @@ -477,23 +477,23 @@ test.serial("Handle network error", async (t) => { const err = result.expectErr("Expect error"); - t.assert(err.message.includes("Failed to fetch"), err.message); - t.is(err.response, undefined); + expect(err.message.includes("Failed to fetch"), err.message).toBeTruthy(); + expect(err.response).toBe(undefined); }); /** * .resource() - "create". */ -test.serial("Create new resource", async (t) => { +test("Create new resource", async () => { const drupalkit = createDrupalkit(); - t.plan(3); + expect.assertions(3); server.use( http.post("*/jsonapi/node/article", async ({ request }) => { const payload = await request.json(); - t.is((payload as any).data.type, "node--article"); + expect((payload as any).data.type).toBe("node--article"); return HttpResponse.json(JsonApiArticleDetail, { headers: { @@ -521,25 +521,25 @@ test.serial("Create new resource", async (t) => { }); const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); const simpleData = drupalkit.jsonApi.simplifyResourceResponse(res); - t.snapshot(simpleData); + expect(simpleData).toMatchSnapshot(); }); -test.serial("Create resource with custom request options", async (t) => { - t.plan(3); +test("Create resource with custom request options", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/jsonapi/node/article", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); - t.assert(request.url.toString().includes("/en/jsonapi")); + expect(request.headers.get("X-Custom")).toBe("1"); + expect(request.url.toString().includes("/en/jsonapi")).toBeTruthy(); return HttpResponse.json(JsonApiArticleDetail, { headers: { @@ -578,7 +578,7 @@ test.serial("Create resource with custom request options", async (t) => { ); }); -test.serial("Handle error when creating new resource", async (t) => { +test("Handle error when creating new resource", async () => { const drupalkit = createDrupalkit(); server.use( @@ -612,26 +612,26 @@ test.serial("Handle error when creating new resource", async (t) => { const error = result.expectErr("Expect error"); - t.assert(error instanceof DrupalkitJsonApiError); - t.is(error.statusCode, 400); + expect(error instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(error.statusCode).toBe(400); }); /** * .resource() - "update". */ -test.serial("Update resource", async (t) => { +test("Update resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; - t.plan(4); + expect.assertions(4); server.use( http.patch("*/jsonapi/node/article/" + uuid, async ({ request }) => { const payload = (await request.json()) as any; - t.is(payload.data.type, "node--article"); - t.is(payload.data.id, uuid); + expect(payload.data.type).toBe("node--article"); + expect(payload.data.id).toBe(uuid); return HttpResponse.json(JsonApiArticleDetail, { headers: { @@ -651,26 +651,26 @@ test.serial("Update resource", async (t) => { }); const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); const simpleData = drupalkit.jsonApi.simplifyResourceResponse(res); - t.snapshot(simpleData); + expect(simpleData).toMatchSnapshot(); }); -test.serial("Update resource with custom request options", async (t) => { - t.plan(3); +test("Update resource with custom request options", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.patch("*/jsonapi/node/article/" + uuid, async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); - t.assert(request.url.toString().includes("/en/jsonapi")); + expect(request.headers.get("X-Custom")).toBe("1"); + expect(request.url.toString().includes("/en/jsonapi")).toBeTruthy(); return HttpResponse.json(JsonApiArticleDetail, { headers: { @@ -701,7 +701,7 @@ test.serial("Update resource with custom request options", async (t) => { ); }); -test.serial("Handle error when updating resource", async (t) => { +test("Handle error when updating resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; @@ -727,15 +727,15 @@ test.serial("Handle error when updating resource", async (t) => { const error = result.expectErr("Expect error"); - t.assert(error instanceof DrupalkitJsonApiError); - t.is(error.statusCode, 400); + expect(error instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(error.statusCode).toBe(400); }); /** * .resource() - "delete". */ -test.serial("Delete resource", async (t) => { +test("Delete resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; @@ -755,24 +755,24 @@ test.serial("Delete resource", async (t) => { }); const res = result.unwrap(); - t.assert(res); + expect(res).toBeTruthy(); }); -test.serial("Delete resource with custom request options", async (t) => { - t.plan(2); +test("Delete resource with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.delete("*/jsonapi/node/article/" + uuid, ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); - HttpResponse.json(JsonApiIncludeError, { + return HttpResponse.json(JsonApiIncludeError, { status: 400, headers: { "Content-Type": "application/vnd.api+json", @@ -796,7 +796,7 @@ test.serial("Delete resource with custom request options", async (t) => { ); }); -test.serial("Handle error when deleting resource", async (t) => { +test("Handle error when deleting resource", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; @@ -817,33 +817,34 @@ test.serial("Handle error when deleting resource", async (t) => { const error = result.expectErr("Expect error"); - t.assert(error instanceof DrupalkitJsonApiError); - t.is(error.statusCode, 400); + expect(error instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(error.statusCode).toBe(400); }); /** * uploadFile(). */ -test.serial("Upload file to entity field", async (t) => { +test("Upload file to entity field", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; const fileData = new Blob(["fake-image-data"], { type: "image/jpeg" }); - t.plan(4); + expect.assertions(4); server.use( http.post( "*/jsonapi/node/with-file/" + uuid + "/field_image", async ({ request }) => { - t.is(request.headers.get("Content-Type"), "application/octet-stream"); - t.is( - request.headers.get("Content-Disposition"), + expect(request.headers.get("Content-Type")).toBe( + "application/octet-stream", + ); + expect(request.headers.get("Content-Disposition")).toBe( 'file; filename="test-image.jpg"', ); const body = await request.arrayBuffer(); - t.is(body.byteLength, fileData.size); + expect(body.byteLength).toBe(fileData.size); return HttpResponse.json(JsonApiFileUpload, { status: 201, @@ -864,25 +865,25 @@ test.serial("Upload file to entity field", async (t) => { ); const res = result.unwrap(); - t.is(res.data?.type, "file--file"); + expect(res.data?.type).toBe("file--file"); }); -test.serial("Upload file with custom request options", async (t) => { - t.plan(2); +test("Upload file with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; const fileData = new Blob(["fake-data"]); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post( "*/jsonapi/node/with-file/" + uuid + "/field_image", ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(JsonApiFileUpload, { status: 201, @@ -909,19 +910,18 @@ test.serial("Upload file with custom request options", async (t) => { ); }); -test.serial("Upload file sanitizes filename", async (t) => { +test("Upload file sanitizes filename", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; const fileData = new Blob(["fake-data"]); - t.plan(1); + expect.assertions(1); server.use( http.post( "*/jsonapi/node/with-file/" + uuid + "/field_image", ({ request }) => { - t.is( - request.headers.get("Content-Disposition"), + expect(request.headers.get("Content-Disposition")).toBe( 'file; filename="ueber-bild.jpg"', ); @@ -944,7 +944,7 @@ test.serial("Upload file sanitizes filename", async (t) => { ); }); -test.serial("Upload file validates filename extension", async (t) => { +test("Upload file validates filename extension", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; const fileData = new Blob(["fake-data"]); @@ -959,12 +959,12 @@ test.serial("Upload file validates filename extension", async (t) => { const err = result.expectErr("Expect validation error"); - t.assert(err instanceof DrupalkitJsonApiError); - t.is(err.message, "Filename must include a file extension"); - t.is(err.statusCode, 400); + expect(err instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(err.message).toBe("Filename must include a file extension"); + expect(err.statusCode).toBe(400); }); -test.serial("Handle error when uploading file", async (t) => { +test("Handle error when uploading file", async () => { const drupalkit = createDrupalkit(); const uuid = "5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f"; const fileData = new Blob(["fake-data"]); @@ -990,6 +990,6 @@ test.serial("Handle error when uploading file", async (t) => { const error = result.expectErr("Expect error"); - t.assert(error instanceof DrupalkitJsonApiError); - t.is(error.statusCode, 400); + expect(error instanceof DrupalkitJsonApiError).toBeTruthy(); + expect(error.statusCode).toBe(400); }); diff --git a/packages/jsonapi/tests/snapshots/DrupalkitJsonApiError.test.ts.md b/packages/jsonapi/tests/snapshots/DrupalkitJsonApiError.test.ts.md deleted file mode 100644 index 9b901bb..0000000 --- a/packages/jsonapi/tests/snapshots/DrupalkitJsonApiError.test.ts.md +++ /dev/null @@ -1,46 +0,0 @@ -# Snapshot report for `tests/DrupalkitJsonApiError.test.ts` - -The actual snapshot is saved in `DrupalkitJsonApiError.test.ts.snap`. - -Generated by [AVA](https://avajs.dev). - -## Extract errors from JSON:API response - -> bad-request-errors - - [ - { - detail: '`wrong-field` is not a valid relationship field name. Possible values: node_type, revision_uid, uid, menu_link, field_media.', - links: { - info: { - href: 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1', - }, - via: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - status: '400', - title: 'Bad Request', - }, - ] - -> unprocessable-entity-errors - - [ - { - detail: 'field_firstname: Dieser Wert sollte nicht leer sein.', - source: { - pointer: '/data/attributes/field_firstname', - }, - status: '422', - title: 'Unprocessable Entity', - }, - { - detail: 'field_lastname: Dieser Wert sollte nicht leer sein.', - source: { - pointer: '/data/attributes/field_lastname', - }, - status: '422', - title: 'Unprocessable Entity', - }, - ] diff --git a/packages/jsonapi/tests/snapshots/DrupalkitJsonApiError.test.ts.snap b/packages/jsonapi/tests/snapshots/DrupalkitJsonApiError.test.ts.snap deleted file mode 100644 index 37a94f7..0000000 Binary files a/packages/jsonapi/tests/snapshots/DrupalkitJsonApiError.test.ts.snap and /dev/null differ diff --git a/packages/jsonapi/tests/snapshots/menu.test.ts.md b/packages/jsonapi/tests/snapshots/menu.test.ts.md deleted file mode 100644 index 9e47e16..0000000 --- a/packages/jsonapi/tests/snapshots/menu.test.ts.md +++ /dev/null @@ -1,54 +0,0 @@ -# Snapshot report for `tests/menu.test.ts` - -The actual snapshot is saved in `menu.test.ts.snap`. - -Generated by [AVA](https://avajs.dev). - -## Get menu items - -> Snapshot 1 - - { - data: [ - { - attributes: { - description: null, - enabled: true, - expanded: false, - menu_name: 'my_menu', - meta: { - entity_id: '2', - }, - options: [], - parent: '', - provider: 'menu_link_content', - route: { - name: 'entity.node.canonical', - parameters: { - node: '14', - }, - }, - title: 'Test-Page', - url: '/test-page', - weight: 0, - }, - id: 'menu_link_content:1c6d3476-61b1-4b7e-a580-6fa63bdac5d1', - type: 'menu_link_content--menu_link_content', - }, - ], - jsonapi: { - meta: { - links: { - self: { - href: 'http://jsonapi.org/format/1.0/', - }, - }, - }, - version: '1.0', - }, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/menu_items/my_menu', - }, - }, - } diff --git a/packages/jsonapi/tests/snapshots/menu.test.ts.snap b/packages/jsonapi/tests/snapshots/menu.test.ts.snap deleted file mode 100644 index 51a6c89..0000000 Binary files a/packages/jsonapi/tests/snapshots/menu.test.ts.snap and /dev/null differ diff --git a/packages/jsonapi/tests/snapshots/resources.test.ts.md b/packages/jsonapi/tests/snapshots/resources.test.ts.md deleted file mode 100644 index 525090e..0000000 --- a/packages/jsonapi/tests/snapshots/resources.test.ts.md +++ /dev/null @@ -1,2218 +0,0 @@ -# Snapshot report for `tests/resources.test.ts` - -The actual snapshot is saved in `resources.test.ts.snap`. - -Generated by [AVA](https://avajs.dev). - -## Build JSON:API url - -> Snapshot 1 - - 'https://my-drupal.com/jsonapi/node/article' - -> Snapshot 2 - - 'https://my-drupal.com/jsonapi/node/article?filter%5Bgroup%5D%5Bgroup%5D%5Bconjunction%5D=OR&filter%5Bstatus%5D%5Bcondition%5D%5Bpath%5D=status&filter%5Bstatus%5D%5Bcondition%5D%5Bvalue%5D=1&filter%5Bstatus%5D%5Bcondition%5D%5BmemberOf%5D=test&include=one%2Ctwo&sort=name&revision=5' - -> Snapshot 3 - - 'https://my-drupal.com/es/jsonapi/node/article' - -> Snapshot 4 - - 'https://my-drupal.com/es/jsonapi/node/article?filter%5Bgroup%5D%5Bgroup%5D%5Bconjunction%5D=OR&filter%5Bstatus%5D%5Bcondition%5D%5Bpath%5D=status&filter%5Bstatus%5D%5Bcondition%5D%5Bvalue%5D=1&filter%5Bstatus%5D%5Bcondition%5D%5BmemberOf%5D=test&include=one%2Ctwo&sort=name&revision=5' - -## Get JSON:API index - -> Snapshot 1 - - '{"jsonapi":{"version":"1.0","meta":{"links":{"self":{"href":"http://jsonapi.org/format/1.0/"}}}},"data":[],"links":{"file--file":{"href":"https://my-drupal.com/jsonapi/file/file"},"media--image":{"href":"https://my-drupal.com/jsonapi/media/image"},"node--article":{"href":"https://my-drupal.com/jsonapi/node/article"},"node--page":{"href":"https://my-drupal.com/jsonapi/node/page"},"self":{"href":"https://my-drupal.com/jsonapi"},"user--user":{"href":"https://my-drupal.com/jsonapi/user/user"}}}' - -## Get JSON:API resource - -> Snapshot 1 - - { - data: { - attributes: { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - langcode: 'de', - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - }, - id: '5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - relationships: { - field_media: { - data: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - meta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/field_media?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/field_media?resourceVersion=id%3A3', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/node_type?resourceVersion=id%3A3', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/revision_uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/revision_uid?resourceVersion=id%3A3', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/uid?resourceVersion=id%3A3', - }, - }, - }, - }, - type: 'node--article', - }, - jsonapi: { - meta: { - links: { - self: { - href: 'http://jsonapi.org/format/1.0/', - }, - }, - }, - version: '1.0', - }, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e?resourceVersion=id%3A3', - }, - }, - } - -## Simplify single resource - -> Snapshot 1 - - { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - field_media: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - resourceIdObjMeta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - id: '5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - } - -## Get localized JSON:API resource - -> Snapshot 1 - - { - data: { - attributes: { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - langcode: 'de', - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - }, - id: '5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - relationships: { - field_media: { - data: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - meta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/field_media?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/field_media?resourceVersion=id%3A3', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/node_type?resourceVersion=id%3A3', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/revision_uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/revision_uid?resourceVersion=id%3A3', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/uid?resourceVersion=id%3A3', - }, - }, - }, - }, - type: 'node--article', - }, - jsonapi: { - meta: { - links: { - self: { - href: 'http://jsonapi.org/format/1.0/', - }, - }, - }, - version: '1.0', - }, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e?resourceVersion=id%3A3', - }, - }, - } - -## Get JSON:API resource with query parameters - -> Snapshot 1 - - 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f?include=uid&resourceVersion=id%3A3' - -## Get many resources - -> Snapshot 1 - - { - data: [ - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - langcode: 'de', - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - }, - id: '58e198f6-41af-48b0-b889-596adcb9622e', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e?resourceVersion=id%3A3', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - relationships: { - field_media: { - data: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - meta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/field_media?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/field_media?resourceVersion=id%3A3', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/node_type?resourceVersion=id%3A3', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/revision_uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/revision_uid?resourceVersion=id%3A3', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/uid?resourceVersion=id%3A3', - }, - }, - }, - }, - type: 'node--article', - }, - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen. Bei den Tricks, die alle Supermärkte anwenden, handelt es sich manchmal um sinnvolle Maßnahmen, die jeder Einzelhändler ergreifen sollte, um seinen Umsatz zu steigern - aber einige sind vielleicht subtiler und weniger offensichtlich, als Sie vielleicht denken.

Da Artikel und Dokumentarfilme, die das Bewusstsein der Verbraucher schärfen, dieses Thema häufig aufgreifen, ist es wahrscheinlich, dass es den Einzelhändlern immer schwerer fällt, mit den offensichtlichsten Tricks durchzukommen. Wir werden immer versiertere Verbraucher, und es gibt wahrscheinlich nicht viel, was uns entgeht. Aber hier sind ein paar Tricks des Einzelhandels, die Sie beachten sollten, wenn Sie im Supermarkt Ihre wöchentlichen Vorräte aufstocken.

', - summary: 'Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen.', - value: '

Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen. Bei den Tricks, die alle Supermärkte anwenden, handelt es sich manchmal um sinnvolle Maßnahmen, die jeder Einzelhändler ergreifen sollte, um seinen Umsatz zu steigern - aber einige sind vielleicht subtiler und weniger offensichtlich, als Sie vielleicht denken.

Da Artikel und Dokumentarfilme, die das Bewusstsein der Verbraucher schärfen, dieses Thema häufig aufgreifen, ist es wahrscheinlich, dass es den Einzelhändlern immer schwerer fällt, mit den offensichtlichsten Tricks durchzukommen. Wir werden immer versiertere Verbraucher, und es gibt wahrscheinlich nicht viel, was uns entgeht. Aber hier sind ein paar Tricks des Einzelhandels, die Sie beachten sollten, wenn Sie im Supermarkt Ihre wöchentlichen Vorräte aufstocken.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T10:01:50+00:00', - default_langcode: true, - drupal_internal__nid: 4, - drupal_internal__vid: 4, - langcode: 'de', - path: { - alias: '/articles/das-richtige-angebot-fuer-den-einkauf-im-supermarkt', - langcode: 'de', - pid: 5, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Das richtige Angebot für den Einkauf im Supermarkt', - }, - id: '331b2b21-41f9-46bf-a043-4856441ec83f', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f?resourceVersion=id%3A4', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/real-deal-supermarket-savvy-shopping', - }, - ], - }, - relationships: { - field_media: { - data: { - id: 'b45b811e-f377-413e-bc76-7c59f14e6b4a', - meta: { - drupal_internal__target_id: 2, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f/field_media?resourceVersion=id%3A4', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f/relationships/field_media?resourceVersion=id%3A4', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f/relationships/node_type?resourceVersion=id%3A4', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f/revision_uid?resourceVersion=id%3A4', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f/relationships/revision_uid?resourceVersion=id%3A4', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f/uid?resourceVersion=id%3A4', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f/relationships/uid?resourceVersion=id%3A4', - }, - }, - }, - }, - type: 'node--article', - }, - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.

Außenbereich

Minze

Minze ist eine großartige Pflanze, denn sie ist winterhart und kann in fast jedem Boden wachsen. Minze kann wild wachsen, also halten Sie sie in einem Topf, sonst könnte sie sich ausbreiten und Ihren ganzen Garten erobern.

Salbei

Wie die Minze ist auch der Salbei eine sehr wüchsige Pflanze, die sich in Ihrem Garten ausbreitet, wenn Sie es zulassen. Die hocharomatische Salbeipflanze kann in einem Topf oder einem Blumenbeet in gut durchlässigem Boden gepflanzt werden. Die beste Art, das Kraut aufzubewahren, ist, die Blätter an der Sonne zu trocknen und in einem kühlen, dunklen Schrank in einem verschlossenen Behälter aufzubewahren.

Rosmarin

Rosmarinpflanzen wachsen zu schönen Sträuchern heran. Rosmarin lässt sich leicht aus Stecklingen ziehen, aber er verträgt keinen Frost. Stellen Sie daher Töpfe oder gepflanzte Sträucher in der Nähe des Hauses auf, um sie vor der Kälte zu schützen. Er wächst gut in Töpfen, da er trockene Böden mag, kann aber auch gut in der Erde überleben. Wenn Sie den Rosmarin beschneiden, um ihn in eine bessere Form zu bringen, bewahren Sie die Zweige auf und hängen Sie sie kopfüber auf, um das Aroma zu bewahren und sie für Lebensmittel zu verwenden.

', - summary: 'Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.', - value: '

Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.

Außenbereich

Minze

Minze ist eine großartige Pflanze, denn sie ist winterhart und kann in fast jedem Boden wachsen. Minze kann wild wachsen, also halten Sie sie in einem Topf, sonst könnte sie sich ausbreiten und Ihren ganzen Garten erobern.

Salbei

Wie die Minze ist auch der Salbei eine sehr wüchsige Pflanze, die sich in Ihrem Garten ausbreitet, wenn Sie es zulassen. Die hocharomatische Salbeipflanze kann in einem Topf oder einem Blumenbeet in gut durchlässigem Boden gepflanzt werden. Die beste Art, das Kraut aufzubewahren, ist, die Blätter an der Sonne zu trocknen und in einem kühlen, dunklen Schrank in einem verschlossenen Behälter aufzubewahren.

Rosmarin

Rosmarinpflanzen wachsen zu schönen Sträuchern heran. Rosmarin lässt sich leicht aus Stecklingen ziehen, aber er verträgt keinen Frost. Stellen Sie daher Töpfe oder gepflanzte Sträucher in der Nähe des Hauses auf, um sie vor der Kälte zu schützen. Er wächst gut in Töpfen, da er trockene Böden mag, kann aber auch gut in der Erde überleben. Wenn Sie den Rosmarin beschneiden, um ihn in eine bessere Form zu bringen, bewahren Sie die Zweige auf und hängen Sie sie kopfüber auf, um das Aroma zu bewahren und sie für Lebensmittel zu verwenden.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:11:24+00:00', - default_langcode: true, - drupal_internal__nid: 5, - drupal_internal__vid: 5, - langcode: 'de', - path: { - alias: '/articles/probieren-sie-es-aus-und-bauen-sie-ihre-eigenen-kraeuter', - langcode: 'de', - pid: 6, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Probieren Sie es aus und bauen Sie Ihre eigenen Kräuter an!', - }, - id: 'c0250ce7-7b5b-4b41-8557-647c858b29a2', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2?resourceVersion=id%3A5', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/give-it-go-and-grow-your-own-herbs', - }, - ], - }, - relationships: { - field_media: { - data: { - id: 'e8871454-98b9-4421-988b-b63b7e749566', - meta: { - drupal_internal__target_id: 7, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2/field_media?resourceVersion=id%3A5', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2/relationships/field_media?resourceVersion=id%3A5', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2/relationships/node_type?resourceVersion=id%3A5', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2/revision_uid?resourceVersion=id%3A5', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2/relationships/revision_uid?resourceVersion=id%3A5', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2/uid?resourceVersion=id%3A5', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2/relationships/uid?resourceVersion=id%3A5', - }, - }, - }, - }, - type: 'node--article', - }, - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.

Wer kocht nicht gern mit diesem vielseitigen Wurzelgemüse? Wir braten sie, kochen sie, mischen sie in Suppen und reiben sie in Salate. Die bescheidene Karotte ist eines unserer Lieblingsgemüse und wird schon seit Tausenden von Jahren angebaut. Damals gab es aber eher lila, rote, gelbe oder weiße Karotten und nicht die orangefarbenen, die wir heute so gut kennen.

Was ist also passiert? Seit wann ist Orange die bevorzugte Farbe?

Es waren die Niederländer im 17. Jahrhundert, die die orangefarbene Sorte kultivierten und populär machten, wahrscheinlich wegen ihrer leuchtenden Farbe und ihres höheren Gehalts an Beta-Carotin. Es wird auch vermutet, dass sie als Hommage an Wilhelm von Oranien angebaut wurde, der den Kampf um die Unabhängigkeit der Niederlande anführte.

Aus welchem Grund auch immer, die orangefarbene Sorte hat sich durchgesetzt, aber halten Sie auf Bauernmärkten und in Lebensmittelgeschäften Ausschau nach den "Heritage"-Sorten, deren Mischung aus Violett, Gelb, Orange und Weiß sich besonders gut zum Kochen eignet und als Beilage absolut großartig aussieht.

', - summary: 'Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.', - value: '

Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.

Wer kocht nicht gern mit diesem vielseitigen Wurzelgemüse? Wir braten sie, kochen sie, mischen sie in Suppen und reiben sie in Salate. Die bescheidene Karotte ist eines unserer Lieblingsgemüse und wird schon seit Tausenden von Jahren angebaut. Damals gab es aber eher lila, rote, gelbe oder weiße Karotten und nicht die orangefarbenen, die wir heute so gut kennen.

Was ist also passiert? Seit wann ist Orange die bevorzugte Farbe?

Es waren die Niederländer im 17. Jahrhundert, die die orangefarbene Sorte kultivierten und populär machten, wahrscheinlich wegen ihrer leuchtenden Farbe und ihres höheren Gehalts an Beta-Carotin. Es wird auch vermutet, dass sie als Hommage an Wilhelm von Oranien angebaut wurde, der den Kampf um die Unabhängigkeit der Niederlande anführte.

Aus welchem Grund auch immer, die orangefarbene Sorte hat sich durchgesetzt, aber halten Sie auf Bauernmärkten und in Lebensmittelgeschäften Ausschau nach den "Heritage"-Sorten, deren Mischung aus Violett, Gelb, Orange und Weiß sich besonders gut zum Kochen eignet und als Beilage absolut großartig aussieht.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:19:20+00:00', - default_langcode: true, - drupal_internal__nid: 6, - drupal_internal__vid: 6, - langcode: 'de', - path: { - alias: '/articles/ein-applaus-fuer-karotten', - langcode: 'de', - pid: 7, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Ein Applaus für Karotten', - }, - id: '708bbe72-76d1-4e20-919c-e2785e0d7e93', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93?resourceVersion=id%3A6', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/lets-hear-it-carrots', - }, - ], - }, - relationships: { - field_media: { - data: { - id: '5560b9b2-b088-44e8-9213-18864ee7c8ae', - meta: { - drupal_internal__target_id: 5, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93/field_media?resourceVersion=id%3A6', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93/relationships/field_media?resourceVersion=id%3A6', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93/relationships/node_type?resourceVersion=id%3A6', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93/revision_uid?resourceVersion=id%3A6', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93/relationships/revision_uid?resourceVersion=id%3A6', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93/uid?resourceVersion=id%3A6', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93/relationships/uid?resourceVersion=id%3A6', - }, - }, - }, - }, - type: 'node--article', - }, - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? Bevor Sie die langweiligen Haferflocken in die Tonne kloppen, sollten Sie sich von unseren Topping-Ideen inspirieren lassen, die Ihre Haferflocken in nur wenigen Minuten von fade zu kreativ und köstlich machen.

Ob Haferflocken, Haferbrei, heiße Müsli oder einfach nur Haferflocken - Haferflocken sind auf der ganzen Welt beliebt, aber in letzter Zeit bekommt der einfache Hafer ein neues Gesicht. Vorbei sind die Zeiten der faden Frühstücksschalen mit dickem Brei, denn Haferflocken werden jetzt als köstliche Mahlzeit serviert, die zu jeder Tageszeit passt. Trendige Haferflockenriegel, die man sich selbst zusammenstellen kann, tauchen immer häufiger auf und bieten das ultimative Haferflockenerlebnis. Die Gäste können wählen, ob die Haferflocken mit Milch oder Wasser zubereitet werden sollen, und dann nach Belieben süße, herzhafte oder sogar pikante Beläge hinzufügen.

Klingt toll, nicht wahr? Bevor Sie also die Tür des Schranks mit dem unscheinbaren Glas Haferflocken schließen, sollten Sie einige dieser leckeren Garnier-Ideen ausprobieren, um Ihre eigenen Haferflocken neu zu gestalten.

', - summary: 'Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? ', - value: '

Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? Bevor Sie die langweiligen Haferflocken in die Tonne kloppen, sollten Sie sich von unseren Topping-Ideen inspirieren lassen, die Ihre Haferflocken in nur wenigen Minuten von fade zu kreativ und köstlich machen.

Ob Haferflocken, Haferbrei, heiße Müsli oder einfach nur Haferflocken - Haferflocken sind auf der ganzen Welt beliebt, aber in letzter Zeit bekommt der einfache Hafer ein neues Gesicht. Vorbei sind die Zeiten der faden Frühstücksschalen mit dickem Brei, denn Haferflocken werden jetzt als köstliche Mahlzeit serviert, die zu jeder Tageszeit passt. Trendige Haferflockenriegel, die man sich selbst zusammenstellen kann, tauchen immer häufiger auf und bieten das ultimative Haferflockenerlebnis. Die Gäste können wählen, ob die Haferflocken mit Milch oder Wasser zubereitet werden sollen, und dann nach Belieben süße, herzhafte oder sogar pikante Beläge hinzufügen.

Klingt toll, nicht wahr? Bevor Sie also die Tür des Schranks mit dem unscheinbaren Glas Haferflocken schließen, sollten Sie einige dieser leckeren Garnier-Ideen ausprobieren, um Ihre eigenen Haferflocken neu zu gestalten.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:26:39+00:00', - default_langcode: true, - drupal_internal__nid: 7, - drupal_internal__vid: 7, - langcode: 'de', - path: { - alias: '/articles/verleihen-sie-ihren-haferflocken-das-ultimative-makeover', - langcode: 'de', - pid: 8, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Verleihen Sie Ihren Haferflocken das ultimative Makeover', - }, - id: 'bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3?resourceVersion=id%3A7', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/give-your-oatmeal-ultimate-makeover', - }, - ], - }, - relationships: { - field_media: { - data: { - id: 'f1af4452-15a1-46ec-9b7e-871212a0ee4c', - meta: { - drupal_internal__target_id: 1, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3/field_media?resourceVersion=id%3A7', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3/relationships/field_media?resourceVersion=id%3A7', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3/relationships/node_type?resourceVersion=id%3A7', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3/revision_uid?resourceVersion=id%3A7', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3/relationships/revision_uid?resourceVersion=id%3A7', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3/uid?resourceVersion=id%3A7', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3/relationships/uid?resourceVersion=id%3A7', - }, - }, - }, - }, - type: 'node--article', - }, - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? Denn was wäre eine Cocktailparty ohne die tollen Gläser mit gemischten Früchten, die bunten Farben und natürlich die kleinen Schirmchen? Tragen Sie Ihren Teil zum Umweltschutz bei, indem Sie auf Plastikstrohhalme verzichten und sich stattdessen ein paar tolle Alternativen aus Glas, Metall oder sogar Bambus besorgen. Vergessen Sie nicht die klassischen Cocktailgläser und die coolen Becher, die diese Rezepte erst so richtig zum Leben erwecken.

Also, schnappen Sie sich den Cocktailmixer, lassen Sie die Spirituosen weg und schütteln Sie diese köstlich erfrischenden Mocktails, die weder Ihnen noch Ihren Gästen an Geschmack oder Stil mangeln werden.

Mango-Köstlich

Dieser köstliche Drink hat einen raffinierten Geschmack, der ideal für Erwachsene ist, und die Farbe passt perfekt zu einem spätsommerlichen BBQ.

Anleitung

Schütteln Sie ein paar Gurkenscheiben und etwas Honig in einem Cocktailshaker gut durch. Einen Schuss Mangopüree und den Saft einer Limette hinzufügen. Etwas Eis hinzufügen und kräftig schütteln. Durch ein Sieb in ein schickes Glas gießen und mit Ginger Beer auffüllen. Ein paar Limettenscheiben, und schon kann es losgehen.

', - summary: 'Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? ', - value: '

Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? Denn was wäre eine Cocktailparty ohne die tollen Gläser mit gemischten Früchten, die bunten Farben und natürlich die kleinen Schirmchen? Tragen Sie Ihren Teil zum Umweltschutz bei, indem Sie auf Plastikstrohhalme verzichten und sich stattdessen ein paar tolle Alternativen aus Glas, Metall oder sogar Bambus besorgen. Vergessen Sie nicht die klassischen Cocktailgläser und die coolen Becher, die diese Rezepte erst so richtig zum Leben erwecken.

Also, schnappen Sie sich den Cocktailmixer, lassen Sie die Spirituosen weg und schütteln Sie diese köstlich erfrischenden Mocktails, die weder Ihnen noch Ihren Gästen an Geschmack oder Stil mangeln werden.

Mango-Köstlich

Dieser köstliche Drink hat einen raffinierten Geschmack, der ideal für Erwachsene ist, und die Farbe passt perfekt zu einem spätsommerlichen BBQ.

Anleitung

Schütteln Sie ein paar Gurkenscheiben und etwas Honig in einem Cocktailshaker gut durch. Einen Schuss Mangopüree und den Saft einer Limette hinzufügen. Etwas Eis hinzufügen und kräftig schütteln. Durch ein Sieb in ein schickes Glas gießen und mit Ginger Beer auffüllen. Ein paar Limettenscheiben, und schon kann es losgehen.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:24:15+00:00', - default_langcode: true, - drupal_internal__nid: 8, - drupal_internal__vid: 8, - langcode: 'de', - path: { - alias: '/articles/ueberspringen-sie-die-spirituosen-mit-koestlichen-mocktails', - langcode: 'de', - pid: 9, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Überspringen Sie die Spirituosen mit köstlichen Mocktails', - }, - id: '7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad?resourceVersion=id%3A8', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/skip-spirits-delicious-mocktails', - }, - ], - }, - relationships: { - field_media: { - data: { - id: 'de14673c-55ac-41ff-823d-3047e9ce2e72', - meta: { - drupal_internal__target_id: 3, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad/field_media?resourceVersion=id%3A8', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad/relationships/field_media?resourceVersion=id%3A8', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad/relationships/node_type?resourceVersion=id%3A8', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad/revision_uid?resourceVersion=id%3A8', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad/relationships/revision_uid?resourceVersion=id%3A8', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad/uid?resourceVersion=id%3A8', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad/relationships/uid?resourceVersion=id%3A8', - }, - }, - }, - }, - type: 'node--article', - }, - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. Und wer würde bei so kuriosen Namen wie Pfifferling, Zigeunerpilz, Füllhorn oder Waldhenne nicht gerne mehr über das Kochen mit dem mächtigen Pilz erfahren?

Eines der besten Dinge an Pilzen ist ihre Vielseitigkeit. Sie können gebraten, geröstet, gegrillt, gedünstet oder sogar in der Mikrowelle gegart werden. Und sie können als Hauptzutat für ein Gericht serviert oder einfach als Teil der Mischung hinzugefügt werden. Das macht Pilze zu einer idealen Wahl für die Zubereitung absolut köstlicher vegetarischer Gerichte.

Werfen wir also einen Blick auf einige unserer Lieblingspilzsorten. Vielleicht haben Sie noch nie mit ihnen gekocht, aber das sollte Sie nicht abschrecken. Mit ihren köstlichen, unverwechselbaren Aromen können Sie Suppen, Vorspeisen und Soßen ganz einfach verwandeln und erstaunliche Nudel- oder Pfannengerichte kreieren.

Probieren Sie die lieblichen Shiitake. Sie werden in der asiatischen Küche verwendet und können getrocknet und rehydriert gekauft werden, um einen kräftigen, tiefen Geschmack zu erhalten. Oder Sie kaufen ihn frisch und fügen ihn zu Suppen und Pfannengerichten hinzu. Dieser Pilz hat nicht nur einen intensiven Geschmack, sondern sieht auch schön aus. Das tiefe Braun und die glatten Formen geben Ihrem Gericht eine besondere Textur. In getrockneter Form und rehydriert sind sie die perfekte Ergänzung für eine tiefe und würzige Brühe für ein Risotto.

Der prächtige sonnige Pfifferling mit seinem gelben Fruchtfleisch hat einen fruchtigen Geschmack - aber es ist erwähnenswert, dass es viele Nachahmer gibt, und man sollte darauf achten, dass man die richtigen isst. Diese sehen in einem Omelett oder einer asiatischen Suppe großartig aus, um die Gelbtöne zu ergänzen.

Die braune Morchel bietet einen fleischigen und unverwechselbaren Geschmack, und Sie werden wahrscheinlich lieben, wie außergewöhnlich sie in einer Mahlzeit aussieht. Die Morchel ist ein beliebter Pilz im Frühjahr, wenn ihr Angebot besonders groß ist.

Eine Delikatesse ist der Enoki mit seinen winzigen weißen Köpfen, die in Büscheln wachsen. Sie können sogar roh in Salaten verzehrt werden. Schließlich können Sie den beliebten Austernpilz wählen. Sie heißen so, weil sie nicht wie ein Pilz aussehen, sondern den Innereien einer Auster ähneln, und ihr süßer Geschmack ist köstlich.

', - summary: 'Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. ', - value: '

Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. Und wer würde bei so kuriosen Namen wie Pfifferling, Zigeunerpilz, Füllhorn oder Waldhenne nicht gerne mehr über das Kochen mit dem mächtigen Pilz erfahren?

Eines der besten Dinge an Pilzen ist ihre Vielseitigkeit. Sie können gebraten, geröstet, gegrillt, gedünstet oder sogar in der Mikrowelle gegart werden. Und sie können als Hauptzutat für ein Gericht serviert oder einfach als Teil der Mischung hinzugefügt werden. Das macht Pilze zu einer idealen Wahl für die Zubereitung absolut köstlicher vegetarischer Gerichte.

Werfen wir also einen Blick auf einige unserer Lieblingspilzsorten. Vielleicht haben Sie noch nie mit ihnen gekocht, aber das sollte Sie nicht abschrecken. Mit ihren köstlichen, unverwechselbaren Aromen können Sie Suppen, Vorspeisen und Soßen ganz einfach verwandeln und erstaunliche Nudel- oder Pfannengerichte kreieren.

Probieren Sie die lieblichen Shiitake. Sie werden in der asiatischen Küche verwendet und können getrocknet und rehydriert gekauft werden, um einen kräftigen, tiefen Geschmack zu erhalten. Oder Sie kaufen ihn frisch und fügen ihn zu Suppen und Pfannengerichten hinzu. Dieser Pilz hat nicht nur einen intensiven Geschmack, sondern sieht auch schön aus. Das tiefe Braun und die glatten Formen geben Ihrem Gericht eine besondere Textur. In getrockneter Form und rehydriert sind sie die perfekte Ergänzung für eine tiefe und würzige Brühe für ein Risotto.

Der prächtige sonnige Pfifferling mit seinem gelben Fruchtfleisch hat einen fruchtigen Geschmack - aber es ist erwähnenswert, dass es viele Nachahmer gibt, und man sollte darauf achten, dass man die richtigen isst. Diese sehen in einem Omelett oder einer asiatischen Suppe großartig aus, um die Gelbtöne zu ergänzen.

Die braune Morchel bietet einen fleischigen und unverwechselbaren Geschmack, und Sie werden wahrscheinlich lieben, wie außergewöhnlich sie in einer Mahlzeit aussieht. Die Morchel ist ein beliebter Pilz im Frühjahr, wenn ihr Angebot besonders groß ist.

Eine Delikatesse ist der Enoki mit seinen winzigen weißen Köpfen, die in Büscheln wachsen. Sie können sogar roh in Salaten verzehrt werden. Schließlich können Sie den beliebten Austernpilz wählen. Sie heißen so, weil sie nicht wie ein Pilz aussehen, sondern den Innereien einer Auster ähneln, und ihr süßer Geschmack ist köstlich.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:17:34+00:00', - default_langcode: true, - drupal_internal__nid: 9, - drupal_internal__vid: 9, - langcode: 'de', - path: { - alias: '/articles/der-umami-fuehrer-fuer-unsere-lieblingspilze', - langcode: 'de', - pid: 10, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Der Umami-Führer für unsere Lieblingspilze', - }, - id: '5e462a38-de37-4769-add2-998db91fd839', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839?resourceVersion=id%3A9', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/umami-guide-our-favorite-mushrooms', - }, - ], - }, - relationships: { - field_media: { - data: { - id: '8f8895dc-d6e4-469f-8a89-f6729cdfed0f', - meta: { - drupal_internal__target_id: 4, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839/field_media?resourceVersion=id%3A9', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839/relationships/field_media?resourceVersion=id%3A9', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839/relationships/node_type?resourceVersion=id%3A9', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839/revision_uid?resourceVersion=id%3A9', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839/relationships/revision_uid?resourceVersion=id%3A9', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839/uid?resourceVersion=id%3A9', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839/relationships/uid?resourceVersion=id%3A9', - }, - }, - }, - }, - type: 'node--article', - }, - { - attributes: { - body: { - format: 'ckeditor', - processed: '

Sie halten sich an das Rezept, wiegen alle Zutaten ab, verwenden die richtige Ofentemperatur und nehmen sich Zeit - und trotzdem wird Ihr Gebäck am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar. Wir haben eine Liste mit den frustrierendsten Backproblemen und den Techniken, die Sie anwenden können, um sie zu vermeiden, zusammengestellt.

Ihr Kuchen ist nicht aufgegangen

Öffnen Sie auf keinen Fall die Ofentür mitten in der Backzeit. Die Hitze zischt heraus und zerstört die chemische Reaktion, die in Ihrem Kuchen stattfindet. Ihr Backtriebmittel braucht die Hitze, um aktiviert zu werden, und Sie könnten es abtöten.

Ihr Kuchen ist zäh und hart

Wenn Ihr heller Biskuit die Konsistenz eines Reservereifens hat, haben Sie Ihren Teig möglicherweise zu stark gemischt. Behalten Sie die Leichtigkeit Ihres Kuchenteigs bei, indem Sie die Zutaten vorsichtig und behutsam unterheben, aber nur so weit, bis sie gerade vermischt sind. Wenn Sie zu viel unterheben, tritt das Gluten im Mehl aus und Sie backen eher Brot als Kuchen.

', - summary: 'Man hält sich an das Rezept, wiegt alle Zutaten ab, wählt die richtige Ofentemperatur und nimmt sich Zeit, und trotzdem wird das Backwerk am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar.', - value: '

Sie halten sich an das Rezept, wiegen alle Zutaten ab, verwenden die richtige Ofentemperatur und nehmen sich Zeit - und trotzdem wird Ihr Gebäck am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar. Wir haben eine Liste mit den frustrierendsten Backproblemen und den Techniken, die Sie anwenden können, um sie zu vermeiden, zusammengestellt.

Ihr Kuchen ist nicht aufgegangen

Öffnen Sie auf keinen Fall die Ofentür mitten in der Backzeit. Die Hitze zischt heraus und zerstört die chemische Reaktion, die in Ihrem Kuchen stattfindet. Ihr Backtriebmittel braucht die Hitze, um aktiviert zu werden, und Sie könnten es abtöten.

Ihr Kuchen ist zäh und hart

Wenn Ihr heller Biskuit die Konsistenz eines Reservereifens hat, haben Sie Ihren Teig möglicherweise zu stark gemischt. Behalten Sie die Leichtigkeit Ihres Kuchenteigs bei, indem Sie die Zutaten vorsichtig und behutsam unterheben, aber nur so weit, bis sie gerade vermischt sind. Wenn Sie zu viel unterheben, tritt das Gluten im Mehl aus und Sie backen eher Brot als Kuchen.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:21:41+00:00', - default_langcode: true, - drupal_internal__nid: 10, - drupal_internal__vid: 10, - langcode: 'de', - path: { - alias: '/articles/missgeschicke-beim-backen-unsere-tipps-zur-fehlerbehebung', - langcode: 'de', - pid: 11, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Missgeschicke beim Backen - unsere Tipps zur Fehlerbehebung', - }, - id: '79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30?resourceVersion=id%3A10', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/baking-mishaps-our-troubleshooting-tips', - }, - ], - }, - relationships: { - field_media: { - data: { - id: 'e1a3b027-8983-44a7-bbea-403898ba0260', - meta: { - drupal_internal__target_id: 6, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30/field_media?resourceVersion=id%3A10', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30/relationships/field_media?resourceVersion=id%3A10', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30/relationships/node_type?resourceVersion=id%3A10', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30/revision_uid?resourceVersion=id%3A10', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30/relationships/revision_uid?resourceVersion=id%3A10', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30/uid?resourceVersion=id%3A10', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30/relationships/uid?resourceVersion=id%3A10', - }, - }, - }, - }, - type: 'node--article', - }, - ], - jsonapi: { - meta: { - links: { - self: { - href: 'http://jsonapi.org/format/1.0/', - }, - }, - }, - version: '1.0', - }, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article', - }, - }, - } - -## Simplify many resources - -> Snapshot 1 - - [ - { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - field_media: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - resourceIdObjMeta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - id: '58e198f6-41af-48b0-b889-596adcb9622e', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e?resourceVersion=id%3A3', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - { - body: { - format: 'ckeditor', - processed: '

Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen. Bei den Tricks, die alle Supermärkte anwenden, handelt es sich manchmal um sinnvolle Maßnahmen, die jeder Einzelhändler ergreifen sollte, um seinen Umsatz zu steigern - aber einige sind vielleicht subtiler und weniger offensichtlich, als Sie vielleicht denken.

Da Artikel und Dokumentarfilme, die das Bewusstsein der Verbraucher schärfen, dieses Thema häufig aufgreifen, ist es wahrscheinlich, dass es den Einzelhändlern immer schwerer fällt, mit den offensichtlichsten Tricks durchzukommen. Wir werden immer versiertere Verbraucher, und es gibt wahrscheinlich nicht viel, was uns entgeht. Aber hier sind ein paar Tricks des Einzelhandels, die Sie beachten sollten, wenn Sie im Supermarkt Ihre wöchentlichen Vorräte aufstocken.

', - summary: 'Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen.', - value: '

Das wird Sie vielleicht nicht überraschen - aber Ihr Supermarkt ist ein heißes Pflaster für ein Marketing-Chaos, das darauf abzielt, den Gewinn zu steigern und die Verbraucher dazu zu bringen, mehr auszugeben, als sie beabsichtigen. Bei den Tricks, die alle Supermärkte anwenden, handelt es sich manchmal um sinnvolle Maßnahmen, die jeder Einzelhändler ergreifen sollte, um seinen Umsatz zu steigern - aber einige sind vielleicht subtiler und weniger offensichtlich, als Sie vielleicht denken.

Da Artikel und Dokumentarfilme, die das Bewusstsein der Verbraucher schärfen, dieses Thema häufig aufgreifen, ist es wahrscheinlich, dass es den Einzelhändlern immer schwerer fällt, mit den offensichtlichsten Tricks durchzukommen. Wir werden immer versiertere Verbraucher, und es gibt wahrscheinlich nicht viel, was uns entgeht. Aber hier sind ein paar Tricks des Einzelhandels, die Sie beachten sollten, wenn Sie im Supermarkt Ihre wöchentlichen Vorräte aufstocken.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T10:01:50+00:00', - default_langcode: true, - drupal_internal__nid: 4, - drupal_internal__vid: 4, - field_media: { - id: 'b45b811e-f377-413e-bc76-7c59f14e6b4a', - resourceIdObjMeta: { - drupal_internal__target_id: 2, - }, - type: 'media--image', - }, - id: '331b2b21-41f9-46bf-a043-4856441ec83f', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/331b2b21-41f9-46bf-a043-4856441ec83f?resourceVersion=id%3A4', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/real-deal-supermarket-savvy-shopping', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/das-richtige-angebot-fuer-den-einkauf-im-supermarkt', - langcode: 'de', - pid: 5, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Das richtige Angebot für den Einkauf im Supermarkt', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - { - body: { - format: 'ckeditor', - processed: '

Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.

Außenbereich

Minze

Minze ist eine großartige Pflanze, denn sie ist winterhart und kann in fast jedem Boden wachsen. Minze kann wild wachsen, also halten Sie sie in einem Topf, sonst könnte sie sich ausbreiten und Ihren ganzen Garten erobern.

Salbei

Wie die Minze ist auch der Salbei eine sehr wüchsige Pflanze, die sich in Ihrem Garten ausbreitet, wenn Sie es zulassen. Die hocharomatische Salbeipflanze kann in einem Topf oder einem Blumenbeet in gut durchlässigem Boden gepflanzt werden. Die beste Art, das Kraut aufzubewahren, ist, die Blätter an der Sonne zu trocknen und in einem kühlen, dunklen Schrank in einem verschlossenen Behälter aufzubewahren.

Rosmarin

Rosmarinpflanzen wachsen zu schönen Sträuchern heran. Rosmarin lässt sich leicht aus Stecklingen ziehen, aber er verträgt keinen Frost. Stellen Sie daher Töpfe oder gepflanzte Sträucher in der Nähe des Hauses auf, um sie vor der Kälte zu schützen. Er wächst gut in Töpfen, da er trockene Böden mag, kann aber auch gut in der Erde überleben. Wenn Sie den Rosmarin beschneiden, um ihn in eine bessere Form zu bringen, bewahren Sie die Zweige auf und hängen Sie sie kopfüber auf, um das Aroma zu bewahren und sie für Lebensmittel zu verwenden.

', - summary: 'Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.', - value: '

Es gibt nichts Besseres, als einen eigenen Vorrat an frischen Kräutern zu haben, die man beim Kochen sofort verwenden kann. Egal, ob Sie einen großen Garten oder eine kleine Fensterbank in der Küche haben, es gibt immer genug Platz für etwas Selbstgezogenes.

Außenbereich

Minze

Minze ist eine großartige Pflanze, denn sie ist winterhart und kann in fast jedem Boden wachsen. Minze kann wild wachsen, also halten Sie sie in einem Topf, sonst könnte sie sich ausbreiten und Ihren ganzen Garten erobern.

Salbei

Wie die Minze ist auch der Salbei eine sehr wüchsige Pflanze, die sich in Ihrem Garten ausbreitet, wenn Sie es zulassen. Die hocharomatische Salbeipflanze kann in einem Topf oder einem Blumenbeet in gut durchlässigem Boden gepflanzt werden. Die beste Art, das Kraut aufzubewahren, ist, die Blätter an der Sonne zu trocknen und in einem kühlen, dunklen Schrank in einem verschlossenen Behälter aufzubewahren.

Rosmarin

Rosmarinpflanzen wachsen zu schönen Sträuchern heran. Rosmarin lässt sich leicht aus Stecklingen ziehen, aber er verträgt keinen Frost. Stellen Sie daher Töpfe oder gepflanzte Sträucher in der Nähe des Hauses auf, um sie vor der Kälte zu schützen. Er wächst gut in Töpfen, da er trockene Böden mag, kann aber auch gut in der Erde überleben. Wenn Sie den Rosmarin beschneiden, um ihn in eine bessere Form zu bringen, bewahren Sie die Zweige auf und hängen Sie sie kopfüber auf, um das Aroma zu bewahren und sie für Lebensmittel zu verwenden.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:11:24+00:00', - default_langcode: true, - drupal_internal__nid: 5, - drupal_internal__vid: 5, - field_media: { - id: 'e8871454-98b9-4421-988b-b63b7e749566', - resourceIdObjMeta: { - drupal_internal__target_id: 7, - }, - type: 'media--image', - }, - id: 'c0250ce7-7b5b-4b41-8557-647c858b29a2', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/c0250ce7-7b5b-4b41-8557-647c858b29a2?resourceVersion=id%3A5', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/give-it-go-and-grow-your-own-herbs', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/probieren-sie-es-aus-und-bauen-sie-ihre-eigenen-kraeuter', - langcode: 'de', - pid: 6, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Probieren Sie es aus und bauen Sie Ihre eigenen Kräuter an!', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - { - body: { - format: 'ckeditor', - processed: '

Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.

Wer kocht nicht gern mit diesem vielseitigen Wurzelgemüse? Wir braten sie, kochen sie, mischen sie in Suppen und reiben sie in Salate. Die bescheidene Karotte ist eines unserer Lieblingsgemüse und wird schon seit Tausenden von Jahren angebaut. Damals gab es aber eher lila, rote, gelbe oder weiße Karotten und nicht die orangefarbenen, die wir heute so gut kennen.

Was ist also passiert? Seit wann ist Orange die bevorzugte Farbe?

Es waren die Niederländer im 17. Jahrhundert, die die orangefarbene Sorte kultivierten und populär machten, wahrscheinlich wegen ihrer leuchtenden Farbe und ihres höheren Gehalts an Beta-Carotin. Es wird auch vermutet, dass sie als Hommage an Wilhelm von Oranien angebaut wurde, der den Kampf um die Unabhängigkeit der Niederlande anführte.

Aus welchem Grund auch immer, die orangefarbene Sorte hat sich durchgesetzt, aber halten Sie auf Bauernmärkten und in Lebensmittelgeschäften Ausschau nach den "Heritage"-Sorten, deren Mischung aus Violett, Gelb, Orange und Weiß sich besonders gut zum Kochen eignet und als Beilage absolut großartig aussieht.

', - summary: 'Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.', - value: '

Ein Hoch auf die bescheidene Karotte! Dieses süße und gesunde "Alltagsgemüse" hat es in sich. Großartiger Geschmack, fantastische Farbe, und wenn man den alten Geschichten Glauben schenkt, kann sie sogar helfen, im Dunkeln besser zu sehen.

Wer kocht nicht gern mit diesem vielseitigen Wurzelgemüse? Wir braten sie, kochen sie, mischen sie in Suppen und reiben sie in Salate. Die bescheidene Karotte ist eines unserer Lieblingsgemüse und wird schon seit Tausenden von Jahren angebaut. Damals gab es aber eher lila, rote, gelbe oder weiße Karotten und nicht die orangefarbenen, die wir heute so gut kennen.

Was ist also passiert? Seit wann ist Orange die bevorzugte Farbe?

Es waren die Niederländer im 17. Jahrhundert, die die orangefarbene Sorte kultivierten und populär machten, wahrscheinlich wegen ihrer leuchtenden Farbe und ihres höheren Gehalts an Beta-Carotin. Es wird auch vermutet, dass sie als Hommage an Wilhelm von Oranien angebaut wurde, der den Kampf um die Unabhängigkeit der Niederlande anführte.

Aus welchem Grund auch immer, die orangefarbene Sorte hat sich durchgesetzt, aber halten Sie auf Bauernmärkten und in Lebensmittelgeschäften Ausschau nach den "Heritage"-Sorten, deren Mischung aus Violett, Gelb, Orange und Weiß sich besonders gut zum Kochen eignet und als Beilage absolut großartig aussieht.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:19:20+00:00', - default_langcode: true, - drupal_internal__nid: 6, - drupal_internal__vid: 6, - field_media: { - id: '5560b9b2-b088-44e8-9213-18864ee7c8ae', - resourceIdObjMeta: { - drupal_internal__target_id: 5, - }, - type: 'media--image', - }, - id: '708bbe72-76d1-4e20-919c-e2785e0d7e93', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/708bbe72-76d1-4e20-919c-e2785e0d7e93?resourceVersion=id%3A6', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/lets-hear-it-carrots', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/ein-applaus-fuer-karotten', - langcode: 'de', - pid: 7, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Ein Applaus für Karotten', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - { - body: { - format: 'ckeditor', - processed: '

Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? Bevor Sie die langweiligen Haferflocken in die Tonne kloppen, sollten Sie sich von unseren Topping-Ideen inspirieren lassen, die Ihre Haferflocken in nur wenigen Minuten von fade zu kreativ und köstlich machen.

Ob Haferflocken, Haferbrei, heiße Müsli oder einfach nur Haferflocken - Haferflocken sind auf der ganzen Welt beliebt, aber in letzter Zeit bekommt der einfache Hafer ein neues Gesicht. Vorbei sind die Zeiten der faden Frühstücksschalen mit dickem Brei, denn Haferflocken werden jetzt als köstliche Mahlzeit serviert, die zu jeder Tageszeit passt. Trendige Haferflockenriegel, die man sich selbst zusammenstellen kann, tauchen immer häufiger auf und bieten das ultimative Haferflockenerlebnis. Die Gäste können wählen, ob die Haferflocken mit Milch oder Wasser zubereitet werden sollen, und dann nach Belieben süße, herzhafte oder sogar pikante Beläge hinzufügen.

Klingt toll, nicht wahr? Bevor Sie also die Tür des Schranks mit dem unscheinbaren Glas Haferflocken schließen, sollten Sie einige dieser leckeren Garnier-Ideen ausprobieren, um Ihre eigenen Haferflocken neu zu gestalten.

', - summary: 'Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? ', - value: '

Sie sind vegan, glutenfrei, fettarm, ballaststoffreich und können sogar den Cholesterinspiegel senken - aber Haferflocken sind doch langweilig, oder? Bevor Sie die langweiligen Haferflocken in die Tonne kloppen, sollten Sie sich von unseren Topping-Ideen inspirieren lassen, die Ihre Haferflocken in nur wenigen Minuten von fade zu kreativ und köstlich machen.

Ob Haferflocken, Haferbrei, heiße Müsli oder einfach nur Haferflocken - Haferflocken sind auf der ganzen Welt beliebt, aber in letzter Zeit bekommt der einfache Hafer ein neues Gesicht. Vorbei sind die Zeiten der faden Frühstücksschalen mit dickem Brei, denn Haferflocken werden jetzt als köstliche Mahlzeit serviert, die zu jeder Tageszeit passt. Trendige Haferflockenriegel, die man sich selbst zusammenstellen kann, tauchen immer häufiger auf und bieten das ultimative Haferflockenerlebnis. Die Gäste können wählen, ob die Haferflocken mit Milch oder Wasser zubereitet werden sollen, und dann nach Belieben süße, herzhafte oder sogar pikante Beläge hinzufügen.

Klingt toll, nicht wahr? Bevor Sie also die Tür des Schranks mit dem unscheinbaren Glas Haferflocken schließen, sollten Sie einige dieser leckeren Garnier-Ideen ausprobieren, um Ihre eigenen Haferflocken neu zu gestalten.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:26:39+00:00', - default_langcode: true, - drupal_internal__nid: 7, - drupal_internal__vid: 7, - field_media: { - id: 'f1af4452-15a1-46ec-9b7e-871212a0ee4c', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'media--image', - }, - id: 'bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/bc3ae841-d9fe-4f95-b1cd-bb5ac2ace4d3?resourceVersion=id%3A7', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/give-your-oatmeal-ultimate-makeover', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/verleihen-sie-ihren-haferflocken-das-ultimative-makeover', - langcode: 'de', - pid: 8, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Verleihen Sie Ihren Haferflocken das ultimative Makeover', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - { - body: { - format: 'ckeditor', - processed: '

Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? Denn was wäre eine Cocktailparty ohne die tollen Gläser mit gemischten Früchten, die bunten Farben und natürlich die kleinen Schirmchen? Tragen Sie Ihren Teil zum Umweltschutz bei, indem Sie auf Plastikstrohhalme verzichten und sich stattdessen ein paar tolle Alternativen aus Glas, Metall oder sogar Bambus besorgen. Vergessen Sie nicht die klassischen Cocktailgläser und die coolen Becher, die diese Rezepte erst so richtig zum Leben erwecken.

Also, schnappen Sie sich den Cocktailmixer, lassen Sie die Spirituosen weg und schütteln Sie diese köstlich erfrischenden Mocktails, die weder Ihnen noch Ihren Gästen an Geschmack oder Stil mangeln werden.

Mango-Köstlich

Dieser köstliche Drink hat einen raffinierten Geschmack, der ideal für Erwachsene ist, und die Farbe passt perfekt zu einem spätsommerlichen BBQ.

Anleitung

Schütteln Sie ein paar Gurkenscheiben und etwas Honig in einem Cocktailshaker gut durch. Einen Schuss Mangopüree und den Saft einer Limette hinzufügen. Etwas Eis hinzufügen und kräftig schütteln. Durch ein Sieb in ein schickes Glas gießen und mit Ginger Beer auffüllen. Ein paar Limettenscheiben, und schon kann es losgehen.

', - summary: 'Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? ', - value: '

Feiern Sie eine Cocktailparty? Warum dann nicht etwas ebenso Besonderes für diejenigen auftischen, die keinen Alkohol trinken, aber trotzdem mitfeiern wollen? Denn was wäre eine Cocktailparty ohne die tollen Gläser mit gemischten Früchten, die bunten Farben und natürlich die kleinen Schirmchen? Tragen Sie Ihren Teil zum Umweltschutz bei, indem Sie auf Plastikstrohhalme verzichten und sich stattdessen ein paar tolle Alternativen aus Glas, Metall oder sogar Bambus besorgen. Vergessen Sie nicht die klassischen Cocktailgläser und die coolen Becher, die diese Rezepte erst so richtig zum Leben erwecken.

Also, schnappen Sie sich den Cocktailmixer, lassen Sie die Spirituosen weg und schütteln Sie diese köstlich erfrischenden Mocktails, die weder Ihnen noch Ihren Gästen an Geschmack oder Stil mangeln werden.

Mango-Köstlich

Dieser köstliche Drink hat einen raffinierten Geschmack, der ideal für Erwachsene ist, und die Farbe passt perfekt zu einem spätsommerlichen BBQ.

Anleitung

Schütteln Sie ein paar Gurkenscheiben und etwas Honig in einem Cocktailshaker gut durch. Einen Schuss Mangopüree und den Saft einer Limette hinzufügen. Etwas Eis hinzufügen und kräftig schütteln. Durch ein Sieb in ein schickes Glas gießen und mit Ginger Beer auffüllen. Ein paar Limettenscheiben, und schon kann es losgehen.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:24:15+00:00', - default_langcode: true, - drupal_internal__nid: 8, - drupal_internal__vid: 8, - field_media: { - id: 'de14673c-55ac-41ff-823d-3047e9ce2e72', - resourceIdObjMeta: { - drupal_internal__target_id: 3, - }, - type: 'media--image', - }, - id: '7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/7ba6c26b-b6b6-4fdf-8566-c9dc1d2133ad?resourceVersion=id%3A8', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/skip-spirits-delicious-mocktails', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/ueberspringen-sie-die-spirituosen-mit-koestlichen-mocktails', - langcode: 'de', - pid: 9, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Überspringen Sie die Spirituosen mit köstlichen Mocktails', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - { - body: { - format: 'ckeditor', - processed: '

Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. Und wer würde bei so kuriosen Namen wie Pfifferling, Zigeunerpilz, Füllhorn oder Waldhenne nicht gerne mehr über das Kochen mit dem mächtigen Pilz erfahren?

Eines der besten Dinge an Pilzen ist ihre Vielseitigkeit. Sie können gebraten, geröstet, gegrillt, gedünstet oder sogar in der Mikrowelle gegart werden. Und sie können als Hauptzutat für ein Gericht serviert oder einfach als Teil der Mischung hinzugefügt werden. Das macht Pilze zu einer idealen Wahl für die Zubereitung absolut köstlicher vegetarischer Gerichte.

Werfen wir also einen Blick auf einige unserer Lieblingspilzsorten. Vielleicht haben Sie noch nie mit ihnen gekocht, aber das sollte Sie nicht abschrecken. Mit ihren köstlichen, unverwechselbaren Aromen können Sie Suppen, Vorspeisen und Soßen ganz einfach verwandeln und erstaunliche Nudel- oder Pfannengerichte kreieren.

Probieren Sie die lieblichen Shiitake. Sie werden in der asiatischen Küche verwendet und können getrocknet und rehydriert gekauft werden, um einen kräftigen, tiefen Geschmack zu erhalten. Oder Sie kaufen ihn frisch und fügen ihn zu Suppen und Pfannengerichten hinzu. Dieser Pilz hat nicht nur einen intensiven Geschmack, sondern sieht auch schön aus. Das tiefe Braun und die glatten Formen geben Ihrem Gericht eine besondere Textur. In getrockneter Form und rehydriert sind sie die perfekte Ergänzung für eine tiefe und würzige Brühe für ein Risotto.

Der prächtige sonnige Pfifferling mit seinem gelben Fruchtfleisch hat einen fruchtigen Geschmack - aber es ist erwähnenswert, dass es viele Nachahmer gibt, und man sollte darauf achten, dass man die richtigen isst. Diese sehen in einem Omelett oder einer asiatischen Suppe großartig aus, um die Gelbtöne zu ergänzen.

Die braune Morchel bietet einen fleischigen und unverwechselbaren Geschmack, und Sie werden wahrscheinlich lieben, wie außergewöhnlich sie in einer Mahlzeit aussieht. Die Morchel ist ein beliebter Pilz im Frühjahr, wenn ihr Angebot besonders groß ist.

Eine Delikatesse ist der Enoki mit seinen winzigen weißen Köpfen, die in Büscheln wachsen. Sie können sogar roh in Salaten verzehrt werden. Schließlich können Sie den beliebten Austernpilz wählen. Sie heißen so, weil sie nicht wie ein Pilz aussehen, sondern den Innereien einer Auster ähneln, und ihr süßer Geschmack ist köstlich.

', - summary: 'Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. ', - value: '

Wir sind der Meinung, dass Pilze eine der besten Zutaten sind, mit denen man kochen kann. Es gibt viele essbare Sorten, jede mit ihrer eigenen, unverwechselbaren Form, Größe und Geschmack. Und wer würde bei so kuriosen Namen wie Pfifferling, Zigeunerpilz, Füllhorn oder Waldhenne nicht gerne mehr über das Kochen mit dem mächtigen Pilz erfahren?

Eines der besten Dinge an Pilzen ist ihre Vielseitigkeit. Sie können gebraten, geröstet, gegrillt, gedünstet oder sogar in der Mikrowelle gegart werden. Und sie können als Hauptzutat für ein Gericht serviert oder einfach als Teil der Mischung hinzugefügt werden. Das macht Pilze zu einer idealen Wahl für die Zubereitung absolut köstlicher vegetarischer Gerichte.

Werfen wir also einen Blick auf einige unserer Lieblingspilzsorten. Vielleicht haben Sie noch nie mit ihnen gekocht, aber das sollte Sie nicht abschrecken. Mit ihren köstlichen, unverwechselbaren Aromen können Sie Suppen, Vorspeisen und Soßen ganz einfach verwandeln und erstaunliche Nudel- oder Pfannengerichte kreieren.

Probieren Sie die lieblichen Shiitake. Sie werden in der asiatischen Küche verwendet und können getrocknet und rehydriert gekauft werden, um einen kräftigen, tiefen Geschmack zu erhalten. Oder Sie kaufen ihn frisch und fügen ihn zu Suppen und Pfannengerichten hinzu. Dieser Pilz hat nicht nur einen intensiven Geschmack, sondern sieht auch schön aus. Das tiefe Braun und die glatten Formen geben Ihrem Gericht eine besondere Textur. In getrockneter Form und rehydriert sind sie die perfekte Ergänzung für eine tiefe und würzige Brühe für ein Risotto.

Der prächtige sonnige Pfifferling mit seinem gelben Fruchtfleisch hat einen fruchtigen Geschmack - aber es ist erwähnenswert, dass es viele Nachahmer gibt, und man sollte darauf achten, dass man die richtigen isst. Diese sehen in einem Omelett oder einer asiatischen Suppe großartig aus, um die Gelbtöne zu ergänzen.

Die braune Morchel bietet einen fleischigen und unverwechselbaren Geschmack, und Sie werden wahrscheinlich lieben, wie außergewöhnlich sie in einer Mahlzeit aussieht. Die Morchel ist ein beliebter Pilz im Frühjahr, wenn ihr Angebot besonders groß ist.

Eine Delikatesse ist der Enoki mit seinen winzigen weißen Köpfen, die in Büscheln wachsen. Sie können sogar roh in Salaten verzehrt werden. Schließlich können Sie den beliebten Austernpilz wählen. Sie heißen so, weil sie nicht wie ein Pilz aussehen, sondern den Innereien einer Auster ähneln, und ihr süßer Geschmack ist köstlich.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:17:34+00:00', - default_langcode: true, - drupal_internal__nid: 9, - drupal_internal__vid: 9, - field_media: { - id: '8f8895dc-d6e4-469f-8a89-f6729cdfed0f', - resourceIdObjMeta: { - drupal_internal__target_id: 4, - }, - type: 'media--image', - }, - id: '5e462a38-de37-4769-add2-998db91fd839', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5e462a38-de37-4769-add2-998db91fd839?resourceVersion=id%3A9', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/umami-guide-our-favorite-mushrooms', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/der-umami-fuehrer-fuer-unsere-lieblingspilze', - langcode: 'de', - pid: 10, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Der Umami-Führer für unsere Lieblingspilze', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - { - body: { - format: 'ckeditor', - processed: '

Sie halten sich an das Rezept, wiegen alle Zutaten ab, verwenden die richtige Ofentemperatur und nehmen sich Zeit - und trotzdem wird Ihr Gebäck am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar. Wir haben eine Liste mit den frustrierendsten Backproblemen und den Techniken, die Sie anwenden können, um sie zu vermeiden, zusammengestellt.

Ihr Kuchen ist nicht aufgegangen

Öffnen Sie auf keinen Fall die Ofentür mitten in der Backzeit. Die Hitze zischt heraus und zerstört die chemische Reaktion, die in Ihrem Kuchen stattfindet. Ihr Backtriebmittel braucht die Hitze, um aktiviert zu werden, und Sie könnten es abtöten.

Ihr Kuchen ist zäh und hart

Wenn Ihr heller Biskuit die Konsistenz eines Reservereifens hat, haben Sie Ihren Teig möglicherweise zu stark gemischt. Behalten Sie die Leichtigkeit Ihres Kuchenteigs bei, indem Sie die Zutaten vorsichtig und behutsam unterheben, aber nur so weit, bis sie gerade vermischt sind. Wenn Sie zu viel unterheben, tritt das Gluten im Mehl aus und Sie backen eher Brot als Kuchen.

', - summary: 'Man hält sich an das Rezept, wiegt alle Zutaten ab, wählt die richtige Ofentemperatur und nimmt sich Zeit, und trotzdem wird das Backwerk am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar.', - value: '

Sie halten sich an das Rezept, wiegen alle Zutaten ab, verwenden die richtige Ofentemperatur und nehmen sich Zeit - und trotzdem wird Ihr Gebäck am Ende matschig, geht nicht auf oder schmeckt einfach nur furchtbar. Wir haben eine Liste mit den frustrierendsten Backproblemen und den Techniken, die Sie anwenden können, um sie zu vermeiden, zusammengestellt.

Ihr Kuchen ist nicht aufgegangen

Öffnen Sie auf keinen Fall die Ofentür mitten in der Backzeit. Die Hitze zischt heraus und zerstört die chemische Reaktion, die in Ihrem Kuchen stattfindet. Ihr Backtriebmittel braucht die Hitze, um aktiviert zu werden, und Sie könnten es abtöten.

Ihr Kuchen ist zäh und hart

Wenn Ihr heller Biskuit die Konsistenz eines Reservereifens hat, haben Sie Ihren Teig möglicherweise zu stark gemischt. Behalten Sie die Leichtigkeit Ihres Kuchenteigs bei, indem Sie die Zutaten vorsichtig und behutsam unterheben, aber nur so weit, bis sie gerade vermischt sind. Wenn Sie zu viel unterheben, tritt das Gluten im Mehl aus und Sie backen eher Brot als Kuchen.

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-30T08:21:41+00:00', - default_langcode: true, - drupal_internal__nid: 10, - drupal_internal__vid: 10, - field_media: { - id: 'e1a3b027-8983-44a7-bbea-403898ba0260', - resourceIdObjMeta: { - drupal_internal__target_id: 6, - }, - type: 'media--image', - }, - id: '79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/79cb4f78-4ae0-4db1-b4e7-5fa9758b9a30?resourceVersion=id%3A10', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/baking-mishaps-our-troubleshooting-tips', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/missgeschicke-beim-backen-unsere-tipps-zur-fehlerbehebung', - langcode: 'de', - pid: 11, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Missgeschicke beim Backen - unsere Tipps zur Fehlerbehebung', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - }, - ] - -## Create new resource - -> Snapshot 1 - - { - data: { - attributes: { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - langcode: 'de', - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - }, - id: '5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - relationships: { - field_media: { - data: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - meta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/field_media?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/field_media?resourceVersion=id%3A3', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/node_type?resourceVersion=id%3A3', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/revision_uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/revision_uid?resourceVersion=id%3A3', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/uid?resourceVersion=id%3A3', - }, - }, - }, - }, - type: 'node--article', - }, - jsonapi: { - meta: { - links: { - self: { - href: 'http://jsonapi.org/format/1.0/', - }, - }, - }, - version: '1.0', - }, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e?resourceVersion=id%3A3', - }, - }, - } - -> Snapshot 2 - - { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - field_media: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - resourceIdObjMeta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - id: '5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - } - -## Update resource - -> Snapshot 1 - - { - data: { - attributes: { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - langcode: 'de', - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - }, - id: '5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - relationships: { - field_media: { - data: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - meta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/field_media?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/field_media?resourceVersion=id%3A3', - }, - }, - }, - node_type: { - data: null, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/node_type?resourceVersion=id%3A3', - }, - }, - }, - revision_uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/revision_uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/revision_uid?resourceVersion=id%3A3', - }, - }, - }, - uid: { - data: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - meta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - links: { - related: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/uid?resourceVersion=id%3A3', - }, - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e/relationships/uid?resourceVersion=id%3A3', - }, - }, - }, - }, - type: 'node--article', - }, - jsonapi: { - meta: { - links: { - self: { - href: 'http://jsonapi.org/format/1.0/', - }, - }, - }, - version: '1.0', - }, - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/58e198f6-41af-48b0-b889-596adcb9622e?resourceVersion=id%3A3', - }, - }, - } - -> Snapshot 2 - - { - body: { - format: 'ckeditor', - processed: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - summary: 'Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.', - value: '

Der Kalender ist voll von Ausreden für Schokoladenliebhaber, von religiösen Festen bis zu Geburtstagen - und sogar dem Weltschokoladentag am 7. Juli. Aber auch Veganer oder Menschen, die sich milchfrei ernähren, müssen nicht leer ausgehen, denn es gibt viele köstliche vegane Schokoladensorten im Angebot.

Milchfreie Milchschokolade wird weitgehend auf die gleiche Weise hergestellt wie normale Schokolade, bis zu dem Punkt, an dem die Milch hinzugefügt wird. Die Kakaobohnen werden ein paar Stunden lang geröstet, bis sie einen reichen Geschmack entwickeln. Die äußeren Schalen werden entfernt, und die inneren Nibs werden gemahlen, um eine Paste zu erhalten, die mit Kakaobutter gemischt werden kann, um den gewünschten Prozentsatz an Schokolade herzustellen. Aromen, Zucker und Milch werden in unterschiedlichen Mengen hinzugefügt, um die gewünschte Schokoladenart zu erhalten. Die Schokolade ist zu diesem Zeitpunkt körnig und muss püriert werden, bis sie glatt ist - das kann Tage dauern! Schließlich wird sie durch Erhitzen und Abkühlen temperiert, bis sie verzehrfertig ist.

Wie also bekommen vegane Chocolatiers den cremigen Milchgeschmack und die Textur in ihre Produkte?

', - }, - changed: '2023-02-23T11:10:08+00:00', - content_translation_outdated: false, - content_translation_source: 'und', - created: '2023-01-27T09:38:39+00:00', - default_langcode: true, - drupal_internal__nid: 3, - drupal_internal__vid: 3, - field_media: { - id: '6530afc8-5635-45b7-a3d9-920d7e10c39e', - resourceIdObjMeta: { - drupal_internal__target_id: 8, - }, - type: 'media--image', - }, - id: '5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - langcode: 'de', - links: { - self: { - href: 'https://my-drupal.com/jsonapi/node/article/5f5f5f5f-5f5f-5f5f-5f5f-5f5f5f5f5f5f', - }, - }, - meta: { - localeInfo: [ - { - langcode: 'en', - path: '/articles/dairy-free-and-delicious-milk-chocolate', - }, - ], - }, - node_type: null, - path: { - alias: '/articles/milchfreie-und-koestliche-milchschokolade', - langcode: 'de', - pid: 4, - }, - promote: true, - relationshipNames: [ - 'node_type', - 'revision_uid', - 'uid', - 'field_media', - ], - revision_log: null, - revision_timestamp: '2023-02-23T11:10:08+00:00', - revision_translation_affected: true, - revision_uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - status: true, - sticky: false, - title: 'Milchfreie und köstliche Milchschokolade', - type: 'node--article', - uid: { - id: 'f0d79a17-a6b1-4c34-ba1b-ae298826f68a', - resourceIdObjMeta: { - drupal_internal__target_id: 1, - }, - type: 'user--user', - }, - } diff --git a/packages/jsonapi/tests/snapshots/resources.test.ts.snap b/packages/jsonapi/tests/snapshots/resources.test.ts.snap deleted file mode 100644 index dae1adc..0000000 Binary files a/packages/jsonapi/tests/snapshots/resources.test.ts.snap and /dev/null differ diff --git a/packages/jsonapi/tests/utils.test.ts b/packages/jsonapi/tests/utils.test.ts index 771095c..fb8a360 100644 --- a/packages/jsonapi/tests/utils.test.ts +++ b/packages/jsonapi/tests/utils.test.ts @@ -1,72 +1,72 @@ -import test from "ava"; +import { expect, test } from "vitest"; import { isJsonApiRequest, sanitizeFilename } from "../src/utils.js"; -test("Check if JSON:API request", (t) => { - t.falsy( +test("Check if JSON:API request", () => { + expect( isJsonApiRequest({ method: "GET", headers: { "Content-type": "application/json", }, }), - ); + ).toBeFalsy(); - t.truthy( + expect( isJsonApiRequest({ method: "GET", headers: { "Content-type": "application/vnd.api+json", }, }), - ); + ).toBeTruthy(); }); -test("sanitizeFilename - passes normal filename through", (t) => { - t.is(sanitizeFilename("test.jpg"), "test.jpg"); - t.is(sanitizeFilename("my-file.pdf"), "my-file.pdf"); - t.is(sanitizeFilename("document_v2.docx"), "document_v2.docx"); +test("sanitizeFilename - passes normal filename through", () => { + expect(sanitizeFilename("test.jpg")).toBe("test.jpg"); + expect(sanitizeFilename("my-file.pdf")).toBe("my-file.pdf"); + expect(sanitizeFilename("document_v2.docx")).toBe("document_v2.docx"); }); -test("sanitizeFilename - transliterates German umlauts", (t) => { - t.is(sanitizeFilename("über.jpg"), "ueber.jpg"); - t.is(sanitizeFilename("Müller.pdf"), "Mueller.pdf"); - t.is(sanitizeFilename("größe.txt"), "groesse.txt"); - t.is(sanitizeFilename("Äpfel.png"), "Aepfel.png"); +test("sanitizeFilename - transliterates German umlauts", () => { + expect(sanitizeFilename("über.jpg")).toBe("ueber.jpg"); + expect(sanitizeFilename("Müller.pdf")).toBe("Mueller.pdf"); + expect(sanitizeFilename("größe.txt")).toBe("groesse.txt"); + expect(sanitizeFilename("Äpfel.png")).toBe("Aepfel.png"); }); -test("sanitizeFilename - removes diacritics", (t) => { - t.is(sanitizeFilename("café.jpg"), "cafe.jpg"); - t.is(sanitizeFilename("naïve.pdf"), "naive.pdf"); - t.is(sanitizeFilename("résumé.docx"), "resume.docx"); +test("sanitizeFilename - removes diacritics", () => { + expect(sanitizeFilename("café.jpg")).toBe("cafe.jpg"); + expect(sanitizeFilename("naïve.pdf")).toBe("naive.pdf"); + expect(sanitizeFilename("résumé.docx")).toBe("resume.docx"); }); -test("sanitizeFilename - removes illegal characters", (t) => { - t.is(sanitizeFilename("file?.jpg"), "file.jpg"); - t.is(sanitizeFilename("file.pdf"), "filename.pdf"); - t.is(sanitizeFilename('file"name.txt'), "filename.txt"); - t.is(sanitizeFilename("file:name.png"), "filename.png"); - t.is(sanitizeFilename("file|name.jpg"), "filename.jpg"); - t.is(sanitizeFilename("file*name.pdf"), "filename.pdf"); +test("sanitizeFilename - removes illegal characters", () => { + expect(sanitizeFilename("file?.jpg")).toBe("file.jpg"); + expect(sanitizeFilename("file.pdf")).toBe("filename.pdf"); + expect(sanitizeFilename('file"name.txt')).toBe("filename.txt"); + expect(sanitizeFilename("file:name.png")).toBe("filename.png"); + expect(sanitizeFilename("file|name.jpg")).toBe("filename.jpg"); + expect(sanitizeFilename("file*name.pdf")).toBe("filename.pdf"); }); -test("sanitizeFilename - handles Windows reserved names", (t) => { - t.is(sanitizeFilename("con.txt"), "file.txt"); - t.is(sanitizeFilename("PRN.pdf"), "file.pdf"); - t.is(sanitizeFilename("aux.jpg"), "file.jpg"); - t.is(sanitizeFilename("NUL.png"), "file.png"); - t.is(sanitizeFilename("COM1.txt"), "file.txt"); - t.is(sanitizeFilename("lpt2.pdf"), "file.pdf"); +test("sanitizeFilename - handles Windows reserved names", () => { + expect(sanitizeFilename("con.txt")).toBe("file.txt"); + expect(sanitizeFilename("PRN.pdf")).toBe("file.pdf"); + expect(sanitizeFilename("aux.jpg")).toBe("file.jpg"); + expect(sanitizeFilename("NUL.png")).toBe("file.png"); + expect(sanitizeFilename("COM1.txt")).toBe("file.txt"); + expect(sanitizeFilename("lpt2.pdf")).toBe("file.pdf"); }); -test("sanitizeFilename - handles edge cases", (t) => { - t.is(sanitizeFilename(".."), "file"); - t.is(sanitizeFilename("..."), "file"); - t.is(sanitizeFilename("file."), "file"); - t.is(sanitizeFilename(""), "file"); +test("sanitizeFilename - handles edge cases", () => { + expect(sanitizeFilename("..")).toBe("file"); + expect(sanitizeFilename("...")).toBe("file"); + expect(sanitizeFilename("file.")).toBe("file"); + expect(sanitizeFilename("")).toBe("file"); }); -test("sanitizeFilename - preserves extension", (t) => { - t.is(sanitizeFilename("über-größe.jpeg"), "ueber-groesse.jpeg"); - t.is(sanitizeFilename("my.file.name.pdf"), "my.file.name.pdf"); +test("sanitizeFilename - preserves extension", () => { + expect(sanitizeFilename("über-größe.jpeg")).toBe("ueber-groesse.jpeg"); + expect(sanitizeFilename("my.file.name.pdf")).toBe("my.file.name.pdf"); }); diff --git a/packages/jsonapi/vitest.config.ts b/packages/jsonapi/vitest.config.ts new file mode 100644 index 0000000..e587732 --- /dev/null +++ b/packages/jsonapi/vitest.config.ts @@ -0,0 +1,3 @@ +import { createVitestConfig } from "@drupal-kit/config-vitest/vitest"; + +export default createVitestConfig(); diff --git a/packages/simple-oauth-auth-code/package.json b/packages/simple-oauth-auth-code/package.json index 54d6209..44f8032 100644 --- a/packages/simple-oauth-auth-code/package.json +++ b/packages/simple-oauth-auth-code/package.json @@ -9,39 +9,29 @@ }, "devDependencies": { "@drupal-kit/config-typescript": "workspace:0.13.4", + "@drupal-kit/config-vitest": "workspace:0.13.4", "@drupal-kit/eslint-config": "workspace:0.13.4", "@drupal-kit/types": "workspace:0.13.4", "@rollup/plugin-typescript": "^11.1.1", "@swc/core": "^1.6.5", - "ava": "^6.1.3", "esbuild": "^0.27.3", "msw": "^2.14.6", "rollup": "^4.59.0", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1", "ts-node": "^10.9.2", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "^3.2.4" }, "scripts": { "build": "rollup -c", "lint": "eslint --ext .ts,.tsx src --max-warnings 0", - "test": "NODE_NO_WARNINGS=1 ava", + "test": "NODE_NO_WARNINGS=1 vitest run", + "test:watch": "NODE_NO_WARNINGS=1 vitest", "typecheck": "pnpm run '/(typecheck:.*)/'", "typecheck:src": "tsc --project ./tsconfig.json", "typecheck:tests": "tsc --project ./tsconfig.tests.json" }, - "ava": { - "files": [ - "tests/**/*.test.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader", - "ts-node/esm" - ] - }, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/simple-oauth-auth-code/tests/DrupalkitSimpleOauthAuthCode.test.ts b/packages/simple-oauth-auth-code/tests/DrupalkitSimpleOauthAuthCode.test.ts index 961af42..78ffde6 100644 --- a/packages/simple-oauth-auth-code/tests/DrupalkitSimpleOauthAuthCode.test.ts +++ b/packages/simple-oauth-auth-code/tests/DrupalkitSimpleOauthAuthCode.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; import { Drupalkit, DrupalkitOptions } from "@drupal-kit/core"; @@ -24,19 +24,19 @@ const createDrupalkit = ( const server = setupServer(); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); -test.serial("Request auth code", async (t) => { +test("Request auth code", async () => { const drupalkit = createDrupalkit(); const operation = "register"; @@ -51,16 +51,16 @@ test.serial("Request auth code", async (t) => { const result = await drupalkit.simpleOauth.requestAuthCode(operation, email); const res = result.unwrap(); - t.deepEqual(res, AuthCodeResponse); + expect(res).toEqual(AuthCodeResponse); }); -test.serial("Request auth code with custom request options", async (t) => { - t.plan(2); +test("Request auth code with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); const operation = "register"; @@ -68,7 +68,7 @@ test.serial("Request auth code with custom request options", async (t) => { server.use( http.post("*/simple-oauth/auth-code", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(AuthCodeResponse); }), @@ -82,7 +82,7 @@ test.serial("Request auth code with custom request options", async (t) => { }); }); -test.serial("Request auth code with explicit endpoint", async (t) => { +test("Request auth code with explicit endpoint", async () => { const endpoint = "/custom/auth-code"; const drupalkit = createDrupalkit({ baseUrl: BASE_URL, @@ -100,10 +100,10 @@ test.serial("Request auth code with explicit endpoint", async (t) => { const result = await drupalkit.simpleOauth.requestAuthCode(operation, email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle network error", async (t) => { +test("Handle network error", async () => { const drupalkit = createDrupalkit(); const operation = "register"; @@ -115,5 +115,5 @@ test.serial("Handle network error", async (t) => { const result = await drupalkit.simpleOauth.requestAuthCode(operation, email); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); diff --git a/packages/simple-oauth-auth-code/vitest.config.ts b/packages/simple-oauth-auth-code/vitest.config.ts new file mode 100644 index 0000000..e587732 --- /dev/null +++ b/packages/simple-oauth-auth-code/vitest.config.ts @@ -0,0 +1,3 @@ +import { createVitestConfig } from "@drupal-kit/config-vitest/vitest"; + +export default createVitestConfig(); diff --git a/packages/simple-oauth/package.json b/packages/simple-oauth/package.json index e1745b3..81ce6ab 100644 --- a/packages/simple-oauth/package.json +++ b/packages/simple-oauth/package.json @@ -9,39 +9,29 @@ }, "devDependencies": { "@drupal-kit/config-typescript": "workspace:0.13.4", + "@drupal-kit/config-vitest": "workspace:0.13.4", "@drupal-kit/eslint-config": "workspace:0.13.4", "@drupal-kit/types": "workspace:0.13.4", "@rollup/plugin-typescript": "^11.1.1", "@swc/core": "^1.6.5", - "ava": "^6.1.3", "esbuild": "^0.27.3", "msw": "^2.14.6", "rollup": "^4.59.0", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1", "ts-node": "^10.9.2", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "^3.2.4" }, "scripts": { "build": "rollup -c", "lint": "eslint --ext .ts,.tsx src --max-warnings 0", - "test": "NODE_NO_WARNINGS=1 ava", + "test": "NODE_NO_WARNINGS=1 vitest run", + "test:watch": "NODE_NO_WARNINGS=1 vitest", "typecheck": "pnpm run '/(typecheck:.*)/'", "typecheck:src": "tsc --project ./tsconfig.json", "typecheck:tests": "tsc --project ./tsconfig.tests.json" }, - "ava": { - "files": [ - "tests/**/*.test.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader", - "ts-node/esm" - ] - }, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/simple-oauth/tests/DrupalkitSimpleOauth.test.ts b/packages/simple-oauth/tests/DrupalkitSimpleOauth.test.ts index b4bbe81..72432c6 100644 --- a/packages/simple-oauth/tests/DrupalkitSimpleOauth.test.ts +++ b/packages/simple-oauth/tests/DrupalkitSimpleOauth.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; import { Drupalkit, DrupalkitError, DrupalkitOptions } from "@drupal-kit/core"; @@ -32,26 +32,26 @@ const createDrupalkit = ( const server = setupServer(); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); -test("Instanciate with plugin", (t) => { +test("Instanciate with plugin", () => { const drupalkit = createDrupalkit(); - t.assert(drupalkit.hasOwnProperty("simpleOauth")); + expect(drupalkit.hasOwnProperty("simpleOauth")).toBeTruthy(); }); -test.serial("Request token with client credentials grant", async (t) => { - t.plan(3); +test("Request token with client credentials grant", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); @@ -59,11 +59,10 @@ test.serial("Request token with client credentials grant", async (t) => { http.post("*/oauth/token", async ({ request }) => { const body = await request.text(); - t.is( - request.headers.get("content-type"), + expect(request.headers.get("content-type")).toBe( "application/x-www-form-urlencoded", ); - t.snapshot(body); + expect(body).toMatchSnapshot(); return HttpResponse.json(TokenResponse); }), @@ -79,21 +78,21 @@ test.serial("Request token with client credentials grant", async (t) => { const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); }); -test.serial("Request token with custom request options", async (t) => { - t.plan(2); +test("Request token with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/oauth/token", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(TokenResponse); }), @@ -114,8 +113,8 @@ test.serial("Request token with custom request options", async (t) => { ); }); -test.serial("Request token authenticated", async (t) => { - t.plan(3); +test("Request token authenticated", async () => { + expect.assertions(3); const authinfo = "Bearer abc123"; @@ -123,13 +122,13 @@ test.serial("Request token authenticated", async (t) => { drupalkit.setAuth(authinfo); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/oauth/token", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); - t.is(request.headers.get("Authorization"), authinfo); + expect(request.headers.get("X-Custom")).toBe("1"); + expect(request.headers.get("Authorization")).toBe(authinfo); return HttpResponse.json(TokenResponse); }), @@ -151,7 +150,7 @@ test.serial("Request token authenticated", async (t) => { ); }); -test.serial("Request token with explicit endpoint", async (t) => { +test("Request token with explicit endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, oauthTokenEndpoint: "/custom/token", @@ -169,10 +168,10 @@ test.serial("Request token with explicit endpoint", async (t) => { }, ); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle request errors", async (t) => { +test("Handle request errors", async () => { const drupalkit = createDrupalkit(); server.use( @@ -191,11 +190,11 @@ test.serial("Handle request errors", async (t) => { const res = result.expectErr("Expected error"); - t.assert(res instanceof DrupalkitSimpleOauthError); - t.is(res.statusCode, 400); + expect(res instanceof DrupalkitSimpleOauthError).toBeTruthy(); + expect(res.statusCode).toBe(400); }); -test.serial("Handle network errors", async (t) => { +test("Handle network errors", async () => { const drupalkit = createDrupalkit(); server.use(http.post("*/oauth/token", async () => HttpResponse.error())); @@ -209,31 +208,28 @@ test.serial("Handle network errors", async (t) => { ); const error = result.expectErr("Expected error"); - t.assert(error.message.includes("Failed to fetch")); + expect(error.message.includes("Failed to fetch")).toBeTruthy(); }); -test.serial( - "Do not produce DrupalkitSimpleOauthErrors when not requesting a token", - async (t) => { - const drupalkit = createDrupalkit(); +test("Do not produce DrupalkitSimpleOauthErrors when not requesting a token", async () => { + const drupalkit = createDrupalkit(); - server.use( - http.post("*/not/oauth/related", async () => - HttpResponse.text(null, { status: 400 }), - ), - ); + server.use( + http.post("*/not/oauth/related", async () => + HttpResponse.text(null, { status: 400 }), + ), + ); - const result = await drupalkit.request("/not/oauth/related", { - method: "POST", - }); + const result = await drupalkit.request("/not/oauth/related", { + method: "POST", + }); - const error = result.expectErr("Expected error"); + const error = result.expectErr("Expected error"); - t.assert(!(error instanceof DrupalkitSimpleOauthError)); - }, -); + expect(!(error instanceof DrupalkitSimpleOauthError)).toBeTruthy(); +}); -test.serial("Request user info", async (t) => { +test("Request user info", async () => { const drupalkit = createDrupalkit(); server.use( @@ -246,21 +242,21 @@ test.serial("Request user info", async (t) => { const res = result.unwrap(); - t.snapshot(res); + expect(res).toMatchSnapshot(); }); -test.serial("Request user info with custom request options", async (t) => { - t.plan(2); +test("Request user info with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.get("*/oauth/userinfo", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(UserInfoResponse); }), @@ -274,7 +270,7 @@ test.serial("Request user info with custom request options", async (t) => { }); }); -test.serial("Request user info with explicit endpoint", async (t) => { +test("Request user info with explicit endpoint", async () => { const endpoint = "/custom/userinfo"; const drupalkit = createDrupalkit({ @@ -290,10 +286,10 @@ test.serial("Request user info with explicit endpoint", async (t) => { const result = await drupalkit.simpleOauth.getUserInfo(); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle request errors when requesting user info", async (t) => { +test("Handle request errors when requesting user info", async () => { const drupalkit = createDrupalkit(); server.use( @@ -306,7 +302,7 @@ test.serial("Handle request errors when requesting user info", async (t) => { const error = result.expectErr("Expected error"); - t.assert(!(error instanceof DrupalkitSimpleOauthError)); - t.assert(error instanceof DrupalkitError); - t.is(error.statusCode, 400); + expect(!(error instanceof DrupalkitSimpleOauthError)).toBeTruthy(); + expect(error instanceof DrupalkitError).toBeTruthy(); + expect(error.statusCode).toBe(400); }); diff --git a/packages/simple-oauth/tests/DrupalkitSimpleOauthError.test.ts b/packages/simple-oauth/tests/DrupalkitSimpleOauthError.test.ts index 9cec6ee..ad56fe5 100644 --- a/packages/simple-oauth/tests/DrupalkitSimpleOauthError.test.ts +++ b/packages/simple-oauth/tests/DrupalkitSimpleOauthError.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { expect, test } from "vitest"; import { DrupalkitError } from "@drupal-kit/core"; import { DrupalkitResponse } from "@drupal-kit/types"; @@ -37,7 +37,7 @@ const createResponse = ({ }; }; -test("Instanciate from DrupalkitError", (t) => { +test("Instanciate from DrupalkitError", () => { const error = new DrupalkitError("test-error", 400, { request, response: createResponse({}), @@ -45,11 +45,11 @@ test("Instanciate from DrupalkitError", (t) => { const soError = DrupalkitSimpleOauthError.fromDrupalkitError(error); - t.assert(soError instanceof DrupalkitSimpleOauthError); - t.assert(soError instanceof DrupalkitError); + expect(soError instanceof DrupalkitSimpleOauthError).toBeTruthy(); + expect(soError instanceof DrupalkitError).toBeTruthy(); }); -test("OauthError should be null if error does not contain simple oauth error data", (t) => { +test("OauthError should be null if error does not contain simple oauth error data", () => { // Without response. let error = new DrupalkitError("test-error", 400, { request, @@ -57,8 +57,8 @@ test("OauthError should be null if error does not contain simple oauth error dat let soError = DrupalkitSimpleOauthError.fromDrupalkitError(error); - t.assert(soError instanceof DrupalkitSimpleOauthError); - t.is(soError.error, null); + expect(soError instanceof DrupalkitSimpleOauthError).toBeTruthy(); + expect(soError.error).toBe(null); // Without payload. error = new DrupalkitError("test-error", 400, { @@ -71,8 +71,8 @@ test("OauthError should be null if error does not contain simple oauth error dat soError = DrupalkitSimpleOauthError.fromDrupalkitError(error); - t.assert(soError instanceof DrupalkitSimpleOauthError); - t.is(soError.error, null); + expect(soError instanceof DrupalkitSimpleOauthError).toBeTruthy(); + expect(soError.error).toBe(null); // With invalid payload. error = new DrupalkitError("test-error", 400, { @@ -87,11 +87,11 @@ test("OauthError should be null if error does not contain simple oauth error dat soError = DrupalkitSimpleOauthError.fromDrupalkitError(error); - t.assert(soError instanceof DrupalkitSimpleOauthError); - t.is(soError.error, null); + expect(soError instanceof DrupalkitSimpleOauthError).toBeTruthy(); + expect(soError.error).toBe(null); }); -test("Set error type, hint and message from response", (t) => { +test("Set error type, hint and message from response", () => { const type: SimpleOauthError = "access_denied"; const hint = "Insufficent permissions"; const message = @@ -111,12 +111,12 @@ test("Set error type, hint and message from response", (t) => { error, ) as DrupalkitSimpleOauthError; - t.is(soError.error, type); - t.is(soError.hint, hint); - t.is(soError.message, message); + expect(soError.error).toBe(type); + expect(soError.hint).toBe(hint); + expect(soError.message).toBe(message); }); -test("Get type of invalid_request error", (t) => { +test("Get type of invalid_request error", () => { const type: SimpleOauthError = "invalid_request"; const hint = "Check the `client_secret` parameter"; @@ -132,10 +132,12 @@ test("Get type of invalid_request error", (t) => { error, ) as DrupalkitSimpleOauthError; - t.is(soError.getInvalidRequestType(), "invalid_parameter_client_secret"); + expect(soError.getInvalidRequestType()).toBe( + "invalid_parameter_client_secret", + ); }); -test("Get invalid_request type as generic if no hint is set", (t) => { +test("Get invalid_request type as generic if no hint is set", () => { const type: SimpleOauthError = "invalid_request"; const hint = ""; @@ -151,10 +153,10 @@ test("Get invalid_request type as generic if no hint is set", (t) => { error, ) as DrupalkitSimpleOauthError; - t.is(soError.getInvalidRequestType(), "generic"); + expect(soError.getInvalidRequestType()).toBe("generic"); }); -test("Get invalid_request type by hint", (t) => { +test("Get invalid_request type by hint", () => { const type: SimpleOauthError = "invalid_request"; const hint = "Authorization code has been revoked"; @@ -170,10 +172,10 @@ test("Get invalid_request type by hint", (t) => { error, ) as DrupalkitSimpleOauthError; - t.is(soError.getInvalidRequestType(), "auth_code_revoked"); + expect(soError.getInvalidRequestType()).toBe("auth_code_revoked"); }); -test("Get invalid_request type by hint regexp", (t) => { +test("Get invalid_request type by hint regexp", () => { const type: SimpleOauthError = "invalid_request"; const hint = "Code challenge method must be one of one, two, three."; @@ -189,10 +191,10 @@ test("Get invalid_request type by hint regexp", (t) => { error, ) as DrupalkitSimpleOauthError; - t.is(soError.getInvalidRequestType(), "code_challenge_invalid"); + expect(soError.getInvalidRequestType()).toBe("code_challenge_invalid"); }); -test("Get invalid_request type as generic if nothing matches", (t) => { +test("Get invalid_request type as generic if nothing matches", () => { const type: SimpleOauthError = "invalid_request"; const hint = "Some unhandled hint."; @@ -208,5 +210,5 @@ test("Get invalid_request type as generic if nothing matches", (t) => { error, ) as DrupalkitSimpleOauthError; - t.is(soError.getInvalidRequestType(), "generic"); + expect(soError.getInvalidRequestType()).toBe("generic"); }); diff --git a/packages/simple-oauth/tests/__snapshots__/DrupalkitSimpleOauth.test.ts.snap b/packages/simple-oauth/tests/__snapshots__/DrupalkitSimpleOauth.test.ts.snap new file mode 100644 index 0000000..a8755f5 --- /dev/null +++ b/packages/simple-oauth/tests/__snapshots__/DrupalkitSimpleOauth.test.ts.snap @@ -0,0 +1,26 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Request token with client credentials grant 1`] = `"grant_type=client_credentials&client_id=12345678901234567890123456789012&client_secret=F9w1cM0GQw7GjjQUaZcscWHtxnMOvn4d"`; + +exports[`Request token with client credentials grant 2`] = ` +{ + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjZlN2ZiNmJiODNlNzZkOWIxOGU2MjAxZmZhYmY3YWFlZTE2OTZiOTMxZGFjOGQ0YTI4ODY0ZjJlZTI1ODA1MGYyZDAwZDE5NDYwNDAxMjY2In0.eyJhdWQiOiJtdm5wb2dJWU9zSngyQWw4YUdaemtIeTYzU3JJYVVvLU16d0pBSzhWU0lNIiwianRpIjoiNmU3ZmI2YmI4M2U3NmQ5YjE4ZTYyMDFmZmFiZjdhYWVlMTY5NmI5MzFkYWM4ZDRhMjg4NjRmMmVlMjU4MDUwZjJkMDBkMTk0NjA0MDEyNjYiLCJpYXQiOjE2Nzg4NjYzODksIm5iZiI6MTY3ODg2NjM4OSwiZXhwIjoxNjc4ODY2Njg5Ljk2MjIzMzEsInNjb3BlIjpbImFsbG93X2FsbCJdLCJzdWIiOiI0NSJ9.A6Ur4pciXtNSEl8mWEjW1DUll-zR4wSXjnS4FMnDHIV_mWclAQxgHuUexSYWnDASCpKC9TohqtSa2aPB_ymP2JhSaz9FTtYklFR2CmYz01HgZ83GGisNhvbdCx67CaWfluVS1fT-PFmut6fAUhbDWcQ7pt3sAZR4Rc6-iCWWe-lx5fnrli5S_egzuqJGj-NkoDg9s3yU6yvsPT8dObN-5Bl2qfU9VA21PYuWo89e-rm56mLexiuGUtPZHOa1hXGQ3WPEu3qlThtxaFgPyN4mFA0ql7JLV4Md2DrMcU8U598epro41KH9K6OvxOKbGFkQupXE-lODN0f3E4O_6Mr0Xk7YNtAna117fQEuoeAusJb024aZ-pxKNN2xjFUbesa6XvCNnkNKgDCaXWL4RWngD7lftYncsH6JoocqRgMmdzZGlf1A4fZtqaFs_DdC_JjjX2eyZZqVLv-wuJ04iGm3BXblUfBjaVx96tlhF30rfih9Cyu3psCzzoE7JLbqeAL6Vn6hQdZmZOyV_36-pOWEdZopy5tY2pZAmUAuUJFG40VEAGzh3dCuSVmni16f0OgQDS_ZpV0xCwEIc_UpkemY33EPVGU8zWWtztELVY6mAzEsR8xZwmtL8x4HadkU4wKoemos1KwwDM26tLR_x6K3H38aMl-M1PuCp2xkQg1gF_s", + "expires_in": 300, + "refresh_token": "def50200ca9707211ac5eee68c5acf9a84f169e0655580b3cfbadf479465d3cb26923cafdc827f459429b6445168c81903df56d2005df9212b63b712158a4b3e59db82c894bc9c88571e44dc8366b9951c5e11799b6b18fbb1eca8774986a6a161a8c4bebd6c604958bd123a3daef60adc2dd9e7acbf1c6003a5d1357a7c325da9b5123a8db4b1eed60abe7697019833359029d824599c799d5cd44ff4a21b3dc1f63b1ab96b958b77d91c2fde6c856762c47228cade0a2847c6c52abdd40ba960e1a53be4e5096489e73ecf143050c33c8ed63bd49d73ac56bf323fa970e77ba85faadf5896311cef5e2f3496f2a9851b48041d3b66227438f347e41726ac914467fd4f671b949f30c597a251e31b54811ab134f937f0df60c4b99819eb621fb3d110f07005354ea459e64b05d4642fa784d002b900047f63d8f55ca7cbbd4b1398e0ca1a6f2b76a1ec43a21072c4c3ba46846e7ae643682a7d8f4fa601de9febc854dd7f799323fccd4c4e94b5351022a6acbcb91462063707df028f9af39936ffe3a2a6127f5ce39b31260ff11c21fb43a8cd91fe4dff75", + "token_type": "Bearer", +} +`; + +exports[`Request user info 1`] = ` +{ + "email": "Patience_Larkin75@example.org", + "email_verified": true, + "locale": "de", + "name": "Marcelo.Ziemann61", + "preferred_username": "Marcelo.Ziemann61", + "profile": "https://my-drupal.com/users/marceloziemann61", + "sub": "45", + "updated_at": "1678809645", + "zoneinfo": null, +} +`; diff --git a/packages/simple-oauth/tests/snapshots/DrupalkitSimpleOauth.test.ts.md b/packages/simple-oauth/tests/snapshots/DrupalkitSimpleOauth.test.ts.md deleted file mode 100644 index fb5153f..0000000 --- a/packages/simple-oauth/tests/snapshots/DrupalkitSimpleOauth.test.ts.md +++ /dev/null @@ -1,36 +0,0 @@ -# Snapshot report for `tests/DrupalkitSimpleOauth.test.ts` - -The actual snapshot is saved in `DrupalkitSimpleOauth.test.ts.snap`. - -Generated by [AVA](https://avajs.dev). - -## Request token with client credentials grant - -> Snapshot 1 - - 'grant_type=client_credentials&client_id=12345678901234567890123456789012&client_secret=F9w1cM0GQw7GjjQUaZcscWHtxnMOvn4d' - -> Snapshot 2 - - { - access_token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjZlN2ZiNmJiODNlNzZkOWIxOGU2MjAxZmZhYmY3YWFlZTE2OTZiOTMxZGFjOGQ0YTI4ODY0ZjJlZTI1ODA1MGYyZDAwZDE5NDYwNDAxMjY2In0.eyJhdWQiOiJtdm5wb2dJWU9zSngyQWw4YUdaemtIeTYzU3JJYVVvLU16d0pBSzhWU0lNIiwianRpIjoiNmU3ZmI2YmI4M2U3NmQ5YjE4ZTYyMDFmZmFiZjdhYWVlMTY5NmI5MzFkYWM4ZDRhMjg4NjRmMmVlMjU4MDUwZjJkMDBkMTk0NjA0MDEyNjYiLCJpYXQiOjE2Nzg4NjYzODksIm5iZiI6MTY3ODg2NjM4OSwiZXhwIjoxNjc4ODY2Njg5Ljk2MjIzMzEsInNjb3BlIjpbImFsbG93X2FsbCJdLCJzdWIiOiI0NSJ9.A6Ur4pciXtNSEl8mWEjW1DUll-zR4wSXjnS4FMnDHIV_mWclAQxgHuUexSYWnDASCpKC9TohqtSa2aPB_ymP2JhSaz9FTtYklFR2CmYz01HgZ83GGisNhvbdCx67CaWfluVS1fT-PFmut6fAUhbDWcQ7pt3sAZR4Rc6-iCWWe-lx5fnrli5S_egzuqJGj-NkoDg9s3yU6yvsPT8dObN-5Bl2qfU9VA21PYuWo89e-rm56mLexiuGUtPZHOa1hXGQ3WPEu3qlThtxaFgPyN4mFA0ql7JLV4Md2DrMcU8U598epro41KH9K6OvxOKbGFkQupXE-lODN0f3E4O_6Mr0Xk7YNtAna117fQEuoeAusJb024aZ-pxKNN2xjFUbesa6XvCNnkNKgDCaXWL4RWngD7lftYncsH6JoocqRgMmdzZGlf1A4fZtqaFs_DdC_JjjX2eyZZqVLv-wuJ04iGm3BXblUfBjaVx96tlhF30rfih9Cyu3psCzzoE7JLbqeAL6Vn6hQdZmZOyV_36-pOWEdZopy5tY2pZAmUAuUJFG40VEAGzh3dCuSVmni16f0OgQDS_ZpV0xCwEIc_UpkemY33EPVGU8zWWtztELVY6mAzEsR8xZwmtL8x4HadkU4wKoemos1KwwDM26tLR_x6K3H38aMl-M1PuCp2xkQg1gF_s', - expires_in: 300, - refresh_token: 'def50200ca9707211ac5eee68c5acf9a84f169e0655580b3cfbadf479465d3cb26923cafdc827f459429b6445168c81903df56d2005df9212b63b712158a4b3e59db82c894bc9c88571e44dc8366b9951c5e11799b6b18fbb1eca8774986a6a161a8c4bebd6c604958bd123a3daef60adc2dd9e7acbf1c6003a5d1357a7c325da9b5123a8db4b1eed60abe7697019833359029d824599c799d5cd44ff4a21b3dc1f63b1ab96b958b77d91c2fde6c856762c47228cade0a2847c6c52abdd40ba960e1a53be4e5096489e73ecf143050c33c8ed63bd49d73ac56bf323fa970e77ba85faadf5896311cef5e2f3496f2a9851b48041d3b66227438f347e41726ac914467fd4f671b949f30c597a251e31b54811ab134f937f0df60c4b99819eb621fb3d110f07005354ea459e64b05d4642fa784d002b900047f63d8f55ca7cbbd4b1398e0ca1a6f2b76a1ec43a21072c4c3ba46846e7ae643682a7d8f4fa601de9febc854dd7f799323fccd4c4e94b5351022a6acbcb91462063707df028f9af39936ffe3a2a6127f5ce39b31260ff11c21fb43a8cd91fe4dff75', - token_type: 'Bearer', - } - -## Request user info - -> Snapshot 1 - - { - email: 'Patience_Larkin75@example.org', - email_verified: true, - locale: 'de', - name: 'Marcelo.Ziemann61', - preferred_username: 'Marcelo.Ziemann61', - profile: 'https://my-drupal.com/users/marceloziemann61', - sub: '45', - updated_at: '1678809645', - zoneinfo: null, - } diff --git a/packages/simple-oauth/tests/snapshots/DrupalkitSimpleOauth.test.ts.snap b/packages/simple-oauth/tests/snapshots/DrupalkitSimpleOauth.test.ts.snap deleted file mode 100644 index 91ad799..0000000 Binary files a/packages/simple-oauth/tests/snapshots/DrupalkitSimpleOauth.test.ts.snap and /dev/null differ diff --git a/packages/simple-oauth/vitest.config.ts b/packages/simple-oauth/vitest.config.ts new file mode 100644 index 0000000..e587732 --- /dev/null +++ b/packages/simple-oauth/vitest.config.ts @@ -0,0 +1,3 @@ +import { createVitestConfig } from "@drupal-kit/config-vitest/vitest"; + +export default createVitestConfig(); diff --git a/packages/user-api/package.json b/packages/user-api/package.json index a7edb2a..627b98f 100644 --- a/packages/user-api/package.json +++ b/packages/user-api/package.json @@ -10,40 +10,30 @@ }, "devDependencies": { "@drupal-kit/config-typescript": "workspace:0.13.4", + "@drupal-kit/config-vitest": "workspace:0.13.4", "@drupal-kit/eslint-config": "workspace:0.13.4", "@drupal-kit/types": "workspace:0.13.4", "@rollup/plugin-typescript": "^11.1.1", "@swc/core": "^1.6.5", "@types/util-deprecate": "^1.0.3", - "ava": "^6.1.3", "esbuild": "^0.27.3", "msw": "^2.14.6", "rollup": "^4.59.0", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1", "ts-node": "^10.9.2", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "^3.2.4" }, "scripts": { "build": "rollup -c", "lint": "eslint --ext .ts,.tsx src --max-warnings 0", - "test": "NODE_NO_WARNINGS=1 ava", + "test": "NODE_NO_WARNINGS=1 vitest run", + "test:watch": "NODE_NO_WARNINGS=1 vitest", "typecheck": "pnpm run '/(typecheck:.*)/'", "typecheck:src": "tsc --project ./tsconfig.json", "typecheck:tests": "tsc --project ./tsconfig.tests.json" }, - "ava": { - "files": [ - "tests/**/*.test.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader", - "ts-node/esm" - ] - }, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/user-api/tests/DrupalkitUserApi.test.ts b/packages/user-api/tests/DrupalkitUserApi.test.ts index 8175b85..e35552a 100644 --- a/packages/user-api/tests/DrupalkitUserApi.test.ts +++ b/packages/user-api/tests/DrupalkitUserApi.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { HttpResponse, http } from "msw"; import { setupServer } from "msw/node"; import { Drupalkit, DrupalkitOptions } from "@drupal-kit/core"; @@ -28,20 +28,20 @@ const createDrupalkit = ( const server = setupServer(); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); -test.serial("Register", async (t) => { - t.plan(3); +test("Register", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); @@ -52,11 +52,11 @@ test.serial("Register", async (t) => { server.use( http.post("*/user-api/register", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); const data = await request.json(); - t.deepEqual(data, payload); + expect(data).toEqual(payload); return HttpResponse.json(UserResponse); }), @@ -66,16 +66,16 @@ test.serial("Register", async (t) => { const res = result.unwrap(); - t.deepEqual(res, UserResponse); + expect(res).toEqual(UserResponse); }); -test.serial("Register with custom request options", async (t) => { - t.plan(2); +test("Register with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); const payload = { @@ -85,7 +85,7 @@ test.serial("Register with custom request options", async (t) => { server.use( http.post("*/user-api/register", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(UserResponse); }), @@ -99,8 +99,8 @@ test.serial("Register with custom request options", async (t) => { }); }); -test.serial("Register with email notification disabled", async (t) => { - t.plan(1); +test("Register with email notification disabled", async () => { + expect.assertions(1); const drupalkit = createDrupalkit(); @@ -111,7 +111,7 @@ test.serial("Register with email notification disabled", async (t) => { server.use( http.post("*/user-api/register", async ({ request }) => { - t.is(request.headers.get("X-Disable-Email-Notification"), "1"); + expect(request.headers.get("X-Disable-Email-Notification")).toBe("1"); return HttpResponse.json(UserResponse); }), @@ -122,8 +122,8 @@ test.serial("Register with email notification disabled", async (t) => { }); }); -test.serial("Register with account activation disabled", async (t) => { - t.plan(1); +test("Register with account activation disabled", async () => { + expect.assertions(1); const drupalkit = createDrupalkit(); @@ -134,7 +134,7 @@ test.serial("Register with account activation disabled", async (t) => { server.use( http.post("*/user-api/register", async ({ request }) => { - t.is(request.headers.get("X-Disable-Account-Activation"), "1"); + expect(request.headers.get("X-Disable-Account-Activation")).toBe("1"); return HttpResponse.json(UserResponse); }), @@ -146,8 +146,8 @@ test.serial("Register with account activation disabled", async (t) => { }); }); -test.serial("Register with all options", async (t) => { - t.plan(2); +test("Register with all options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); @@ -158,8 +158,8 @@ test.serial("Register with all options", async (t) => { server.use( http.post("*/user-api/register", async ({ request }) => { - t.is(request.headers.get("X-Disable-Email-Notification"), "1"); - t.is(request.headers.get("X-Disable-Account-Activation"), "1"); + expect(request.headers.get("X-Disable-Email-Notification")).toBe("1"); + expect(request.headers.get("X-Disable-Account-Activation")).toBe("1"); return HttpResponse.json(UserResponse); }), @@ -171,7 +171,7 @@ test.serial("Register with all options", async (t) => { }); }); -test.serial("Register with custom endpoint", async (t) => { +test("Register with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiRegistrationEndpoint: "/custom/register", @@ -190,10 +190,10 @@ test.serial("Register with custom endpoint", async (t) => { const result = await drupalkit.userApi.register(payload); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle register error", async (t) => { +test("Handle register error", async () => { const drupalkit = createDrupalkit(); const payload = { @@ -209,15 +209,15 @@ test.serial("Handle register error", async (t) => { const result = await drupalkit.userApi.register(payload); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Resend register email. */ -test.serial("Resend register email", async (t) => { - t.plan(3); +test("Resend register email", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; @@ -225,9 +225,9 @@ test.serial("Resend register email", async (t) => { server.use( http.post("*/user-api/register/resend-email", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { email, operation }); + expect(await request.json()).toEqual({ email, operation }); return HttpResponse.json(successResponse); }), @@ -237,23 +237,23 @@ test.serial("Resend register email", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Resend register email with custom request options", async (t) => { - t.plan(2); +test("Resend register email with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; const operation = "register"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/register/resend-email", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -267,7 +267,7 @@ test.serial("Resend register email with custom request options", async (t) => { }); }); -test.serial("Resend register email with custom endpoint", async (t) => { +test("Resend register email with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiRegisterResendEmailEndpoint: "/custom/register/resend-email", @@ -283,10 +283,10 @@ test.serial("Resend register email with custom endpoint", async (t) => { const result = await drupalkit.userApi.resendRegisterEmail(email, operation); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Resend register email - deprecated version", async (t) => { +test("Resend register email - deprecated version", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiResendMailEndpoint: "/custom/register/resend-email", @@ -305,10 +305,10 @@ test.serial("Resend register email - deprecated version", async (t) => { operation, ); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while resend register email", async (t) => { +test("Handle error while resend register email", async () => { const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; const operation = "register"; @@ -321,21 +321,21 @@ test.serial("Handle error while resend register email", async (t) => { const result = await drupalkit.userApi.resendRegisterEmail(email, operation); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * initCancelAccount(). */ -test.serial("Init cancel account", async (t) => { - t.plan(2); +test("Init cancel account", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); server.use( http.post("*/user-api/cancel-account/init", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); return HttpResponse.json(successResponse); }), @@ -345,21 +345,21 @@ test.serial("Init cancel account", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Init cancel account with custom request options", async (t) => { - t.plan(2); +test("Init cancel account with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/cancel-account/init", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -373,7 +373,7 @@ test.serial("Init cancel account with custom request options", async (t) => { }); }); -test.serial("Init cancel account with custom endpoint", async (t) => { +test("Init cancel account with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiInitCancelAccountEndpoint: "/custom/cancel-account/init", @@ -387,10 +387,10 @@ test.serial("Init cancel account with custom endpoint", async (t) => { const result = await drupalkit.userApi.initCancelAccount(); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Init cancel account - deprecated version", async (t) => { +test("Init cancel account - deprecated version", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiInitAccountCancelEndpoint: "/custom/cancel-account/init", @@ -404,10 +404,10 @@ test.serial("Init cancel account - deprecated version", async (t) => { const result = await drupalkit.userApi.initAccountCancel(); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while init cancel account", async (t) => { +test("Handle error while init cancel account", async () => { const drupalkit = createDrupalkit(); server.use( @@ -418,21 +418,21 @@ test.serial("Handle error while init cancel account", async (t) => { const result = await drupalkit.userApi.initCancelAccount(); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * cancelAccount(). */ -test.serial("Cancel account", async (t) => { - t.plan(2); +test("Cancel account", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); server.use( http.post("*/user-api/cancel-account", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); return HttpResponse.json(successResponse); }), @@ -442,21 +442,21 @@ test.serial("Cancel account", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Cancel account with custom request options", async (t) => { - t.plan(2); +test("Cancel account with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/cancel-account", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); return HttpResponse.json(successResponse); }), @@ -470,7 +470,7 @@ test.serial("Cancel account with custom request options", async (t) => { }); }); -test.serial("Cancel account with custom endpoint", async (t) => { +test("Cancel account with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiCancelAccountEndpoint: "/custom/cancel-account", @@ -484,10 +484,10 @@ test.serial("Cancel account with custom endpoint", async (t) => { const result = await drupalkit.userApi.cancelAccount(); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while cancel account", async (t) => { +test("Handle error while cancel account", async () => { const drupalkit = createDrupalkit(); server.use( @@ -498,24 +498,24 @@ test.serial("Handle error while cancel account", async (t) => { const result = await drupalkit.userApi.cancelAccount(); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Init set password */ -test.serial("Init set password", async (t) => { - t.plan(3); +test("Init set password", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; server.use( http.post("*/user-api/set-password/init", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { email }); + expect(await request.json()).toEqual({ email }); return HttpResponse.json(successResponse); }), @@ -525,22 +525,22 @@ test.serial("Init set password", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Init set password with custom request options", async (t) => { - t.plan(2); +test("Init set password with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/set-password/init", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -554,7 +554,7 @@ test.serial("Init set password with custom request options", async (t) => { }); }); -test.serial("Init set password with custom endpoint", async (t) => { +test("Init set password with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiInitSetPasswordEndpoint: "/custom/set-password/init", @@ -569,10 +569,10 @@ test.serial("Init set password with custom endpoint", async (t) => { const result = await drupalkit.userApi.initSetPassword(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Init set password - deprecated version", async (t) => { +test("Init set password - deprecated version", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiResetPasswordEndpoint: "/custom/set-password/init", @@ -587,10 +587,10 @@ test.serial("Init set password - deprecated version", async (t) => { const result = await drupalkit.userApi.resetPassword(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while init set password", async (t) => { +test("Handle error while init set password", async () => { const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; @@ -602,24 +602,24 @@ test.serial("Handle error while init set password", async (t) => { const result = await drupalkit.userApi.initSetPassword(email); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Set password */ -test.serial("Set password", async (t) => { - t.plan(3); +test("Set password", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const newPassword = "new-password"; server.use( http.post("*/user-api/set-password", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { newPassword }); + expect(await request.json()).toEqual({ newPassword }); return HttpResponse.json(successResponse); }), @@ -629,22 +629,22 @@ test.serial("Set password", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Set password with custom request options", async (t) => { - t.plan(2); +test("Set password with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const newPassword = "new-password"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/set-password", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -658,7 +658,7 @@ test.serial("Set password with custom request options", async (t) => { }); }); -test.serial("Set password with custom endpoint", async (t) => { +test("Set password with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiSetPasswordEndpoint: "/custom/set-password", @@ -673,10 +673,10 @@ test.serial("Set password with custom endpoint", async (t) => { const result = await drupalkit.userApi.setPassword(newPassword); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Set password - deprecated version", async (t) => { +test("Set password - deprecated version", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiUpdatePasswordEndpoint: "/custom/set-password", @@ -691,10 +691,10 @@ test.serial("Set password - deprecated version", async (t) => { const result = await drupalkit.userApi.updatePassword(newPassword); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while set password", async (t) => { +test("Handle error while set password", async () => { const drupalkit = createDrupalkit(); const newPassword = "new-password"; @@ -706,24 +706,24 @@ test.serial("Handle error while set password", async (t) => { const result = await drupalkit.userApi.setPassword(newPassword); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Init unset password */ -test.serial("Init unset password", async (t) => { - t.plan(3); +test("Init unset password", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; server.use( http.post("*/user-api/unset-password/init", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { email }); + expect(await request.json()).toEqual({ email }); return HttpResponse.json(successResponse); }), @@ -733,22 +733,22 @@ test.serial("Init unset password", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Init unset password with custom request options", async (t) => { - t.plan(2); +test("Init unset password with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/unset-password/init", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -762,7 +762,7 @@ test.serial("Init unset password with custom request options", async (t) => { }); }); -test.serial("Init unset password with custom endpoint", async (t) => { +test("Init unset password with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiInitUnsetPasswordEndpoint: "/custom/unset-password/init", @@ -777,10 +777,10 @@ test.serial("Init unset password with custom endpoint", async (t) => { const result = await drupalkit.userApi.initUnsetPassword(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while init unset password", async (t) => { +test("Handle error while init unset password", async () => { const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; @@ -792,23 +792,23 @@ test.serial("Handle error while init unset password", async (t) => { const result = await drupalkit.userApi.initUnsetPassword(email); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Unset password */ -test.serial("Unset password with verification", async (t) => { - t.plan(3); +test("Unset password with verification", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); server.use( http.post("*/user-api/unset-password", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), {}); + expect(await request.json()).toEqual({}); return HttpResponse.json(successResponse); }), @@ -818,20 +818,20 @@ test.serial("Unset password with verification", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Unset password with currentPassword", async (t) => { - t.plan(3); +test("Unset password with currentPassword", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const currentPassword = "abc123"; server.use( http.post("*/user-api/unset-password", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { currentPassword }); + expect(await request.json()).toEqual({ currentPassword }); return HttpResponse.json(successResponse); }), @@ -841,21 +841,21 @@ test.serial("Unset password with currentPassword", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Unset password with custom request options", async (t) => { - t.plan(2); +test("Unset password with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/unset-password", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -869,7 +869,7 @@ test.serial("Unset password with custom request options", async (t) => { }); }); -test.serial("Unset password with custom endpoint", async (t) => { +test("Unset password with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiUnsetPasswordEndpoint: "/custom/unset-password", @@ -883,10 +883,10 @@ test.serial("Unset password with custom endpoint", async (t) => { const result = await drupalkit.userApi.unsetPassword(); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while unset password", async (t) => { +test("Handle error while unset password", async () => { const drupalkit = createDrupalkit(); const newPassword = "new-password"; @@ -898,24 +898,24 @@ test.serial("Handle error while unset password", async (t) => { const result = await drupalkit.userApi.unsetPassword(newPassword); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Passwordless login */ -test.serial("Passwordless login", async (t) => { - t.plan(3); +test("Passwordless login", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; server.use( http.post("*/user-api/passwordless-login", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { email }); + expect(await request.json()).toEqual({ email }); return HttpResponse.json(successResponse); }), @@ -925,22 +925,22 @@ test.serial("Passwordless login", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Passwordless login with custom request options", async (t) => { - t.plan(2); +test("Passwordless login with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/passwordless-login", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -954,7 +954,7 @@ test.serial("Passwordless login with custom request options", async (t) => { }); }); -test.serial("Passwordless login with custom endpoint", async (t) => { +test("Passwordless login with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiPasswordlessLoginEndpoint: "/custom/passwordless-login", @@ -969,10 +969,10 @@ test.serial("Passwordless login with custom endpoint", async (t) => { const result = await drupalkit.userApi.passwordlessLogin(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while passwordless login", async (t) => { +test("Handle error while passwordless login", async () => { const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; @@ -984,24 +984,24 @@ test.serial("Handle error while passwordless login", async (t) => { const result = await drupalkit.userApi.passwordlessLogin(email); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Init set email */ -test.serial("Init set email", async (t) => { - t.plan(3); +test("Init set email", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; server.use( http.post("*/user-api/set-email/init", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { email }); + expect(await request.json()).toEqual({ email }); return HttpResponse.json(successResponse); }), @@ -1011,22 +1011,22 @@ test.serial("Init set email", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Init set email with custom request options", async (t) => { - t.plan(2); +test("Init set email with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/set-email/init", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -1040,7 +1040,7 @@ test.serial("Init set email with custom request options", async (t) => { }); }); -test.serial("Init set email with custom endpoint", async (t) => { +test("Init set email with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiInitSetEmailEndpoint: "/custom/set-email/init", @@ -1055,10 +1055,10 @@ test.serial("Init set email with custom endpoint", async (t) => { const result = await drupalkit.userApi.initSetEmail(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Init set email - deprecated version", async (t) => { +test("Init set email - deprecated version", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiVerifyEmailEndpoint: "/custom/set-email/init", @@ -1073,10 +1073,10 @@ test.serial("Init set email - deprecated version", async (t) => { const result = await drupalkit.userApi.verifyEmail(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while init set email", async (t) => { +test("Handle error while init set email", async () => { const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; @@ -1088,24 +1088,24 @@ test.serial("Handle error while init set email", async (t) => { const result = await drupalkit.userApi.initSetEmail(email); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); /** * Set email */ -test.serial("Set email", async (t) => { - t.plan(3); +test("Set email", async () => { + expect.assertions(3); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; server.use( http.post("*/user-api/set-email", async ({ request }) => { - t.is(request.headers.get("content-type"), "application/json"); + expect(request.headers.get("content-type")).toBe("application/json"); - t.deepEqual(await request.json(), { email }); + expect(await request.json()).toEqual({ email }); return HttpResponse.json(successResponse); }), @@ -1115,22 +1115,22 @@ test.serial("Set email", async (t) => { const res = result.unwrap(); - t.deepEqual(res, successResponse); + expect(res).toEqual(successResponse); }); -test.serial("Set email with custom request options", async (t) => { - t.plan(2); +test("Set email with custom request options", async () => { + expect.assertions(2); const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; drupalkit.hook.before("request", (options) => { - t.is(options.cache, "no-cache"); + expect(options.cache).toBe("no-cache"); }); server.use( http.post("*/user-api/set-email", async ({ request }) => { - t.is(request.headers.get("X-Custom"), "1"); + expect(request.headers.get("X-Custom")).toBe("1"); return HttpResponse.json(successResponse); }), @@ -1144,7 +1144,7 @@ test.serial("Set email with custom request options", async (t) => { }); }); -test.serial("Set email with custom endpoint", async (t) => { +test("Set email with custom endpoint", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiSetEmailEndpoint: "/custom/set-email", @@ -1159,10 +1159,10 @@ test.serial("Set email with custom endpoint", async (t) => { const result = await drupalkit.userApi.setEmail(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Set email - deprecated version", async (t) => { +test("Set email - deprecated version", async () => { const drupalkit = createDrupalkit({ baseUrl: BASE_URL, userApiUpdateEmailEndpoint: "/custom/set-email", @@ -1177,10 +1177,10 @@ test.serial("Set email - deprecated version", async (t) => { const result = await drupalkit.userApi.updateEmail(email); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Handle error while set email", async (t) => { +test("Handle error while set email", async () => { const drupalkit = createDrupalkit(); const email = "JzWZg@example.com"; @@ -1192,5 +1192,5 @@ test.serial("Handle error while set email", async (t) => { const result = await drupalkit.userApi.setEmail(email); - t.assert(result.err); + expect(result.err).toBeTruthy(); }); diff --git a/packages/user-api/vitest.config.ts b/packages/user-api/vitest.config.ts new file mode 100644 index 0000000..e587732 --- /dev/null +++ b/packages/user-api/vitest.config.ts @@ -0,0 +1,3 @@ +import { createVitestConfig } from "@drupal-kit/config-vitest/vitest"; + +export default createVitestConfig(); diff --git a/packages/verification/package.json b/packages/verification/package.json index a2d40ed..ecd6ba1 100644 --- a/packages/verification/package.json +++ b/packages/verification/package.json @@ -8,39 +8,29 @@ }, "devDependencies": { "@drupal-kit/config-typescript": "workspace:0.13.4", + "@drupal-kit/config-vitest": "workspace:0.13.4", "@drupal-kit/eslint-config": "workspace:0.13.4", "@drupal-kit/types": "workspace:0.13.4", "@rollup/plugin-typescript": "^11.1.1", "@swc/core": "^1.6.5", - "ava": "^6.1.3", "esbuild": "^0.27.3", "msw": "^2.14.6", "rollup": "^4.59.0", "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1", "ts-node": "^10.9.2", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "^3.2.4" }, "scripts": { "build": "rollup -c", "lint": "eslint --ext .ts,.tsx src --max-warnings 0", - "test": "NODE_NO_WARNINGS=1 ava", + "test": "NODE_NO_WARNINGS=1 vitest run", + "test:watch": "NODE_NO_WARNINGS=1 vitest", "typecheck": "pnpm run '/(typecheck:.*)/'", "typecheck:src": "tsc --project ./tsconfig.json", "typecheck:tests": "tsc --project ./tsconfig.tests.json" }, - "ava": { - "files": [ - "tests/**/*.test.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader", - "ts-node/esm" - ] - }, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/verification/tests/DrupalkitVerification.test.ts b/packages/verification/tests/DrupalkitVerification.test.ts index 55ac656..27313d7 100644 --- a/packages/verification/tests/DrupalkitVerification.test.ts +++ b/packages/verification/tests/DrupalkitVerification.test.ts @@ -1,4 +1,4 @@ -import test from "ava"; +import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; import { HttpResponse, http } from "msw"; import { setupServer } from "msw/node"; import { Drupalkit, DrupalkitOptions } from "@drupal-kit/core"; @@ -27,26 +27,26 @@ const createDrupalkit = ( const server = setupServer(); -test.before(() => { +beforeAll(() => { server.listen(); }); -test.afterEach(() => { +afterEach(() => { server.resetHandlers(); }); -test.after(() => { +afterAll(() => { server.close(); }); -test("Instanciate with plugin", (t) => { +test("Instanciate with plugin", () => { const drupalkit = createDrupalkit(); - t.assert(drupalkit.hasOwnProperty("verification")); + expect(drupalkit.hasOwnProperty("verification")).toBeTruthy(); }); -test.serial("Add Hash verification to a request once", async (t) => { - t.plan(4); +test("Add Hash verification to a request once", async () => { + expect.assertions(4); let first = true; const drupalkit = createDrupalkit(); @@ -55,10 +55,10 @@ test.serial("Add Hash verification to a request once", async (t) => { server.use( http.get("*/", async ({ request }) => { if (first) { - t.is(request.headers.get("x-verification-hash"), hash); + expect(request.headers.get("x-verification-hash")).toBe(hash); first = false; } else { - t.not(request.headers.get("x-verification-hash"), hash); + expect(request.headers.get("x-verification-hash")).not.toBe(hash); } return HttpResponse.text(); @@ -71,18 +71,18 @@ test.serial("Add Hash verification to a request once", async (t) => { method: "GET", }); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); // Second request. result = await drupalkit.request("/", { method: "GET", }); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); -test.serial("Add Magic code verification to a request once", async (t) => { - t.plan(4); +test("Add Magic code verification to a request once", async () => { + expect.assertions(4); let first = true; const drupalkit = createDrupalkit(); @@ -91,10 +91,10 @@ test.serial("Add Magic code verification to a request once", async (t) => { server.use( http.get("*/", async ({ request }) => { if (first) { - t.is(request.headers.get("x-verification-magic-code"), code); + expect(request.headers.get("x-verification-magic-code")).toBe(code); first = false; } else { - t.not(request.headers.get("x-verification-magic-code"), code); + expect(request.headers.get("x-verification-magic-code")).not.toBe(code); } return HttpResponse.text(); @@ -108,12 +108,12 @@ test.serial("Add Magic code verification to a request once", async (t) => { method: "GET", }); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); // Second request. result = await drupalkit.request("/", { method: "GET", }); - t.assert(result.ok); + expect(result.ok).toBeTruthy(); }); diff --git a/packages/verification/vitest.config.ts b/packages/verification/vitest.config.ts new file mode 100644 index 0000000..e587732 --- /dev/null +++ b/packages/verification/vitest.config.ts @@ -0,0 +1,3 @@ +import { createVitestConfig } from "@drupal-kit/config-vitest/vitest"; + +export default createVitestConfig(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ce2387..ba8c3ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,6 +106,18 @@ importers: specifier: 5.4.5 version: 5.4.5 + packages/config/vitest: + devDependencies: + '@drupal-kit/config-typescript': + specifier: workspace:0.13.4 + version: link:../typescript + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) + packages/consumers: dependencies: '@drupal-kit/core': @@ -115,6 +127,9 @@ importers: '@drupal-kit/config-typescript': specifier: workspace:0.13.4 version: link:../config/typescript + '@drupal-kit/config-vitest': + specifier: workspace:0.13.4 + version: link:../config/vitest '@drupal-kit/eslint-config': specifier: workspace:0.13.4 version: link:../config/eslint @@ -127,9 +142,6 @@ importers: '@swc/core': specifier: ^1.6.5 version: 1.6.5 - ava: - specifier: ^6.1.3 - version: 6.1.3 esbuild: specifier: ^0.27.3 version: 0.27.3 @@ -151,6 +163,9 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) packages/core: dependencies: @@ -170,6 +185,9 @@ importers: '@drupal-kit/config-typescript': specifier: workspace:0.13.4 version: link:../config/typescript + '@drupal-kit/config-vitest': + specifier: workspace:0.13.4 + version: link:../config/vitest '@drupal-kit/eslint-config': specifier: workspace:0.13.4 version: link:../config/eslint @@ -188,9 +206,6 @@ importers: '@wunderwerk/node-pkg-version-export': specifier: 1.0.0-beta.1 version: 1.0.0-beta.1 - ava: - specifier: ^6.1.3 - version: 6.1.3 esbuild: specifier: ^0.27.3 version: 0.27.3 @@ -212,6 +227,9 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) packages/jsonapi: dependencies: @@ -234,6 +252,9 @@ importers: '@drupal-kit/config-typescript': specifier: workspace:0.13.4 version: link:../config/typescript + '@drupal-kit/config-vitest': + specifier: workspace:0.13.4 + version: link:../config/vitest '@drupal-kit/eslint-config': specifier: workspace:0.13.4 version: link:../config/eslint @@ -246,9 +267,6 @@ importers: '@swc/core': specifier: ^1.6.5 version: 1.6.5 - ava: - specifier: ^6.1.3 - version: 6.1.3 esbuild: specifier: ^0.27.3 version: 0.27.3 @@ -270,6 +288,9 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) packages/simple-oauth: dependencies: @@ -283,6 +304,9 @@ importers: '@drupal-kit/config-typescript': specifier: workspace:0.13.4 version: link:../config/typescript + '@drupal-kit/config-vitest': + specifier: workspace:0.13.4 + version: link:../config/vitest '@drupal-kit/eslint-config': specifier: workspace:0.13.4 version: link:../config/eslint @@ -295,9 +319,6 @@ importers: '@swc/core': specifier: ^1.6.5 version: 1.6.5 - ava: - specifier: ^6.1.3 - version: 6.1.3 esbuild: specifier: ^0.27.3 version: 0.27.3 @@ -319,6 +340,9 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) packages/simple-oauth-auth-code: dependencies: @@ -332,6 +356,9 @@ importers: '@drupal-kit/config-typescript': specifier: workspace:0.13.4 version: link:../config/typescript + '@drupal-kit/config-vitest': + specifier: workspace:0.13.4 + version: link:../config/vitest '@drupal-kit/eslint-config': specifier: workspace:0.13.4 version: link:../config/eslint @@ -344,9 +371,6 @@ importers: '@swc/core': specifier: ^1.6.5 version: 1.6.5 - ava: - specifier: ^6.1.3 - version: 6.1.3 esbuild: specifier: ^0.27.3 version: 0.27.3 @@ -368,6 +392,9 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) packages/types: devDependencies: @@ -396,6 +423,9 @@ importers: '@drupal-kit/config-typescript': specifier: workspace:0.13.4 version: link:../config/typescript + '@drupal-kit/config-vitest': + specifier: workspace:0.13.4 + version: link:../config/vitest '@drupal-kit/eslint-config': specifier: workspace:0.13.4 version: link:../config/eslint @@ -411,9 +441,6 @@ importers: '@types/util-deprecate': specifier: ^1.0.3 version: 1.0.3 - ava: - specifier: ^6.1.3 - version: 6.1.3 esbuild: specifier: ^0.27.3 version: 0.27.3 @@ -435,6 +462,9 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) packages/verification: dependencies: @@ -445,6 +475,9 @@ importers: '@drupal-kit/config-typescript': specifier: workspace:0.13.4 version: link:../config/typescript + '@drupal-kit/config-vitest': + specifier: workspace:0.13.4 + version: link:../config/vitest '@drupal-kit/eslint-config': specifier: workspace:0.13.4 version: link:../config/eslint @@ -457,9 +490,6 @@ importers: '@swc/core': specifier: ^1.6.5 version: 1.6.5 - ava: - specifier: ^6.1.3 - version: 6.1.3 esbuild: specifier: ^0.27.3 version: 0.27.3 @@ -481,6 +511,9 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@20.14.8)(msw@2.14.6) packages: @@ -1508,13 +1541,6 @@ packages: '@types/node': 20.14.8 dev: true - /@isaacs/fs-minipass@4.0.1: - resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} - engines: {node: '>=18.0.0'} - dependencies: - minipass: 7.1.3 - dev: true - /@jridgewell/gen-mapping@0.3.5: resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -1543,6 +1569,10 @@ packages: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true + /@jridgewell/sourcemap-codec@1.5.5: + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + dev: true + /@jridgewell/trace-mapping@0.3.25: resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} dependencies: @@ -1554,7 +1584,7 @@ packages: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.5 dev: true /@manypkg/find-root@1.1.0: @@ -1604,24 +1634,6 @@ packages: read-yaml-file: 1.1.0 dev: true - /@mapbox/node-pre-gyp@1.0.11: - resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} - hasBin: true - dependencies: - detect-libc: 2.0.3 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.7.0 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.6.2 - tar: 7.5.15 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@mswjs/interceptors@0.41.9: resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} engines: {node: '>=18'} @@ -1693,14 +1705,6 @@ packages: typescript: 5.4.5 dev: true - /@rollup/pluginutils@4.2.1: - resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} - engines: {node: '>= 8.0.0'} - dependencies: - estree-walker: 2.0.2 - picomatch: 2.3.2 - dev: true - /@rollup/pluginutils@5.1.0(rollup@4.59.0): resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} @@ -1916,11 +1920,6 @@ packages: dev: true optional: true - /@sindresorhus/merge-streams@2.3.0: - resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} - engines: {node: '>=18'} - dev: true - /@swc/core-darwin-arm64@1.6.5: resolution: {integrity: sha512-RGQhMdni2v1/ANQ/2K+F+QYdzaucekYBewZcX1ogqJ8G5sbPaBdYdDN1qQ4kHLCIkPtGP6qC7c71qPEqL2RidQ==} engines: {node: '>=10'} @@ -2110,12 +2109,23 @@ packages: dev: true optional: true + /@types/chai@5.2.3: + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + dev: true + /@types/conventional-commits-parser@5.0.0: resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} dependencies: '@types/node': 20.14.8 dev: true + /@types/deep-eql@4.0.2: + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + dev: true + /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true @@ -2288,26 +2298,68 @@ packages: deprecated: Potential CWE-502 - Update to 1.3.1 or higher dev: true - /@vercel/nft@0.26.5: - resolution: {integrity: sha512-NHxohEqad6Ra/r4lGknO52uc/GrWILXAMs1BB4401GTqww0fw1bAqzpG1XHuDO+dprg4GvsD9ZLLSsdo78p9hQ==} - engines: {node: '>=16'} - hasBin: true + /@vitest/expect@3.2.4: + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} dependencies: - '@mapbox/node-pre-gyp': 1.0.11 - '@rollup/pluginutils': 4.2.1 - acorn: 8.16.0 - acorn-import-attributes: 1.9.5(acorn@8.16.0) - async-sema: 3.1.1 - bindings: 1.5.0 - estree-walker: 2.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - node-gyp-build: 4.8.1 - resolve-from: 5.0.0 - transitivePeerDependencies: - - encoding - - supports-color + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + dev: true + + /@vitest/mocker@3.2.4(msw@2.14.6)(vite@7.3.3): + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + msw: 2.14.6(@types/node@20.14.8)(typescript@5.4.5) + vite: 7.3.3(@types/node@20.14.8) + dev: true + + /@vitest/pretty-format@3.2.4: + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + dependencies: + tinyrainbow: 2.0.0 + dev: true + + /@vitest/runner@3.2.4: + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + dev: true + + /@vitest/snapshot@3.2.4: + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + dev: true + + /@vitest/spy@3.2.4: + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + dependencies: + tinyspy: 4.0.4 + dev: true + + /@vitest/utils@3.2.4: + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 dev: true /@wunderwerk/eslint-config@1.0.7(@typescript-eslint/parser@7.14.1)(eslint-plugin-import@2.29.1)(eslint-plugin-jsdoc@48.0.2)(eslint@8.57.1)(typescript@5.4.5): @@ -2348,18 +2400,6 @@ packages: through: 2.3.8 dev: true - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true - - /acorn-import-attributes@1.9.5(acorn@8.16.0): - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - dependencies: - acorn: 8.16.0 - dev: true - /acorn-jsx@5.3.2(acorn@8.16.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2373,13 +2413,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} - engines: {node: '>=0.4.0'} - dependencies: - acorn: 8.16.0 - dev: true - /acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} @@ -2392,15 +2425,6 @@ packages: hasBin: true dev: true - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - dev: true - /ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} dependencies: @@ -2429,11 +2453,6 @@ packages: engines: {node: '>=8'} dev: true - /ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - dev: true - /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -2448,29 +2467,11 @@ packages: color-convert: 2.0.1 dev: true - /ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - dev: true - - /aproba@2.1.0: - resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} - dev: true - /are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} dev: true - /are-we-there-yet@2.0.0: - resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} - engines: {node: '>=10'} - deprecated: This package is no longer supported. - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - dev: true - /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true @@ -2502,11 +2503,6 @@ packages: dev: true optional: true - /array-find-index@1.0.2: - resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} - engines: {node: '>=0.10.0'} - dev: true - /array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} dev: true @@ -2639,75 +2635,11 @@ packages: dev: true optional: true - /arrgv@1.0.2: - resolution: {integrity: sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==} - engines: {node: '>=8.0.0'} - dev: true - - /arrify@3.0.0: - resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} + /assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} dev: true - /async-sema@3.1.1: - resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} - dev: true - - /ava@6.1.3: - resolution: {integrity: sha512-tkKbpF1pIiC+q09wNU9OfyTDYZa8yuWvU2up3+lFJ3lr1RmnYh2GBpPwzYUEB0wvTPIUysGjcZLNZr7STDviRA==} - engines: {node: ^18.18 || ^20.8 || ^21 || ^22} - hasBin: true - peerDependencies: - '@ava/typescript': '*' - peerDependenciesMeta: - '@ava/typescript': - optional: true - dependencies: - '@vercel/nft': 0.26.5 - acorn: 8.16.0 - acorn-walk: 8.3.5 - ansi-styles: 6.2.3 - arrgv: 1.0.2 - arrify: 3.0.0 - callsites: 4.2.0 - cbor: 9.0.2 - chalk: 5.6.2 - chunkd: 2.0.1 - ci-info: 4.4.0 - ci-parallel-vars: 1.0.1 - cli-truncate: 4.0.0 - code-excerpt: 4.0.0 - common-path-prefix: 3.0.0 - concordance: 5.0.4 - currently-unhandled: 0.4.1 - debug: 4.4.3 - emittery: 1.2.1 - figures: 6.1.0 - globby: 14.1.0 - ignore-by-default: 2.1.0 - indent-string: 5.0.0 - is-plain-object: 5.0.0(patch_hash=lray4voikhoiy5hx7wwtwhzd7e) - is-promise: 4.0.0 - matcher: 5.0.0 - memoize: 10.2.0 - ms: 2.1.3 - p-map: 7.0.4 - package-config: 5.0.0 - picomatch: 3.0.2 - plur: 5.1.0 - pretty-ms: 9.3.0 - resolve-cwd: 3.0.0 - stack-utils: 2.0.6 - strip-ansi: 7.2.0 - supertap: 3.0.1 - temp-dir: 3.0.0 - write-file-atomic: 5.0.1 - yargs: 17.7.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -2742,16 +2674,6 @@ packages: is-windows: 1.0.2 dev: true - /bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - dependencies: - file-uri-to-path: 1.0.0 - dev: true - - /blueimp-md5@2.19.0: - resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} - dev: true - /brace-expansion@1.1.15: resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} dependencies: @@ -2789,6 +2711,11 @@ packages: engines: {node: '>=6'} dev: true + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + /call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -2829,20 +2756,19 @@ packages: engines: {node: '>=6'} dev: true - /callsites@4.2.0: - resolution: {integrity: sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==} - engines: {node: '>=12.20'} - dev: true - /caniuse-lite@1.0.30001636: resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} dev: true - /cbor@9.0.2: - resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==} - engines: {node: '>=16'} + /chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} dependencies: - nofilter: 3.1.0 + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 dev: true /chalk@2.4.2: @@ -2871,30 +2797,9 @@ packages: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} dev: true - /chownr@3.0.0: - resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} - engines: {node: '>=18'} - dev: true - - /chunkd@2.0.1: - resolution: {integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==} - dev: true - - /ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} - engines: {node: '>=8'} - dev: true - - /ci-parallel-vars@1.0.1: - resolution: {integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==} - dev: true - - /cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} - dependencies: - slice-ansi: 5.0.0 - string-width: 7.2.0 + /check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} dev: true /cli-width@4.1.0: @@ -2911,13 +2816,6 @@ packages: wrap-ansi: 7.0.0 dev: true - /code-excerpt@4.0.0: - resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - convert-to-spaces: 2.0.1 - dev: true - /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -2939,20 +2837,11 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - dev: true - /comment-parser@1.4.1: resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} engines: {node: '>= 12.0.0'} dev: true - /common-path-prefix@3.0.0: - resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} - dev: true - /compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} dependencies: @@ -2964,24 +2853,6 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /concordance@5.0.4: - resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} - engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'} - dependencies: - date-time: 3.1.0 - esutils: 2.0.3 - fast-diff: 1.3.0 - js-string-escape: 1.0.1 - lodash: 4.18.1 - md5-hex: 3.0.1 - semver: 7.6.2 - well-known-symbols: 2.0.0 - dev: true - - /console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: true - /conventional-changelog-angular@7.0.0: resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} engines: {node: '>=16'} @@ -3011,11 +2882,6 @@ packages: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true - /convert-to-spaces@2.0.1: - resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - /cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} @@ -3064,13 +2930,6 @@ packages: which: 2.0.2 dev: true - /currently-unhandled@0.4.1: - resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} - engines: {node: '>=0.10.0'} - dependencies: - array-find-index: 1.0.2 - dev: true - /dargs@8.1.0: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} @@ -3113,13 +2972,6 @@ packages: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} dev: true - /date-time@3.1.0: - resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==} - engines: {node: '>=6'} - dependencies: - time-zone: 1.0.0 - dev: true - /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -3167,6 +3019,11 @@ packages: ms: 2.1.3 dev: true + /deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + dev: true + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -3200,20 +3057,11 @@ packages: object-keys: 1.1.1 dev: true - /delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: true - /detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} dev: true - /detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} - engines: {node: '>=8'} - dev: true - /diff@4.0.4: resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} @@ -3270,15 +3118,6 @@ packages: resolution: {integrity: sha512-CDyzcJ5XW78SHzsIOdn27z8J4ist8eaFLhdto2hSMSJQgsiwvbv2fbizcKUICryw1Wii1TI/FEkvzvJsR3awrA==} dev: true - /emittery@1.2.1: - resolution: {integrity: sha512-sFz64DCRjirhwHLxofFqxYQm6DCp6o0Ix7jwKQvuCHPn4GMRZNuBZyLPu9Ccmk/QSCAMZt6FOUqA8JZCQvA9fw==} - engines: {node: '>=14.16'} - dev: true - - /emoji-regex@10.3.0: - resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} - dev: true - /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -3436,6 +3275,10 @@ packages: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} dev: true + /es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + dev: true + /es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -3521,21 +3364,11 @@ packages: engines: {node: '>=0.8.0'} dev: true - /escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - dev: true - /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: true - /escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - dev: true - /eslint-config-prettier@8.10.0(eslint@8.57.1): resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true @@ -3778,11 +3611,22 @@ packages: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} dev: true + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.8 + dev: true + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} dev: true + /expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + dev: true + /extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} dev: true @@ -3791,10 +3635,6 @@ packages: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true - /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -3806,17 +3646,6 @@ packages: micromatch: 4.0.8 dev: true - /fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - dev: true - /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: true @@ -3851,11 +3680,16 @@ packages: reusify: 1.0.4 dev: true - /figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} + /fdir@6.5.0(picomatch@4.0.4): + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true dependencies: - is-unicode-supported: 2.0.0 + picomatch: 4.0.4 dev: true /file-entry-cache@6.0.1: @@ -3865,10 +3699,6 @@ packages: flat-cache: 3.2.0 dev: true - /file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - dev: true - /fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -3876,11 +3706,6 @@ packages: to-regex-range: 5.0.1 dev: true - /find-up-simple@1.0.0: - resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} - engines: {node: '>=18'} - dev: true - /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -3972,22 +3797,6 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /gauge@3.0.2: - resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} - engines: {node: '>=10'} - deprecated: This package is no longer supported. - dependencies: - aproba: 2.1.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: true - /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3998,11 +3807,6 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: true - /get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} - engines: {node: '>=18'} - dev: true - /get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} dependencies: @@ -4144,18 +3948,6 @@ packages: slash: 3.0.0 dev: true - /globby@14.1.0: - resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} - engines: {node: '>=18'} - dependencies: - '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.3 - ignore: 7.0.5 - path-type: 6.0.0 - slash: 5.1.0 - unicorn-magic: 0.3.0 - dev: true - /gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -4233,10 +4025,6 @@ packages: dev: true optional: true - /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: true - /hasown@2.0.0: resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} engines: {node: '>= 0.4'} @@ -4258,16 +4046,6 @@ packages: set-cookie-parser: 3.1.0 dev: true - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - dev: true - /human-id@4.1.3: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true @@ -4280,21 +4058,11 @@ packages: safer-buffer: 2.1.2 dev: true - /ignore-by-default@2.1.0: - resolution: {integrity: sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==} - engines: {node: '>=10 <11 || >=12 <13 || >=14'} - dev: true - /ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} dev: true - /ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - dev: true - /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -4312,11 +4080,6 @@ packages: engines: {node: '>=0.8.19'} dev: true - /indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} - dev: true - /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -4354,11 +4117,6 @@ packages: dev: true optional: true - /irregular-plurals@3.5.0: - resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==} - engines: {node: '>=8'} - dev: true - /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -4456,11 +4214,6 @@ packages: engines: {node: '>=8'} dev: true - /is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - dev: true - /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} @@ -4525,12 +4278,9 @@ packages: /is-plain-object@5.0.0(patch_hash=lray4voikhoiy5hx7wwtwhzd7e): resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + dev: false patched: true - /is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - dev: true - /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -4605,11 +4355,6 @@ packages: dev: true optional: true - /is-unicode-supported@2.0.0: - resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} - engines: {node: '>=18'} - dev: true - /is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -4667,15 +4412,14 @@ packages: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} dev: true - /js-string-escape@1.0.1: - resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} - engines: {node: '>= 0.8'} - dev: true - /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true + /js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + dev: true + /js-yaml@3.14.2: resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} hasBin: true @@ -4782,11 +4526,6 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true - /load-json-file@7.0.1: - resolution: {integrity: sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -4844,10 +4583,6 @@ packages: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} dev: true - /lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - dev: true - /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -4857,6 +4592,10 @@ packages: dev: true optional: true + /loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + dev: true + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -4876,42 +4615,20 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} + /magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} dependencies: - semver: 6.3.1 + '@jridgewell/sourcemap-codec': 1.5.5 dev: true /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /matcher@5.0.0: - resolution: {integrity: sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - escape-string-regexp: 5.0.0 - dev: true - /math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - /md5-hex@3.0.1: - resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} - engines: {node: '>=8'} - dependencies: - blueimp-md5: 2.19.0 - dev: true - - /memoize@10.2.0: - resolution: {integrity: sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==} - engines: {node: '>=18'} - dependencies: - mimic-function: 5.0.1 - dev: true - /meow@12.1.1: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} @@ -4930,11 +4647,6 @@ packages: picomatch: 2.3.2 dev: true - /mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - dev: true - /minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} dependencies: @@ -4952,18 +4664,6 @@ packages: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true - /minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true - - /minizlib@3.1.0: - resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} - engines: {node: '>= 18'} - dependencies: - minipass: 7.1.3 - dev: true - /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -5016,6 +4716,12 @@ packages: engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} dev: true + /nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -5032,43 +4738,16 @@ packages: whatwg-url: 5.0.0 dev: true - /node-gyp-build@4.8.1: - resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==} - hasBin: true - dev: true - /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} dev: true - /nofilter@3.1.0: - resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} - engines: {node: '>=12.19'} - dev: true - - /nopt@5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} - hasBin: true - dependencies: - abbrev: 1.1.1 - dev: true - - /npmlog@5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - deprecated: This package is no longer supported. - dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - dev: true - /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} requiresBuild: true dev: true + optional: true /object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} @@ -5241,24 +4920,11 @@ packages: engines: {node: '>=6'} dev: true - /p-map@7.0.4: - resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} - engines: {node: '>=18'} - dev: true - /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} dev: true - /package-config@5.0.0: - resolution: {integrity: sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==} - engines: {node: '>=18'} - dependencies: - find-up-simple: 1.0.0 - load-json-file: 7.0.1 - dev: true - /package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} dependencies: @@ -5282,11 +4948,6 @@ packages: lines-and-columns: 1.2.4 dev: true - /parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - dev: true - /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -5320,9 +4981,13 @@ packages: engines: {node: '>=8'} dev: true - /path-type@6.0.0: - resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} - engines: {node: '>=18'} + /pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + dev: true + + /pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} dev: true /picocolors@1.1.1: @@ -5334,9 +4999,9 @@ packages: engines: {node: '>=8.6'} dev: true - /picomatch@3.0.2: - resolution: {integrity: sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==} - engines: {node: '>=10'} + /picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} dev: true /pify@4.0.1: @@ -5344,13 +5009,6 @@ packages: engines: {node: '>=6'} dev: true - /plur@5.1.0: - resolution: {integrity: sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - irregular-plurals: 3.5.0 - dev: true - /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -5358,6 +5016,15 @@ packages: dev: true optional: true + /postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + dev: true + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -5375,13 +5042,6 @@ packages: hasBin: true dev: true - /pretty-ms@9.3.0: - resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} - engines: {node: '>=18'} - dependencies: - parse-ms: 4.0.0 - dev: true - /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} requiresBuild: true @@ -5432,15 +5092,6 @@ packages: strip-bom: 3.0.0 dev: true - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - dev: true - /reflect.getprototypeof@1.0.6: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} @@ -5487,13 +5138,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - dev: true - /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -5639,10 +5283,6 @@ packages: dev: true optional: true - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true - /safe-regex-test@1.0.2: resolution: {integrity: sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==} engines: {node: '>= 0.4'} @@ -5686,17 +5326,6 @@ packages: hasBin: true dev: true - /serialize-error@7.0.1: - resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} - engines: {node: '>=10'} - dependencies: - type-fest: 0.13.1 - dev: true - - /set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: true - /set-cookie-parser@3.1.0: resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} dev: true @@ -5795,8 +5424,8 @@ packages: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} dev: true /signal-exit@4.1.0: @@ -5809,17 +5438,9 @@ packages: engines: {node: '>=8'} dev: true - /slash@5.1.0: - resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} - engines: {node: '>=14.16'} - dev: true - - /slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 4.0.0 + /source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} dev: true /spawndamnit@3.0.1: @@ -5853,11 +5474,8 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true - /stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 2.0.0 + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true /statuses@2.0.2: @@ -5865,6 +5483,10 @@ packages: engines: {node: '>= 0.8'} dev: true + /std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + dev: true + /strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} dev: true @@ -5878,15 +5500,6 @@ packages: strip-ansi: 6.0.1 dev: true - /string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - dependencies: - emoji-regex: 10.3.0 - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 - dev: true - /string.prototype.matchall@4.0.11: resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} engines: {node: '>= 0.4'} @@ -5965,12 +5578,6 @@ packages: dev: true optional: true - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - dev: true - /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -5978,13 +5585,6 @@ packages: ansi-regex: 5.0.1 dev: true - /strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - dependencies: - ansi-regex: 6.2.2 - dev: true - /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -5995,14 +5595,10 @@ packages: engines: {node: '>=8'} dev: true - /supertap@3.0.1: - resolution: {integrity: sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + /strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} dependencies: - indent-string: 5.0.0 - js-yaml: 3.14.2 - serialize-error: 7.0.1 - strip-ansi: 7.2.0 + js-tokens: 9.0.1 dev: true /supports-color@5.5.0: @@ -6108,22 +5704,6 @@ packages: engines: {node: '>=20'} dev: true - /tar@7.5.15: - resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} - engines: {node: '>=18'} - dependencies: - '@isaacs/fs-minipass': 4.0.1 - chownr: 3.0.0 - minipass: 7.1.3 - minizlib: 3.1.0 - yallist: 5.0.0 - dev: true - - /temp-dir@3.0.0: - resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} - engines: {node: '>=14.16'} - dev: true - /term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -6142,9 +5722,12 @@ packages: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: true - /time-zone@1.0.0: - resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==} - engines: {node: '>=4'} + /tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + dev: true + + /tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} dev: true /tinyexec@1.2.2: @@ -6152,6 +5735,29 @@ packages: engines: {node: '>=18'} dev: true + /tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + dev: true + + /tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + dev: true + + /tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + dev: true + /tldts-core@7.4.0: resolution: {integrity: sha512-/mb9kRld+x1sIMXxWNOAp5m6C+D4GrAORWlJkOJ5dElvxdN1eutz/o7qHLp9gFvDF4Y3/L2xeScoxz6AbEo8rQ==} dev: true @@ -6265,11 +5871,6 @@ packages: prelude-ls: 1.2.1 dev: true - /type-fest@0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} - dev: true - /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} @@ -6396,11 +5997,6 @@ packages: engines: {node: '>=18'} dev: true - /unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - dev: true - /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -6429,18 +6025,157 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + /vite-node@3.2.4(@types/node@20.14.8): + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.3(@types/node@20.14.8) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml dev: true - /well-known-symbols@2.0.0: - resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==} - engines: {node: '>=6'} + /vite@7.3.3(@types/node@20.14.8): + resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + dependencies: + '@types/node': 20.14.8 + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.59.0 + tinyglobby: 0.2.16 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@3.2.4(@types/node@20.14.8)(msw@2.14.6): + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 5.2.3 + '@types/node': 20.14.8 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(msw@2.14.6)(vite@7.3.3) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.16 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.3(@types/node@20.14.8) + vite-node: 3.2.4(@types/node@20.14.8) + why-is-node-running: 2.3.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true /whatwg-url@5.0.0: @@ -6524,10 +6259,13 @@ packages: isexe: 2.0.0 dev: true - /wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + /why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true dependencies: - string-width: 4.2.3 + siginfo: 2.0.0 + stackback: 0.0.2 dev: true /wrap-ansi@7.0.0: @@ -6543,14 +6281,6 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - dev: true - /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6564,11 +6294,6 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yallist@5.0.0: - resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} - engines: {node: '>=18'} - dev: true - /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'}