From 75aeea5b7e769f71873a8e4cd8a5ca2670e61d38 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Feb 2024 06:36:53 +0000 Subject: [PATCH] Version Packages --- .changeset/five-baboons-smell.md | 16 --- .changeset/five-dancers-pump.md | 11 -- .changeset/five-kiwis-laugh.md | 5 - .changeset/fresh-boats-yawn.md | 5 - .changeset/funny-mugs-brake.md | 9 -- .changeset/great-trains-doubt.md | 5 - .changeset/green-flies-draw.md | 5 - .changeset/green-zebras-greet.md | 19 ---- .changeset/hip-forks-drum.md | 15 --- .changeset/large-singers-argue.md | 5 - .changeset/late-tigers-smash.md | 7 -- .changeset/lucky-gorillas-grab.md | 5 - .changeset/new-crabs-buy.md | 53 ---------- .changeset/new-geese-flash.md | 5 - .changeset/nine-coins-hear.md | 5 - .changeset/orange-moles-yell.md | 5 - .changeset/plenty-rings-wink.md | 5 - .changeset/pretty-needles-hope.md | 7 -- .changeset/quiet-pants-beam.md | 5 - .changeset/rude-bobcats-unite.md | 9 -- .changeset/smart-ghosts-complain.md | 5 - .changeset/twelve-masks-turn.md | 7 -- CHANGELOG.md | 153 ++++++++++++++++++++++++++++ package.json | 4 +- 24 files changed, 155 insertions(+), 215 deletions(-) delete mode 100644 .changeset/five-baboons-smell.md delete mode 100644 .changeset/five-dancers-pump.md delete mode 100644 .changeset/five-kiwis-laugh.md delete mode 100644 .changeset/fresh-boats-yawn.md delete mode 100644 .changeset/funny-mugs-brake.md delete mode 100644 .changeset/great-trains-doubt.md delete mode 100644 .changeset/green-flies-draw.md delete mode 100644 .changeset/green-zebras-greet.md delete mode 100644 .changeset/hip-forks-drum.md delete mode 100644 .changeset/large-singers-argue.md delete mode 100644 .changeset/late-tigers-smash.md delete mode 100644 .changeset/lucky-gorillas-grab.md delete mode 100644 .changeset/new-crabs-buy.md delete mode 100644 .changeset/new-geese-flash.md delete mode 100644 .changeset/nine-coins-hear.md delete mode 100644 .changeset/orange-moles-yell.md delete mode 100644 .changeset/plenty-rings-wink.md delete mode 100644 .changeset/pretty-needles-hope.md delete mode 100644 .changeset/quiet-pants-beam.md delete mode 100644 .changeset/rude-bobcats-unite.md delete mode 100644 .changeset/smart-ghosts-complain.md delete mode 100644 .changeset/twelve-masks-turn.md diff --git a/.changeset/five-baboons-smell.md b/.changeset/five-baboons-smell.md deleted file mode 100644 index 1911e201b..000000000 --- a/.changeset/five-baboons-smell.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'skuba': patch ---- - -lint: Disable `Promise` return checks in tests - -This works around an [existing incompatibility](https://github.com/koajs/koa/issues/1755) between Koa and the built-in `http.RequestListener` type: - -```typescript -const app = new Koa(); - -const agent = supertest.agent(app.callback()); -// ~~~~~~~~~~~~~~ -// Promise returned in function argument where a void return was expected. -// @typescript-eslint/no-misused-promises -``` diff --git a/.changeset/five-dancers-pump.md b/.changeset/five-dancers-pump.md deleted file mode 100644 index d176e210e..000000000 --- a/.changeset/five-dancers-pump.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'skuba': minor ---- - -lint: Overhaul internal linting system - -Previously, internal lint rules would not fail a `skuba lint` check but would silently make changes to your working tree. These changes may have never been committed and may have caused subsequent noise when running `skuba format` or `skuba lint`. - -Now, internal linting is now promoted to a top-level tool alongside ESLint, Prettier, and tsc. Rules will report whether changes need to be made, and changes will only be applied in `format` or autofix modes (in CI). As a consequence, `skuba lint` may fail upon upgrading to this version if your project has internal lint violations that have been left unaddressed up to this point. - -You can configure `skuba lint` to automatically push autofixes; this eases adoption of linting rule changes and automatically resolves issues arising from a forgotten `skuba format`. You'll need to configure your CI environment to support this feature. See our [GitHub autofixes](https://seek-oss.github.io/skuba/docs/deep-dives/github.html#github-autofixes) documentation to learn more. diff --git a/.changeset/five-kiwis-laugh.md b/.changeset/five-kiwis-laugh.md deleted file mode 100644 index 84413cef7..000000000 --- a/.changeset/five-kiwis-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': minor ---- - -format: Switch Distroless image from `nodejs-debian11` to `nodejs-debian12` diff --git a/.changeset/fresh-boats-yawn.md b/.changeset/fresh-boats-yawn.md deleted file mode 100644 index db3be1448..000000000 --- a/.changeset/fresh-boats-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -deps: picomatch ^3.0.0 diff --git a/.changeset/funny-mugs-brake.md b/.changeset/funny-mugs-brake.md deleted file mode 100644 index c5fa21986..000000000 --- a/.changeset/funny-mugs-brake.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'skuba': patch ---- - -Jest: Export `Config` type - -This resolves a TypeScript error that could present itself when using `Jest.mergePreset` with the [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) compiler option: - -> TS4082: Default export of the module has or is using private name `ConfigGlobals`. diff --git a/.changeset/great-trains-doubt.md b/.changeset/great-trains-doubt.md deleted file mode 100644 index dd7dbd7b1..000000000 --- a/.changeset/great-trains-doubt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -deps: Prettier 3.2 diff --git a/.changeset/green-flies-draw.md b/.changeset/green-flies-draw.md deleted file mode 100644 index f8a921677..000000000 --- a/.changeset/green-flies-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -template/lambda-sqs-worker: Remove `@aws-sdk/util-utf8-node` library diff --git a/.changeset/green-zebras-greet.md b/.changeset/green-zebras-greet.md deleted file mode 100644 index 252036896..000000000 --- a/.changeset/green-zebras-greet.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -'skuba': patch ---- - -build, build-package, test: Remove empty export synthesis for Jest setup files - -[`isolatedModules`](https://www.typescriptlang.org/tsconfig#isolatedModules) was enabled by default in [v5.0.0](https://github.com/seek-oss/skuba/releases/tag/v5.0.0). To ease this migration, the commands listed above were updated to dynamically synthesise an empty export for `jest.setup.ts` and `jest.setup.int.ts` files; this compatibility logic has now been removed. - -Up-to-date projects are unlikely to be affected, but you can easily add an empty export statement to placate the TypeScript compiler: - -```console -jest.setup.ts(1,1): error TS1208: 'jest.setup.ts' cannot be compiled under '--isolatedModules' because it is considered a global script file. Add an import, export, or an empty 'export {}' statement to make it a module. -``` - -```diff -process.env.ENVIRONMENT = 'test'; - -+ export {}; -``` diff --git a/.changeset/hip-forks-drum.md b/.changeset/hip-forks-drum.md deleted file mode 100644 index 4199516e2..000000000 --- a/.changeset/hip-forks-drum.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'skuba': minor ---- - -template: Initialise new projects with [pnpm](https://pnpm.io/) - -New projects based on built-in templates will now use pnpm as their package manager as per updated organisational guidance. - -Custom templates will continue to default to Yarn 1.x until a future major version, though you can opt in to pnpm via `skuba.template.js`: - -```diff -module.exports = { -+ packageManager: 'pnpm', -}; -``` diff --git a/.changeset/large-singers-argue.md b/.changeset/large-singers-argue.md deleted file mode 100644 index f04f6e51e..000000000 --- a/.changeset/large-singers-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -template/lambda-sqs-worker-cdk: Switch to `aws-cdk-lib/assertions` diff --git a/.changeset/late-tigers-smash.md b/.changeset/late-tigers-smash.md deleted file mode 100644 index e74eba834..000000000 --- a/.changeset/late-tigers-smash.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'skuba': minor ---- - -deps: TypeScript 5.3 - -This major release includes breaking changes. See the [TypeScript 5.3](https://devblogs.microsoft.com/typescript/announcing-typescript-5-3/) announcement for more information. diff --git a/.changeset/lucky-gorillas-grab.md b/.changeset/lucky-gorillas-grab.md deleted file mode 100644 index dd95b2e76..000000000 --- a/.changeset/lucky-gorillas-grab.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -template/\*-rest-api: Set `readonlyRootFilesystem` as a security best practice diff --git a/.changeset/new-crabs-buy.md b/.changeset/new-crabs-buy.md deleted file mode 100644 index 35097efc8..000000000 --- a/.changeset/new-crabs-buy.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -'skuba': patch ---- - -template: Use `propagate-environment` for Docker Compose Buildkite plugin - -This simplifies the Docker Compose environment variable configuration required for Buildkite and GitHub integrations. - -In your `docker-compose.yml`: - -```diff -services: - app: -- environment: -- # Enable Buildkite + GitHub integrations. -- - BUILDKITE -- - BUILDKITE_AGENT_ACCESS_TOKEN -- - BUILDKITE_BRANCH -- - BUILDKITE_BUILD_NUMBER -- - BUILDKITE_JOB_ID -- - BUILDKITE_PIPELINE_DEFAULT_BRANCH -- - BUILDKITE_STEP_ID -- - GITHUB_API_TOKEN - image: ${BUILDKITE_PLUGIN_DOCKER_IMAGE:-''} - init: true - volumes: - - ./:/workdir - # Mount agent for Buildkite annotations. - - /usr/bin/buildkite-agent:/usr/bin/buildkite-agent - # Mount cached dependencies. - - /workdir/node_modules -``` - -In your `.buildkite/pipeline.yml`: - -```diff -steps: - - commands: - - pnpm run lint - - pnpm run test - env: - # At SEEK, this instructs the build agent to populate the GITHUB_API_TOKEN environment variable for this step. - GET_GITHUB_TOKEN: 'please' - plugins: - - *aws-sm - - *private-npm - - *docker-ecr-cache - - docker-compose#v4.16.0: -+ environment: -+ - GITHUB_API_TOKEN -+ propagate-environment: true - run: app -``` diff --git a/.changeset/new-geese-flash.md b/.changeset/new-geese-flash.md deleted file mode 100644 index a52301c57..000000000 --- a/.changeset/new-geese-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -template/\*-rest-api: Disable dev CloudWatch dashboards for cost savings diff --git a/.changeset/nine-coins-hear.md b/.changeset/nine-coins-hear.md deleted file mode 100644 index e5a3d4582..000000000 --- a/.changeset/nine-coins-hear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -template/lambda-sqs-worker-cdk: Add blue-green deployment, smoke test and version pruning functionality diff --git a/.changeset/orange-moles-yell.md b/.changeset/orange-moles-yell.md deleted file mode 100644 index 79b097d74..000000000 --- a/.changeset/orange-moles-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': minor ---- - -init: Default to `arm64` platform and `main` branch diff --git a/.changeset/plenty-rings-wink.md b/.changeset/plenty-rings-wink.md deleted file mode 100644 index 71d6b7746..000000000 --- a/.changeset/plenty-rings-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -init: Run Prettier after templating diff --git a/.changeset/pretty-needles-hope.md b/.changeset/pretty-needles-hope.md deleted file mode 100644 index 38cd87468..000000000 --- a/.changeset/pretty-needles-hope.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'skuba': patch ---- - -template/lambda-sqs-worker\*: Set [maximum concurrency](https://aws.amazon.com/blogs/compute/introducing-maximum-concurrency-of-aws-lambda-functions-when-using-amazon-sqs-as-an-event-source/) - -This prevents messages from going directly to the DLQ when the function reaches its reserved concurrency limit. diff --git a/.changeset/quiet-pants-beam.md b/.changeset/quiet-pants-beam.md deleted file mode 100644 index df64ddee6..000000000 --- a/.changeset/quiet-pants-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': patch ---- - -template/koa-rest-api: Improve input validation error response for Zod unions diff --git a/.changeset/rude-bobcats-unite.md b/.changeset/rude-bobcats-unite.md deleted file mode 100644 index dd550ee75..000000000 --- a/.changeset/rude-bobcats-unite.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'skuba': patch ---- - -template/lambda-sqs-worker-cdk: Introduce bundling with esbuild, `--hotswap` and `--watch` - -This template now uses the `aws_lambda_nodejs.NodejsFunction` construct which uses esbuild to bundle the Lambda function. This [reduces cold start time](https://aws.amazon.com/blogs/developer/reduce-lambda-cold-start-times-migrate-to-aws-sdk-for-javascript-v3/) and time to build on CI. - -The `--hotswap` and `--watch` options allow you to rapidly deploy your code changes to AWS, enhancing the developer feedback loop. This change introduces `deploy:hotswap` and `deploy:watch` scripts to the `package.json` manifest and a `Deploy Dev (Hotswap)` step to the Buildkite pipeline. Read more about watch and hotswap [on the AWS Developer Tools Blog](https://aws.amazon.com/blogs/developer/increasing-development-speed-with-cdk-watch/). diff --git a/.changeset/smart-ghosts-complain.md b/.changeset/smart-ghosts-complain.md deleted file mode 100644 index b3a224007..000000000 --- a/.changeset/smart-ghosts-complain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'skuba': minor ---- - -init: Support `main` default branch diff --git a/.changeset/twelve-masks-turn.md b/.changeset/twelve-masks-turn.md deleted file mode 100644 index 26bfb7818..000000000 --- a/.changeset/twelve-masks-turn.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'skuba': minor ---- - -lint: Introduce skuba patches - -This feature adds patches which are run only once on the `lint` or `format` commands following a skuba update. If your build pipeline is utilising [autofixes](https://seek-oss.github.io/skuba/docs/deep-dives/github.html#github-autofixes), these changes will be pushed up automatically. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1463f2c46..f3a2b24c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,158 @@ # skuba +## 7.4.0 + +### Minor Changes + +- **lint:** Overhaul internal linting system ([#1370](https://github.com/seek-oss/skuba/pull/1370)) + + Previously, internal lint rules would not fail a `skuba lint` check but would silently make changes to your working tree. These changes may have never been committed and may have caused subsequent noise when running `skuba format` or `skuba lint`. + + Now, internal linting is now promoted to a top-level tool alongside ESLint, Prettier, and tsc. Rules will report whether changes need to be made, and changes will only be applied in `format` or autofix modes (in CI). As a consequence, `skuba lint` may fail upon upgrading to this version if your project has internal lint violations that have been left unaddressed up to this point. + + You can configure `skuba lint` to automatically push autofixes; this eases adoption of linting rule changes and automatically resolves issues arising from a forgotten `skuba format`. You'll need to configure your CI environment to support this feature. See our [GitHub autofixes](https://seek-oss.github.io/skuba/docs/deep-dives/github.html#github-autofixes) documentation to learn more. + +- **format:** Switch Distroless image from `nodejs-debian11` to `nodejs-debian12` ([#1381](https://github.com/seek-oss/skuba/pull/1381)) + +- **template:** Initialise new projects with [pnpm](https://pnpm.io/) ([#1289](https://github.com/seek-oss/skuba/pull/1289)) + + New projects based on built-in templates will now use pnpm as their package manager as per updated organisational guidance. + + Custom templates will continue to default to Yarn 1.x until a future major version, though you can opt in to pnpm via `skuba.template.js`: + + ```diff + module.exports = { + + packageManager: 'pnpm', + }; + ``` + +- **deps:** TypeScript 5.3 ([#1324](https://github.com/seek-oss/skuba/pull/1324)) + + This major release includes breaking changes. See the [TypeScript 5.3](https://devblogs.microsoft.com/typescript/announcing-typescript-5-3/) announcement for more information. + +- **init:** Default to `arm64` platform and `main` branch ([#1343](https://github.com/seek-oss/skuba/pull/1343)) + +- **init:** Support `main` default branch ([#1335](https://github.com/seek-oss/skuba/pull/1335)) + +- **lint:** Introduce skuba patches ([#1274](https://github.com/seek-oss/skuba/pull/1274)) + + This feature adds patches which are run only once on the `lint` or `format` commands following a skuba update. If your build pipeline is utilising [autofixes](https://seek-oss.github.io/skuba/docs/deep-dives/github.html#github-autofixes), these changes will be pushed up automatically. + +### Patch Changes + +- **lint:** Disable `Promise` return checks in tests ([#1366](https://github.com/seek-oss/skuba/pull/1366)) + + This works around an [existing incompatibility](https://github.com/koajs/koa/issues/1755) between Koa and the built-in `http.RequestListener` type: + + ```typescript + const app = new Koa(); + + const agent = supertest.agent(app.callback()); + // ~~~~~~~~~~~~~~ + // Promise returned in function argument where a void return was expected. + // @typescript-eslint/no-misused-promises + ``` + +- **deps:** picomatch ^3.0.0 ([#1309](https://github.com/seek-oss/skuba/pull/1309)) + +- **Jest:** Export `Config` type ([#1360](https://github.com/seek-oss/skuba/pull/1360)) + + This resolves a TypeScript error that could present itself when using `Jest.mergePreset` with the [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) compiler option: + + > TS4082: Default export of the module has or is using private name `ConfigGlobals`. + +- **deps:** Prettier 3.2 ([#1384](https://github.com/seek-oss/skuba/pull/1384)) + +- **template/lambda-sqs-worker:** Remove `@aws-sdk/util-utf8-node` library ([#1326](https://github.com/seek-oss/skuba/pull/1326)) + +- **build, build-package, test:** Remove empty export synthesis for Jest setup files ([#1274](https://github.com/seek-oss/skuba/pull/1274)) + + [`isolatedModules`](https://www.typescriptlang.org/tsconfig#isolatedModules) was enabled by default in [v5.0.0](https://github.com/seek-oss/skuba/releases/tag/v5.0.0). To ease this migration, the commands listed above were updated to dynamically synthesise an empty export for `jest.setup.ts` and `jest.setup.int.ts` files; this compatibility logic has now been removed. + + Up-to-date projects are unlikely to be affected, but you can easily add an empty export statement to placate the TypeScript compiler: + + ```console + jest.setup.ts(1,1): error TS1208: 'jest.setup.ts' cannot be compiled under '--isolatedModules' because it is considered a global script file. Add an import, export, or an empty 'export {}' statement to make it a module. + ``` + + ```diff + process.env.ENVIRONMENT = 'test'; + + + export {}; + ``` + +- **template/lambda-sqs-worker-cdk:** Switch to `aws-cdk-lib/assertions` ([#1372](https://github.com/seek-oss/skuba/pull/1372)) + +- **template/\*-rest-api:** Set `readonlyRootFilesystem` as a security best practice ([#1394](https://github.com/seek-oss/skuba/pull/1394)) + +- **template:** Use `propagate-environment` for Docker Compose Buildkite plugin ([#1392](https://github.com/seek-oss/skuba/pull/1392)) + + This simplifies the Docker Compose environment variable configuration required for Buildkite and GitHub integrations. + + In your `docker-compose.yml`: + + ```diff + services: + app: + - environment: + - # Enable Buildkite + GitHub integrations. + - - BUILDKITE + - - BUILDKITE_AGENT_ACCESS_TOKEN + - - BUILDKITE_BRANCH + - - BUILDKITE_BUILD_NUMBER + - - BUILDKITE_JOB_ID + - - BUILDKITE_PIPELINE_DEFAULT_BRANCH + - - BUILDKITE_STEP_ID + - - GITHUB_API_TOKEN + image: ${BUILDKITE_PLUGIN_DOCKER_IMAGE:-''} + init: true + volumes: + - ./:/workdir + # Mount agent for Buildkite annotations. + - /usr/bin/buildkite-agent:/usr/bin/buildkite-agent + # Mount cached dependencies. + - /workdir/node_modules + ``` + + In your `.buildkite/pipeline.yml`: + + ```diff + steps: + - commands: + - pnpm run lint + - pnpm run test + env: + # At SEEK, this instructs the build agent to populate the GITHUB_API_TOKEN environment variable for this step. + GET_GITHUB_TOKEN: 'please' + plugins: + - *aws-sm + - *private-npm + - *docker-ecr-cache + - docker-compose#v4.16.0: + + environment: + + - GITHUB_API_TOKEN + + propagate-environment: true + run: app + ``` + +- **template/\*-rest-api:** Disable dev CloudWatch dashboards for cost savings ([#1395](https://github.com/seek-oss/skuba/pull/1395)) + +- **template/lambda-sqs-worker-cdk:** Add blue-green deployment, smoke test and version pruning functionality ([#1327](https://github.com/seek-oss/skuba/pull/1327)) + +- **init:** Run Prettier after templating ([#1337](https://github.com/seek-oss/skuba/pull/1337)) + +- **template/lambda-sqs-worker\*:** Set [maximum concurrency](https://aws.amazon.com/blogs/compute/introducing-maximum-concurrency-of-aws-lambda-functions-when-using-amazon-sqs-as-an-event-source/) ([#1412](https://github.com/seek-oss/skuba/pull/1412)) + + This prevents messages from going directly to the DLQ when the function reaches its reserved concurrency limit. + +- **template/koa-rest-api:** Improve input validation error response for Zod unions ([#1339](https://github.com/seek-oss/skuba/pull/1339)) + +- **template/lambda-sqs-worker-cdk:** Introduce bundling with esbuild, `--hotswap` and `--watch` ([#1321](https://github.com/seek-oss/skuba/pull/1321)) + + This template now uses the `aws_lambda_nodejs.NodejsFunction` construct which uses esbuild to bundle the Lambda function. This [reduces cold start time](https://aws.amazon.com/blogs/developer/reduce-lambda-cold-start-times-migrate-to-aws-sdk-for-javascript-v3/) and time to build on CI. + + The `--hotswap` and `--watch` options allow you to rapidly deploy your code changes to AWS, enhancing the developer feedback loop. This change introduces `deploy:hotswap` and `deploy:watch` scripts to the `package.json` manifest and a `Deploy Dev (Hotswap)` step to the Buildkite pipeline. Read more about watch and hotswap [on the AWS Developer Tools Blog](https://aws.amazon.com/blogs/developer/increasing-development-speed-with-cdk-watch/). + ## 7.3.1 ### Patch Changes diff --git a/package.json b/package.json index f290c4ce6..9a42a0bb2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "skuba", - "version": "7.3.1", + "version": "7.4.0", "private": false, "description": "SEEK development toolkit for backend applications and packages", "homepage": "https://github.com/seek-oss/skuba#readme", @@ -168,6 +168,6 @@ "entryPoint": "src/index.ts", "template": null, "type": "package", - "version": "7.3.1" + "version": "7.4.0" } }