diff --git a/README.md b/README.md index 2f09879f03..14b28c3eb8 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Instead of running `pnpm start:base`, you can alternatively use `pnpm start:all` | :4204 | `root (/)` drafts realm | ✅ | 🚫 | | :4205 | qunit server mounting realms in iframes for testing | ✅ | 🚫 | | :5001 | Mail user interface for viewing emails sent to local SMTP | ✅ | 🚫 | +| :5435 | Postgres DB | ✅ | 🚫 | | :8008 | Matrix synapse server | ✅ | 🚫 | #### Using `start:development` diff --git a/packages/host/app/lib/SQLiteAdapter.ts b/packages/host/app/lib/sqlite-adapter.ts similarity index 100% rename from packages/host/app/lib/SQLiteAdapter.ts rename to packages/host/app/lib/sqlite-adapter.ts diff --git a/packages/host/tests/unit/indexer-test.ts b/packages/host/tests/unit/indexer-test.ts index d679e12bdd..47b7b9ad4b 100644 --- a/packages/host/tests/unit/indexer-test.ts +++ b/packages/host/tests/unit/indexer-test.ts @@ -9,7 +9,7 @@ import { } from '@cardstack/runtime-common'; import ENV from '@cardstack/host/config/environment'; -import SQLiteAdapter from '@cardstack/host/lib/SQLiteAdapter'; +import SQLiteAdapter from '@cardstack/host/lib/sqlite-adapter'; import { testRealmURL, setupIndex } from '../helpers'; diff --git a/packages/host/tests/unit/query-test.ts b/packages/host/tests/unit/query-test.ts index bd5548993d..ed958ec8c4 100644 --- a/packages/host/tests/unit/query-test.ts +++ b/packages/host/tests/unit/query-test.ts @@ -12,8 +12,8 @@ import { } from '@cardstack/runtime-common'; import ENV from '@cardstack/host/config/environment'; -import SQLiteAdapter from '@cardstack/host/lib/SQLiteAdapter'; import { shimExternals } from '@cardstack/host/lib/externals'; +import SQLiteAdapter from '@cardstack/host/lib/sqlite-adapter'; import { CardDef } from 'https://cardstack.com/base/card-api'; diff --git a/packages/host/tests/unit/sqlite-test.ts b/packages/host/tests/unit/sqlite-test.ts index e43b4c2c18..2b2a898516 100644 --- a/packages/host/tests/unit/sqlite-test.ts +++ b/packages/host/tests/unit/sqlite-test.ts @@ -1,6 +1,6 @@ import { module, test } from 'qunit'; -import SQLiteAdapter from '@cardstack/host/lib/SQLiteAdapter'; +import SQLiteAdapter from '@cardstack/host/lib/sqlite-adapter'; module('Unit | sqlite | SQLiteAdapter', function () { test('run a sqlite db using the SQLiteAdapter', async function (assert) { diff --git a/packages/realm-server/migrations/.eslintrc.js b/packages/realm-server/migrations/.eslintrc.js new file mode 100644 index 0000000000..9cd5fbd2a6 --- /dev/null +++ b/packages/realm-server/migrations/.eslintrc.js @@ -0,0 +1,17 @@ +'use strict'; + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'script', + }, + env: { + browser: false, + node: true, + }, + extends: ['plugin:n/recommended'], + rules: { + camelcase: 'off', + }, +}; diff --git a/packages/realm-server/migrations/1712771547705_initial.js b/packages/realm-server/migrations/1712771547705_initial.js new file mode 100644 index 0000000000..62f1e0742f --- /dev/null +++ b/packages/realm-server/migrations/1712771547705_initial.js @@ -0,0 +1,62 @@ +exports.up = (pgm) => { + pgm.createTable('indexed_cards', { + card_url: { type: 'varchar', notNull: true }, + realm_version: { type: 'varchar', notNull: true }, + realm_url: { type: 'varchar', notNull: true }, + pristine_doc: 'jsonb', + search_doc: 'jsonb', + error_doc: 'jsonb', + deps: 'jsonb', + types: 'jsonb', + embedded_html: 'varchar', + isolated_html: 'varchar', + indexed_at: 'integer', + is_deleted: 'boolean', + }); + pgm.sql('ALTER TABLE indexed_cards SET UNLOGGED'); + pgm.addConstraint('indexed_cards', 'indexed_cards_pkey', { + primaryKey: ['card_url', 'realm_version'], + }); + pgm.createIndex('indexed_cards', ['realm_version']); + pgm.createIndex('indexed_cards', ['realm_url']); + + pgm.createTable('realm_versions', { + realm_url: { type: 'varchar', notNull: true }, + current_version: { type: 'integer', notNull: true }, + }); + + pgm.sql('ALTER TABLE realm_versions SET UNLOGGED'); + pgm.addConstraint('realm_versions', 'realm_versions_pkey', { + primaryKey: ['realm_url'], + }); + pgm.createIndex('realm_versions', ['current_version']); + + pgm.createType('job_statuses', ['unfulfilled', 'resolved', 'rejected']); + pgm.createTable('jobs', { + id: 'id', // shorthand for primary key that is an auto incremented id + category: { + type: 'varchar', + notNull: true, + }, + args: 'jsonb', + status: { + type: 'job_statuses', + default: 'unfulfilled', + notNull: true, + }, + created_at: { + type: 'timestamp', + notNull: true, + default: pgm.func('current_timestamp'), + }, + finished_at: { + type: 'timestamp', + }, + queue: { + type: 'varchar', + notNull: true, + }, + result: 'jsonb', + }); + pgm.sql('ALTER TABLE jobs SET UNLOGGED'); +}; diff --git a/packages/realm-server/package.json b/packages/realm-server/package.json index 5f4b352568..4e92c50b68 100644 --- a/packages/realm-server/package.json +++ b/packages/realm-server/package.json @@ -21,6 +21,8 @@ "@types/lodash": "^4.14.182", "@types/mime-types": "^2.1.1", "@types/node": "^18.18.5", + "@types/node-pg-migrate": "^2.3.1", + "@types/pg": "^8.11.5", "@types/qs": "^6.9.14", "@types/qunit": "^2.11.3", "@types/sane": "^2.0.1", @@ -46,7 +48,9 @@ "lodash": "^4.17.21", "loglevel": "^1.8.1", "mime-types": "^2.1.35", + "node-pg-migrate": "^6.2.2", "npm-run-all": "^4.1.5", + "pg": "^8.11.5", "prettier": "^2.8.4", "prettier-plugin-ember-template-tag": "^1.1.0", "qs": "^6.10.5", @@ -63,11 +67,13 @@ "yargs": "^17.5.1" }, "scripts": { - "test": "NODE_NO_WARNINGS=1 qunit --require ts-node/register/transpile-only tests/index.ts", + "test": "./scripts/remove-test-dbs.sh && NODE_NO_WARNINGS=1 PGPORT=5435 qunit --require ts-node/register/transpile-only tests/index.ts", "test:dom": "cd ../matrix && pnpm register-test-user && cd ../realm-server && start-server-and-test 'pnpm run wait' 'http://127.0.0.1:4205' 'testem ci -f ./testem.js'", "start:test-container": "http-server ./dom-tests -p 4205 --silent", "start:matrix": "cd ../matrix && pnpm assert-synapse-running", "start:smtp": "cd ../matrix && pnpm assert-smtp-running", + "start:pg": "./scripts/start-pg.sh", + "stop:pg": "./scripts/stop-pg.sh", "test:wait-for-servers": "NODE_NO_WARNINGS=1 start-server-and-test 'pnpm run wait' 'http-get://localhost:4201/base/fields/boolean-field?acceptHeader=application%2Fvnd.card%2Bjson' 'pnpm run wait' 'http-get://localhost:4202/node-test/person-1?acceptHeader=application%2Fvnd.card%2Bjson|http://localhost:8008|http://localhost:5001' 'test'", "setup:base-in-deployment": "rm -rf /persistent/base && cp -R ../base /persistent", "setup:drafts-in-deployment": "mkdir -p /persistent/drafts && cp --verbose --update --recursive ../drafts-realm/. /persistent/drafts/", @@ -88,7 +94,8 @@ "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"", "lint:js": "eslint . --cache", "lint:js:fix": "eslint . --fix", - "lint:glint": "glint" + "lint:glint": "glint", + "migrate": "node-pg-migrate" }, "volta": { "extends": "../../package.json" diff --git a/packages/realm-server/pg-adapter.ts b/packages/realm-server/pg-adapter.ts new file mode 100644 index 0000000000..d06d7c22ca --- /dev/null +++ b/packages/realm-server/pg-adapter.ts @@ -0,0 +1,100 @@ +import { + type DBAdapter, + type PgPrimitive, + type ExecuteOptions, +} from '@cardstack/runtime-common'; +import migrate from 'node-pg-migrate'; +import { join } from 'path'; +import { Pool, Client } from 'pg'; + +import postgresConfig from './pg-config'; + +function config() { + return postgresConfig({ + database: 'boxel', + }); +} + +export default class PgAdapter implements DBAdapter { + private pool: Pool; + + constructor() { + let { user, host, database, password, port } = config(); + console.log(`connecting to DB ${user}@${host}:${port}/${database}`); + this.pool = new Pool({ + user, + host, + database, + password, + port, + }); + } + + async startClient() { + await this.migrateDb(); + } + + async close() { + if (this.pool) { + await this.pool.end(); + } + } + + async execute( + sql: string, + opts?: ExecuteOptions, + ): Promise[]> { + let client = await this.pool.connect(); + try { + let { rows } = await client.query({ text: sql, values: opts?.bind }); + return rows; + } catch (e: any) { + console.error( + `Error executing SQL ${e.result.message}:\n${sql}${ + opts?.bind ? ' with bindings: ' + JSON.stringify(opts?.bind) : '' + }`, + e, + ); + throw e; + } finally { + client.release(); + } + } + + private async migrateDb() { + const config = postgresConfig(); + let client = new Client( + Object.assign({}, config, { database: 'postgres' }), + ); + try { + await client.connect(); + let response = await client.query( + `select count(*)=1 as has_database from pg_database where datname=$1`, + [config.database], + ); + if (!response.rows[0].has_database) { + await client.query(`create database ${config.database}`); + } + } finally { + client.end(); + } + + await migrate({ + direction: 'up', + migrationsTable: 'migrations', + singleTransaction: true, + checkOrder: false, + databaseUrl: { + user: config.user, + host: config.host, + database: config.database, + password: config.password, + port: config.port, + }, + count: Infinity, + dir: join(__dirname, 'migrations'), + ignorePattern: '.*\\.eslintrc\\.js', + log: (...args) => console.log(...args), + }); + } +} diff --git a/packages/realm-server/pg-config.ts b/packages/realm-server/pg-config.ts new file mode 100644 index 0000000000..3501517c4f --- /dev/null +++ b/packages/realm-server/pg-config.ts @@ -0,0 +1,11 @@ +import { type ClientConfig } from 'pg'; + +export default function postgresConfig(defaultConfig: ClientConfig = {}) { + return Object.assign({}, defaultConfig, { + host: process.env.PGHOST || 'localhost', + port: process.env.PGPORT || '5432', + user: process.env.PGUSER || 'postgres', + password: process.env.PGPASSWORD || undefined, + database: process.env.PGDATABASE || 'postgres', + }); +} diff --git a/packages/realm-server/scripts/remove-test-dbs.sh b/packages/realm-server/scripts/remove-test-dbs.sh new file mode 100755 index 0000000000..89058c1765 --- /dev/null +++ b/packages/realm-server/scripts/remove-test-dbs.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +databases=$(docker exec boxel-pg psql -U postgres -w -lqt | cut -d \| -f 1 | grep -E 'test_db_' | tr -d ' ') + +for db in $databases; do + docker exec boxel-pg dropdb -U postgres -w $db +done diff --git a/packages/realm-server/scripts/start-all.sh b/packages/realm-server/scripts/start-all.sh index 5a67355a83..ba874a161f 100755 --- a/packages/realm-server/scripts/start-all.sh +++ b/packages/realm-server/scripts/start-all.sh @@ -1,7 +1,7 @@ #! /bin/sh NODE_NO_WARNINGS=1 start-server-and-test \ - 'run-p start:matrix start:smtp start:development start:base:root' \ + 'run-p start:pg start:matrix start:smtp start:development start:base:root' \ 'http-get://localhost:4201/base/fields/boolean-field?acceptHeader=application%2Fvnd.card%2Bjson|http-get://localhost:4203/fields/boolean-field?acceptHeader=application%2Fvnd.card%2Bjson|http-get://localhost:4201/drafts/index?acceptHeader=application%2Fvnd.card%2Bjson|http://localhost:8008|http://localhost:5001' \ 'run-p start:test-realms start:test-container' \ 'http-get://localhost:4202/node-test/person-1?acceptHeader=application%2Fvnd.card%2Bjson|http-get://127.0.0.1:4205' \ diff --git a/packages/realm-server/scripts/start-development.sh b/packages/realm-server/scripts/start-development.sh index c034dc4f94..5d08a59019 100755 --- a/packages/realm-server/scripts/start-development.sh +++ b/packages/realm-server/scripts/start-development.sh @@ -1,6 +1,11 @@ #! /bin/sh pnpm setup:base-assets -NODE_ENV=development NODE_NO_WARNINGS=1 LOG_LEVELS='*=info' REALM_SECRET_SEED="shhh! it's a secret" ts-node \ +NODE_ENV=development \ + NODE_NO_WARNINGS=1 \ + LOG_LEVELS='*=info' \ + REALM_SECRET_SEED="shhh! it's a secret" \ + PGPORT="5435" \ + ts-node \ --transpileOnly main \ --port=4201 \ \ diff --git a/packages/realm-server/scripts/start-pg.sh b/packages/realm-server/scripts/start-pg.sh new file mode 100755 index 0000000000..46bc925538 --- /dev/null +++ b/packages/realm-server/scripts/start-pg.sh @@ -0,0 +1,8 @@ +#! /bin/sh +if [ -z "$(docker ps -f name=boxel-pg --all --format '{{.Names}}')" ]; then + # running postgres on port 5435 so it doesn't collide with native postgres + # that may be running on your system + docker run --name boxel-pg -e POSTGRES_HOST_AUTH_METHOD=trust -p 5435:5432 -d postgres >/dev/null +else + docker start boxel-pg >/dev/null +fi diff --git a/packages/realm-server/scripts/start-test-realms.sh b/packages/realm-server/scripts/start-test-realms.sh index 29d62cd9e3..1fa2bdd072 100755 --- a/packages/realm-server/scripts/start-test-realms.sh +++ b/packages/realm-server/scripts/start-test-realms.sh @@ -1,6 +1,10 @@ #! /bin/sh -NODE_ENV=test NODE_NO_WARNINGS=1 REALM_SECRET_SEED="shhh! it's a secret" ts-node \ +NODE_ENV=test \ + NODE_NO_WARNINGS=1 \ + REALM_SECRET_SEED="shhh! it's a secret" \ + PGPORT="5435" \ + ts-node \ --transpileOnly main \ --port=4202 \ \ diff --git a/packages/realm-server/scripts/stop-pg.sh b/packages/realm-server/scripts/stop-pg.sh new file mode 100755 index 0000000000..6bf33ffcc1 --- /dev/null +++ b/packages/realm-server/scripts/stop-pg.sh @@ -0,0 +1,2 @@ +#! /bin/sh +docker stop boxel-pg >/dev/null diff --git a/packages/realm-server/tests/helpers/index.ts b/packages/realm-server/tests/helpers/index.ts index f07652de4b..1beaa2dfd5 100644 --- a/packages/realm-server/tests/helpers/index.ts +++ b/packages/realm-server/tests/helpers/index.ts @@ -28,6 +28,10 @@ let basePath = resolve(join(__dirname, '..', '..', '..', 'base')); let manager = new RunnerOptionsManager(); let getRunner: IndexRunner | undefined; +export async function prepareTestDB() { + process.env.PGDATABASE = `test_db_${Math.floor(10000000 * Math.random())}`; +} + export async function createRealm( loader: Loader, dir: string, diff --git a/packages/realm-server/tests/index.ts b/packages/realm-server/tests/index.ts index 3e125f176d..69c5751e24 100644 --- a/packages/realm-server/tests/index.ts +++ b/packages/realm-server/tests/index.ts @@ -5,3 +5,4 @@ import './indexing-test'; import './module-syntax-test'; import './permissions/permission-checker-test'; import './auth-client-test'; +import './pg-test'; diff --git a/packages/realm-server/tests/pg-test.ts b/packages/realm-server/tests/pg-test.ts new file mode 100644 index 0000000000..ecc8dbe8c0 --- /dev/null +++ b/packages/realm-server/tests/pg-test.ts @@ -0,0 +1,67 @@ +import { module, test } from 'qunit'; +import { prepareTestDB } from './helpers'; +import PgAdapter from '../pg-adapter'; + +module('Postgres', function (hooks) { + hooks.beforeEach(async function () { + prepareTestDB(); + }); + + test('it can connect to the DB and run migrations', async function (assert) { + let adapter = new PgAdapter(); + await adapter.startClient(); + + { + let result = await adapter.execute(` + SELECT column_name + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'indexed_cards' + `); + let columns = result.map((r) => r.column_name); + assert.deepEqual(columns, [ + 'card_url', + 'realm_version', + 'realm_url', + 'pristine_doc', + 'search_doc', + 'error_doc', + 'deps', + 'types', + 'embedded_html', + 'isolated_html', + 'indexed_at', + 'is_deleted', + ]); + } + { + let result = await adapter.execute(` + SELECT column_name + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'realm_versions' + `); + let columns = result.map((r) => r.column_name); + assert.deepEqual(columns, ['realm_url', 'current_version']); + } + { + let result = await adapter.execute(` + SELECT column_name + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'jobs' + `); + let columns = result.map((r) => r.column_name); + assert.deepEqual(columns, [ + 'id', + 'category', + 'args', + 'status', + 'created_at', + 'finished_at', + 'queue', + 'result', + ]); + } + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d365d1341f..3af74addea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + overrides: '@types/eslint': 8.4.1 '@embroider/util': 1.12.0 @@ -1485,6 +1489,12 @@ importers: '@types/node': specifier: ^18.18.5 version: 18.18.5 + '@types/node-pg-migrate': + specifier: ^2.3.1 + version: 2.3.1 + '@types/pg': + specifier: ^8.11.5 + version: 8.11.5 '@types/qs': specifier: ^6.9.14 version: 6.9.14 @@ -1560,9 +1570,15 @@ importers: mime-types: specifier: ^2.1.35 version: 2.1.35 + node-pg-migrate: + specifier: ^6.2.2 + version: 6.2.2(pg@8.11.5) npm-run-all: specifier: ^4.1.5 version: 4.1.5 + pg: + specifier: ^8.11.5 + version: 8.11.5 prettier: specifier: github:cardstack/prettier#glimmer-style-tag-in-template-support version: github.com/cardstack/prettier/60eccfdc598d682a931d3c569ffb0c4f92ef5db6 @@ -4400,7 +4416,7 @@ packages: '@types/ember__object': ^4.0.4 '@types/ember__routing': ^4.0.11 ember-cli-htmlbars: ^6.0.1 - ember-modifier: ^3.2.7 || ^4.0.0 + ember-modifier: ^4.1.0 peerDependenciesMeta: '@types/ember__array': optional: true @@ -5044,7 +5060,7 @@ packages: /@prettier/sync@0.2.1(prettier@3.1.0-dev): resolution: {integrity: sha512-7ls1R6//+GPYD9vof1XaL5psViv83CwpdwlS8oUkWldYgbPhzZ3WgxIQMWqGyBmWPmoBfQg8C7jj7KI/ZuDHhQ==} peerDependencies: - prettier: ^3.0.0 + prettier: github:cardstack/prettier#glimmer-style-tag-in-template-support dependencies: prettier: github.com/cardstack/prettier/60eccfdc598d682a931d3c569ffb0c4f92ef5db6 dev: true @@ -5593,6 +5609,11 @@ packages: form-data: 4.0.0 dev: false + /@types/node-pg-migrate@2.3.1: + resolution: {integrity: sha512-ccKzTzDu8b6IRttXh1Ch6JlEQvVM4jrRUU9xyEp+Pq5yd3vBCg84pJeAUzycyts5Tg+eyiLqmlIW3g/SrRlqEQ==} + deprecated: This is a stub types definition for node-pg-migrate (https://github.com/theoephraim/node-pg-migrate#readme). node-pg-migrate provides its own type definitions, so you don\'t need @types/node-pg-migrate installed! + dev: true + /@types/node@18.15.13: resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} dev: true @@ -5604,6 +5625,14 @@ packages: resolution: {integrity: sha512-3YmXzzPAdOTVljVMkTMBdBEvlOLg2cDQaDhnnhT3nT9uDbnJzjWhKlzb+desT12Y7tGqaN6d+AbozcKzyL36Ng==} dev: true + /@types/pg@8.11.5: + resolution: {integrity: sha512-2xMjVviMxneZHDHX5p5S6tsRRs7TpDHeeK7kTTMe/kAC/mRRNjWHjZg0rkiY+e17jXSZV3zJYDxXV8Cy72/Vuw==} + dependencies: + '@types/node': 18.18.5 + pg-protocol: 1.6.1 + pg-types: 4.0.2 + dev: true + /@types/pluralize@0.0.30: resolution: {integrity: sha512-kVww6xZrW/db5BR9OqiT71J9huRdQ+z/r+LbDuT7/EK50mCmj5FoaIARnVv0rvjUS/YpDox0cDU9lpQT011VBA==} dev: false @@ -10229,6 +10258,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decamelize@5.0.1: + resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} + engines: {node: '>=10'} + dev: true + /decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true @@ -12385,7 +12419,7 @@ packages: engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} peerDependencies: ember-template-lint: '>= 4.0.0' - prettier: '>= 3.0.0' + prettier: github:cardstack/prettier#glimmer-style-tag-in-template-support dependencies: '@prettier/sync': 0.2.1(prettier@3.1.0-dev) ember-template-lint: 5.11.2 @@ -12579,7 +12613,7 @@ packages: resolution: {integrity: sha512-4V6rT9b9XSeMaPmiYYA2hIhfMD088vru6AvfGWBY7L1w/st5hYv6iIfamy9+W1l9xKvIH3Hcl8+B0637/SRmzQ==} engines: {node: 14.* || >= 16} peerDependencies: - ember-modifier: ^3.2.7 || >= 4.0.0 + ember-modifier: ^4.1.0 ember-source: ^3.28.0 || ^4.0.0 || >= 5.0.0 dependencies: '@embroider/addon-shim': 1.8.7 @@ -13238,7 +13272,7 @@ packages: peerDependencies: eslint: '>=5.0.0' eslint-config-prettier: '*' - prettier: '>=1.13.0' + prettier: github:cardstack/prettier#glimmer-style-tag-in-template-support peerDependenciesMeta: eslint-config-prettier: optional: true @@ -13253,10 +13287,10 @@ packages: resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - '@types/eslint': '>=8.0.0' + '@types/eslint': 8.4.1 eslint: '>=8.0.0' eslint-config-prettier: '*' - prettier: '>=3.0.0' + prettier: github:cardstack/prettier#glimmer-style-tag-in-template-support peerDependenciesMeta: '@types/eslint': optional: true @@ -13274,10 +13308,10 @@ packages: resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - '@types/eslint': '>=8.0.0' + '@types/eslint': 8.4.1 eslint: '>=8.0.0' eslint-config-prettier: '*' - prettier: '>=3.0.0' + prettier: github:cardstack/prettier#glimmer-style-tag-in-template-support peerDependenciesMeta: '@types/eslint': optional: true @@ -18088,6 +18122,20 @@ packages: which: 2.0.2 dev: true + /node-pg-migrate@6.2.2(pg@8.11.5): + resolution: {integrity: sha512-0WYLTXpWu2doeZhiwJUW/1u21OqAFU2CMQ8YZ8VBcJ0xrdqYAjtd8GGFe5A5DM4NJdIZsqJcLPDFqY0FQsmivw==} + engines: {node: '>=12.20.0'} + hasBin: true + peerDependencies: + pg: '>=4.3.0 <9.0.0' + dependencies: + '@types/pg': 8.11.5 + decamelize: 5.0.1 + mkdirp: 1.0.4 + pg: 8.11.5 + yargs: 17.3.1 + dev: true + /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} @@ -18315,6 +18363,10 @@ packages: es-abstract: 1.22.2 dev: true + /obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + dev: true + /oidc-client-ts@2.4.0: resolution: {integrity: sha512-WijhkTrlXK2VvgGoakWJiBdfIsVGz6CFzgjNNqZU1hPKV2kyeEaJgLs7RwuiSp2WhLfWBQuLvr2SxVlZnk3N1w==} engines: {node: '>=12.13.0'} @@ -18910,6 +18962,86 @@ packages: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} dev: true + /pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + requiresBuild: true + dev: true + optional: true + + /pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + dev: true + + /pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + dev: true + + /pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + dev: true + + /pg-pool@3.6.2(pg@8.11.5): + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} + peerDependencies: + pg: '>=8.0' + dependencies: + pg: 8.11.5 + dev: true + + /pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + dev: true + + /pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + dev: true + + /pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + dev: true + + /pg@8.11.5: + resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + dependencies: + pg-connection-string: 2.6.4 + pg-pool: 3.6.2(pg@8.11.5) + pg-protocol: 1.6.1 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + dev: true + + /pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + dependencies: + split2: 4.2.0 + dev: true + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -19105,6 +19237,54 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + dev: true + + /postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + dev: true + + /postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + dev: true + + /postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + dependencies: + obuf: 1.1.2 + dev: true + + /postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + dev: true + + /postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + dev: true + + /postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + dependencies: + xtend: 4.0.2 + dev: true + + /postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + dev: true + + /postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} + dev: true + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -19136,7 +19316,7 @@ packages: resolution: {integrity: sha512-zJTC+NhEU0kHNnVh7OtcvMmkJmYTgFTist76FP9q07m9+WCvcaunR1sTFIOlGE9TH/5UGm6rlF86Umt9ouorAg==} engines: {node: 16.* || 18.* || >= 20} peerDependencies: - prettier: '>= 3.0.0' + prettier: github:cardstack/prettier#glimmer-style-tag-in-template-support dependencies: '@babel/core': 7.24.3(supports-color@8.1.1) '@glimmer/syntax': 0.84.3 @@ -20783,6 +20963,11 @@ packages: extend-shallow: 3.0.2 dev: true + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: true + /split@0.3.3: resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} dependencies: @@ -23062,6 +23247,19 @@ packages: yargs-parser: 20.2.9 dev: true + /yargs@17.3.1: + resolution: {integrity: sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==} + engines: {node: '>=12'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + /yargs@17.5.1: resolution: {integrity: sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==} engines: {node: '>=12'} @@ -23238,7 +23436,3 @@ packages: transitivePeerDependencies: - supports-color dev: true - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false