ekwo-os 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +873 -0
- package/dist/args.d.ts +57 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +134 -0
- package/dist/args.js.map +1 -0
- package/dist/assets/expected-objects.json +9432 -0
- package/dist/assets/migrations/20260911120000_core_companies.sql +260 -0
- package/dist/assets/migrations/20260911120100_accounts_journals.sql +211 -0
- package/dist/assets/migrations/20260911120200_contacts.sql +90 -0
- package/dist/assets/migrations/20260911120300_taxes.sql +146 -0
- package/dist/assets/migrations/20260911120400_entries.sql +338 -0
- package/dist/assets/migrations/20260911120500_documents.sql +279 -0
- package/dist/assets/migrations/20260911120600_payments_reconciliation.sql +251 -0
- package/dist/assets/migrations/20260911120700_bank.sql +178 -0
- package/dist/assets/migrations/20260911120800_analytics_attachments.sql +128 -0
- package/dist/assets/migrations/20260911120900_post_document.sql +279 -0
- package/dist/assets/migrations/20260911121000_reporting.sql +235 -0
- package/dist/assets/migrations/20260911121100_country_templates.sql +226 -0
- package/dist/assets/migrations/20260911121200_fec.sql +71 -0
- package/dist/assets/migrations/20260911130000_instance.sql +77 -0
- package/dist/assets/migrations/20260911130100_instance_members.sql +224 -0
- package/dist/assets/migrations/20260911140000_instance_admins.sql +112 -0
- package/dist/assets/migrations/20260911160000_tax_posting_templates_unique.sql +17 -0
- package/dist/assets/migrations/20260911170000_document_amount_paid.sql +82 -0
- package/dist/assets/migrations/20260911173000_post_payment.sql +122 -0
- package/dist/assets/migrations/20260911173100_sequence_counters_under_rls.sql +84 -0
- package/dist/assets/migrations/20260911183000_country_journal_defaults.sql +143 -0
- package/dist/assets/migrations/20260911193853_line_account_defaults.sql +274 -0
- package/dist/assets/migrations/20260911195054_products.sql +221 -0
- package/dist/assets/migrations/20260911210131_anon_surface.sql +85 -0
- package/dist/assets/migrations/20260912074712_country_packs.sql +318 -0
- package/dist/assets/migrations/20260912080311_report_code_and_region.sql +198 -0
- package/dist/assets/migrations/20260912081014_pack_certification_maintained.sql +31 -0
- package/dist/assets/migrations/20260912081015_pack_certification_backfill.sql +16 -0
- package/dist/assets/migrations/20260912090407_tax_report_boxes.sql +329 -0
- package/dist/assets/migrations/20260912091917_tax_on_base_value.sql +25 -0
- package/dist/assets/migrations/20260912091918_tax_engine_columns.sql +776 -0
- package/dist/assets/migrations/20260912094412_opening_and_closing.sql +759 -0
- package/dist/assets/migrations/20260912095825_charts_of_accounts.sql +326 -0
- package/dist/assets/migrations/20260912100412_financial_statements.sql +632 -0
- package/dist/assets/migrations/20260912104719_one_formula_evaluator.sql +209 -0
- package/dist/assets/migrations/20260912105720_entry_kind_appropriation.sql +26 -0
- package/dist/assets/migrations/20260912105721_appropriation_entry_kind.sql +444 -0
- package/dist/assets/migrations/20260912111751_document_rules.sql +274 -0
- package/dist/assets/migrations/20260912112132_cash_basis_vat_and_fx.sql +1189 -0
- package/dist/assets/migrations/20260913074512_modules.sql +493 -0
- package/dist/assets/migrations/20260913075903_asset_disposal_roles.sql +47 -0
- package/dist/assets/migrations/20260913083216_capabilities.sql +637 -0
- package/dist/assets/migrations/20260913083901_company_invitations.sql +262 -0
- package/dist/assets/migrations/20260913084402_user_preferences.sql +376 -0
- package/dist/assets/migrations/20260913084847_company_profile.sql +261 -0
- package/dist/assets/migrations/20260913085436_numbering_reads_the_pack.sql +235 -0
- package/dist/assets/migrations/20260913085932_api_keys.sql +303 -0
- package/dist/assets/migrations/20260913090216_fiscal_year_bounds.sql +159 -0
- package/dist/assets/migrations/20260913092527_entries_import.sql +219 -0
- package/dist/assets/migrations/20260913101536_null_safe_guards.sql +188 -0
- package/dist/assets/migrations/20260913102115_installer_is_named.sql +462 -0
- package/dist/assets/migrations/20260913102758_no_currency_default.sql +138 -0
- package/dist/assets/migrations/20260913103355_cash_basis_needs_a_box.sql +453 -0
- package/dist/assets/migrations/20260913104014_aged_balance_named_group.sql +79 -0
- package/dist/assets/migrations/20260913104232_foreign_key_indexes.sql +144 -0
- package/dist/assets/migrations/20260913105120_declared_no_reader.sql +41 -0
- package/dist/assets/migrations/20260913111407_pack_languages.sql +223 -0
- package/dist/assets/migrations/20260913112233_schema_comments_for_a_reader.sql +27 -0
- package/dist/assets/migrations/20260913114535_fec_opening_balances.sql +307 -0
- package/dist/assets/migrations/20260914103412_audit_log.sql +509 -0
- package/dist/assets/migrations/20260914111907_pack_upgrade.sql +428 -0
- package/dist/assets/migrations/20260914120500_rounding_reads_the_currency.sql +206 -0
- package/dist/assets/migrations/20260914121200_amounts_round_at_the_currency.sql +2403 -0
- package/dist/assets/migrations/20260914134325_schema_version_0_2_0.sql +36 -0
- package/dist/assets/migrations/20260914143915_accounts_in_use.sql +428 -0
- package/dist/assets/migrations/20260914144731_account_code_frozen.sql +99 -0
- package/dist/assets/migrations/20260914151207_schema_grants_its_own_rights.sql +284 -0
- package/dist/assets/migrations/20260914152840_pack_upgrade_records_its_own_line.sql +237 -0
- package/dist/assets/migrations/20260914163943_declaration_periodicity.sql +349 -0
- package/dist/assets/migrations/20260915094000_foreign_services_received.sql +37 -0
- package/dist/assets/migrations/20260915094500_mentions_read_a_foreign_service.sql +72 -0
- package/dist/assets/migrations/20260915153000_document_shares.sql +568 -0
- package/dist/assets/migrations/20260915160000_ec_sales_list.sql +181 -0
- package/dist/assets/migrations/20260915161842_pack_sources.sql +56 -0
- package/dist/assets/migrations/20260915170500_schema_version_0_3_0.sql +45 -0
- package/dist/assets/migrations/20260915174500_a_vat_category_is_a_code.sql +177 -0
- package/dist/assets/migrations/20260915180000_document_rule_references.sql +85 -0
- package/dist/assets/migrations/20260915181000_territories.sql +342 -0
- package/dist/assets/migrations/20260915181500_ec_sales_list_reads_the_territories.sql +222 -0
- package/dist/assets/migrations/20260915182000_intracom_triangular.sql +53 -0
- package/dist/assets/migrations/20260915182500_a_triangular_supply_says_reverse_charge.sql +99 -0
- package/dist/assets/migrations/20260915191200_a_document_knows_its_language.sql +633 -0
- package/dist/assets/migrations/20260915195000_a_price_that_holds_its_tax.sql +649 -0
- package/dist/assets/migrations/20260915200000_a_posting_names_its_boxes.sql +1004 -0
- package/dist/assets/migrations/20260916094500_a_company_files_more_than_one_declaration.sql +795 -0
- package/dist/assets/migrations/20260916103000_a_ledger_line_names_its_posting.sql +902 -0
- package/dist/assets/migrations/20260916123000_a_vocabulary_for_a_tax_that_is_not_a_vat.sql +144 -0
- package/dist/assets/migrations/20260916124000_a_box_can_be_a_rate_of_a_box.sql +525 -0
- package/dist/assets/migrations/20260916125000_a_tax_follows_the_territory.sql +1318 -0
- package/dist/assets/migrations/20260916126000_a_tax_point_and_its_exception.sql +1211 -0
- package/dist/assets/migrations/20260917090000_a_counterparty_that_learns.sql +561 -0
- package/dist/assets/migrations/20260917120000_what_the_money_pays.sql +545 -0
- package/dist/assets/migrations/20260917150000_a_filing_is_a_row.sql +488 -0
- package/dist/assets/migrations/20260917170000_when_it_is_due.sql +194 -0
- package/dist/assets/migrations/20260917180000_a_box_is_a_number_and_a_kind.sql +213 -0
- package/dist/assets/migrations/20260917190000_what_a_declaration_owes.sql +486 -0
- package/dist/assets/migrations/20260917200000_what_moved_after_it_went.sql +227 -0
- package/dist/assets/migrations/20260918090000_a_form_names_its_file.sql +27 -0
- package/dist/assets/migrations/20260918110000_a_deposit_and_what_came_back.sql +286 -0
- package/dist/assets/migrations/20260918113741_a_fourth_preset_is_named.sql +9 -0
- package/dist/assets/migrations/20260918113807_a_client_reads_and_hands_over.sql +96 -0
- package/dist/assets/migrations/20260918114322_two_definer_functions_check_their_caller.sql +75 -0
- package/dist/assets/migrations/20260918130000_every_company_somebody_keeps.sql +177 -0
- package/dist/assets/migrations/20260918140000_a_session_nobody_prepared.sql +40 -0
- package/dist/assets/migrations/20260918141107_a_line_keeps_the_tax_it_was_posted_with.sql +256 -0
- package/dist/assets/migrations/20260918141342_a_company_has_an_electronic_address.sql +66 -0
- package/dist/assets/migrations/20260918141605_an_invoice_reads_whole_from_the_views.sql +313 -0
- package/dist/assets/migrations/20260918141627_a_policy_asks_once.sql +132 -0
- package/dist/assets/migrations/20260918143352_a_suggestion_reads_the_words_once.sql +204 -0
- package/dist/assets/migrations/20260918143417_a_reference_of_the_caller_and_a_rehearsal.sql +115 -0
- package/dist/assets/migrations/20260918150712_a_company_leaves_with_its_books.sql +1119 -0
- package/dist/assets/migrations/20260918150931_a_statement_is_imported_once.sql +654 -0
- package/dist/assets/migrations/20260918161204_a_posted_document_does_not_move.sql +437 -0
- package/dist/assets/migrations/20260918161538_a_posted_entry_does_not_move.sql +252 -0
- package/dist/assets/migrations/20260918171946_an_entry_is_posted_by_post_entry.sql +289 -0
- package/dist/assets/migrations/20260918174312_schema_version_0_4_0.sql +53 -0
- package/dist/assets/modules/assets/module.json +13 -0
- package/dist/assets/modules/assets/supabase/migrations/20260913081447_assets.sql +1190 -0
- package/dist/assets/modules/assets/supabase/migrations/20260913104234_foreign_key_indexes.sql +47 -0
- package/dist/assets/modules/assets/supabase/migrations/20260913104500_assets_capabilities.sql +145 -0
- package/dist/assets/modules/assets/supabase/migrations/20260914122100_assets_round_at_the_currency.sql +564 -0
- package/dist/assets/modules/assets/supabase/migrations/20260914145012_assets_accounts_in_use.sql +37 -0
- package/dist/assets/modules/assets/supabase/migrations/20260914151530_assets_grants_its_own_rights.sql +63 -0
- package/dist/assets/modules/assets/supabase/migrations/20260918113912_assets_client_reads.sql +20 -0
- package/dist/assets/modules/assets/supabase/migrations/20260918150801_assets_leave_with_the_company.sql +41 -0
- package/dist/assets/modules/budgets/module.json +12 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260913083012_budgets.sql +210 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260913104233_foreign_key_indexes.sql +37 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260913104501_budgets_capabilities.sql +72 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260914122400_budgets_round_at_the_currency.sql +68 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260914145048_budgets_accounts_in_use.sql +30 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260914151812_budgets_grants_its_own_rights.sql +31 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260918113954_budgets_client_reads.sql +20 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260918150823_budgets_leave_with_the_company.sql +37 -0
- package/dist/assets/modules/schema/module.1.json +69 -0
- package/dist/assets/seed/00_currencies.sql +15 -0
- package/dist/assets/seed/00_territories.sql +280 -0
- package/dist/assets/seed/05_framework_generic.sql +110 -0
- package/dist/assets/seed/10_pack_be.sql +1284 -0
- package/dist/assets/seed/11_pack_fr.sql +1112 -0
- package/dist/assets/seed/12_pack_lu.sql +2484 -0
- package/dist/assets/seed/13_pack_ee.sql +733 -0
- package/dist/assets/seed/14_pack_gb.sql +796 -0
- package/dist/assets/seed/15_pack_us.sql +751 -0
- package/dist/assets/seed/90_demo_company.sql +368 -0
- package/dist/assets/seed/modules/assets/10_pack_be.sql +58 -0
- package/dist/assets/seed/modules/assets/11_pack_fr.sql +57 -0
- package/dist/assets/seed/modules/assets/14_pack_gb.sql +58 -0
- package/dist/assets/seed/modules/assets/15_pack_us.sql +60 -0
- package/dist/auth.d.ts +59 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +134 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin.d.ts +6 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +8 -0
- package/dist/bin.js.map +1 -0
- package/dist/books.d.ts +60 -0
- package/dist/books.d.ts.map +1 -0
- package/dist/books.js +168 -0
- package/dist/books.js.map +1 -0
- package/dist/bootstrap.d.ts +236 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +459 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bundle.d.ts +27 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +47 -0
- package/dist/bundle.js.map +1 -0
- package/dist/checklist.d.ts +35 -0
- package/dist/checklist.d.ts.map +1 -0
- package/dist/checklist.js +64 -0
- package/dist/checklist.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +318 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/company.d.ts +59 -0
- package/dist/commands/company.d.ts.map +1 -0
- package/dist/commands/company.js +287 -0
- package/dist/commands/company.js.map +1 -0
- package/dist/commands/contact.d.ts +9 -0
- package/dist/commands/contact.d.ts.map +1 -0
- package/dist/commands/contact.js +82 -0
- package/dist/commands/contact.js.map +1 -0
- package/dist/commands/demo.d.ts +19 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +72 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +68 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/document.d.ts +17 -0
- package/dist/commands/document.d.ts.map +1 -0
- package/dist/commands/document.js +217 -0
- package/dist/commands/document.js.map +1 -0
- package/dist/commands/init.d.ts +31 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +535 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +24 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +128 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/migrate.d.ts +30 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +115 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/module.d.ts +32 -0
- package/dist/commands/module.d.ts.map +1 -0
- package/dist/commands/module.js +285 -0
- package/dist/commands/module.js.map +1 -0
- package/dist/commands/pack.d.ts +22 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +481 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/payment.d.ts +15 -0
- package/dist/commands/payment.d.ts.map +1 -0
- package/dist/commands/payment.js +97 -0
- package/dist/commands/payment.js.map +1 -0
- package/dist/commands/register.d.ts +20 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +134 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +134 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/whoami.d.ts +21 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +133 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/company.d.ts +16 -0
- package/dist/company.d.ts.map +1 -0
- package/dist/company.js +26 -0
- package/dist/company.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +50 -0
- package/dist/config.js.map +1 -0
- package/dist/connection.d.ts +79 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +116 -0
- package/dist/connection.js.map +1 -0
- package/dist/context.d.ts +47 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +93 -0
- package/dist/context.js.map +1 -0
- package/dist/doctor.d.ts +45 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +448 -0
- package/dist/doctor.js.map +1 -0
- package/dist/grants.d.ts +88 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +174 -0
- package/dist/grants.js.map +1 -0
- package/dist/identity.d.ts +54 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +132 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory.d.ts +155 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +303 -0
- package/dist/inventory.js.map +1 -0
- package/dist/migrations.d.ts +76 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +226 -0
- package/dist/migrations.js.map +1 -0
- package/dist/module/read.d.ts +91 -0
- package/dist/module/read.d.ts.map +1 -0
- package/dist/module/read.js +172 -0
- package/dist/module/read.js.map +1 -0
- package/dist/output.d.ts +110 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +173 -0
- package/dist/output.js.map +1 -0
- package/dist/pack/certification.d.ts +27 -0
- package/dist/pack/certification.d.ts.map +1 -0
- package/dist/pack/certification.js +38 -0
- package/dist/pack/certification.js.map +1 -0
- package/dist/pack/compile.d.ts +75 -0
- package/dist/pack/compile.d.ts.map +1 -0
- package/dist/pack/compile.js +801 -0
- package/dist/pack/compile.js.map +1 -0
- package/dist/pack/filing.d.ts +48 -0
- package/dist/pack/filing.d.ts.map +1 -0
- package/dist/pack/filing.js +56 -0
- package/dist/pack/filing.js.map +1 -0
- package/dist/pack/read.d.ts +766 -0
- package/dist/pack/read.d.ts.map +1 -0
- package/dist/pack/read.js +2336 -0
- package/dist/pack/read.js.map +1 -0
- package/dist/pack/schema.d.ts +26 -0
- package/dist/pack/schema.d.ts.map +1 -0
- package/dist/pack/schema.js +181 -0
- package/dist/pack/schema.js.map +1 -0
- package/dist/pack/territories.d.ts +98 -0
- package/dist/pack/territories.d.ts.map +1 -0
- package/dist/pack/territories.js +0 -0
- package/dist/pack/territories.js.map +1 -0
- package/dist/pack/upgrade.d.ts +86 -0
- package/dist/pack/upgrade.d.ts.map +1 -0
- package/dist/pack/upgrade.js +76 -0
- package/dist/pack/upgrade.js.map +1 -0
- package/dist/pack/vat-codes.d.ts +273 -0
- package/dist/pack/vat-codes.d.ts.map +1 -0
- package/dist/pack/vat-codes.js +513 -0
- package/dist/pack/vat-codes.js.map +1 -0
- package/dist/profiles.d.ts +67 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +187 -0
- package/dist/profiles.js.map +1 -0
- package/dist/prompt.d.ts +40 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +120 -0
- package/dist/prompt.js.map +1 -0
- package/dist/registry.d.ts +84 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +104 -0
- package/dist/registry.js.map +1 -0
- package/dist/rest.d.ts +81 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +245 -0
- package/dist/rest.js.map +1 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +15 -0
- package/dist/schema.js.map +1 -0
- package/dist/seeds.d.ts +27 -0
- package/dist/seeds.d.ts.map +1 -0
- package/dist/seeds.js +47 -0
- package/dist/seeds.js.map +1 -0
- package/dist/session.d.ts +39 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +92 -0
- package/dist/session.js.map +1 -0
- package/dist/sql.d.ts +48 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +112 -0
- package/dist/sql.js.map +1 -0
- package/dist/status.d.ts +94 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +120 -0
- package/dist/status.js.map +1 -0
- package/dist/ui.d.ts +55 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +114 -0
- package/dist/ui.js.map +1 -0
- package/package.json +29 -0
- package/schema/output.1.json +613 -0
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ekwo doctor` — the checks worth running on a live installation.
|
|
3
|
+
*
|
|
4
|
+
* Each one is something that is true of a healthy database and that nothing
|
|
5
|
+
* in the schema can enforce on its own: a table someone added without row
|
|
6
|
+
* level security, a migration that never landed, a membership row pointing at
|
|
7
|
+
* a user who has since been deleted, a bank statement whose declared closing
|
|
8
|
+
* balance does not match its lines.
|
|
9
|
+
*
|
|
10
|
+
* The doctor reads and reports. It never repairs: the fix for a missing
|
|
11
|
+
* policy is a migration, and the fix for an orphaned membership is a decision
|
|
12
|
+
* about who should have access, not a delete the installer guesses at.
|
|
13
|
+
*/
|
|
14
|
+
import { compareSection, describeFinding } from './grants.js';
|
|
15
|
+
import { compareCatalogue, describeDifferences, installedSections, readExpectedObjects, readGrants, } from './inventory.js';
|
|
16
|
+
import { migrationGap } from './migrations.js';
|
|
17
|
+
import { scalar } from './sql.js';
|
|
18
|
+
export async function doctor(db, migrations, options = {}) {
|
|
19
|
+
const checks = [];
|
|
20
|
+
// Read once and handed to both: the inventory carries the objects a release
|
|
21
|
+
// defines and the privileges it grants on them, and reading the file twice
|
|
22
|
+
// would be two answers to one question.
|
|
23
|
+
const expected = options.expected ?? (await readExpectedObjects());
|
|
24
|
+
checks.push(await checkMigrations(db, migrations));
|
|
25
|
+
checks.push(await checkCatalogue(db, expected));
|
|
26
|
+
checks.push(await checkRowLevelSecurity(db));
|
|
27
|
+
checks.push(await checkPolicies(db));
|
|
28
|
+
checks.push(await checkGrants(db, expected));
|
|
29
|
+
checks.push(await checkOrphanMembers(db));
|
|
30
|
+
checks.push(await checkOrphanAdmins(db));
|
|
31
|
+
checks.push(await checkBankAccounts(db));
|
|
32
|
+
checks.push(await checkStatements(db));
|
|
33
|
+
checks.push(await checkPostedEntriesBalance(db));
|
|
34
|
+
checks.push(await checkAuditTrail(db));
|
|
35
|
+
return {
|
|
36
|
+
checks,
|
|
37
|
+
problems: checks.filter((c) => c.severity === 'problem').length,
|
|
38
|
+
warnings: checks.filter((c) => c.severity === 'warning').length,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function checkMigrations(db, migrations) {
|
|
42
|
+
const gap = await migrationGap(db, migrations);
|
|
43
|
+
if (gap.unknown.length > 0) {
|
|
44
|
+
return {
|
|
45
|
+
name: 'migrations',
|
|
46
|
+
severity: 'warning',
|
|
47
|
+
summary: `the database is ahead of this CLI by ${gap.unknown.length} migration(s)`,
|
|
48
|
+
details: gap.unknown.map((v) => `${v} is applied but not shipped here — upgrade the CLI`),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (gap.pending.length > 0) {
|
|
52
|
+
return {
|
|
53
|
+
name: 'migrations',
|
|
54
|
+
severity: 'problem',
|
|
55
|
+
summary: `${gap.pending.length} migration(s) pending`,
|
|
56
|
+
details: gap.pending.map((m) => m.file),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
name: 'migrations',
|
|
61
|
+
severity: 'ok',
|
|
62
|
+
summary: `${gap.applied.length} applied, none pending`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Every object this release defines, against what the database holds.
|
|
67
|
+
*
|
|
68
|
+
* The list is generated from the migrations and ships with the CLI, so the
|
|
69
|
+
* check cannot drift: see `inventory.ts` for why missing, extra and a policy
|
|
70
|
+
* are three different severities.
|
|
71
|
+
*
|
|
72
|
+
* The two schema versions are reported and never gate the comparison. A
|
|
73
|
+
* database older than the CLI is precisely when an operator runs this, and
|
|
74
|
+
* refusing to look would be refusing the case: what it says then is which
|
|
75
|
+
* version the inventory describes, which one the database reports, and what
|
|
76
|
+
* differs between them.
|
|
77
|
+
*/
|
|
78
|
+
async function checkCatalogue(db, expected) {
|
|
79
|
+
if (expected === undefined) {
|
|
80
|
+
return {
|
|
81
|
+
name: 'catalogue',
|
|
82
|
+
severity: 'warning',
|
|
83
|
+
summary: 'this build ships no inventory, so there is nothing to compare against',
|
|
84
|
+
details: ['Run `npm run build` in a checkout, or reinstall the package.'],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const comparison = await compareCatalogue(db, expected);
|
|
88
|
+
const faults = [];
|
|
89
|
+
const information = [];
|
|
90
|
+
for (const section of comparison.sections) {
|
|
91
|
+
const lines = describeDifferences(section);
|
|
92
|
+
faults.push(...lines.faults);
|
|
93
|
+
information.push(...lines.information);
|
|
94
|
+
}
|
|
95
|
+
const versions = comparison.databaseVersion === comparison.expectedVersion
|
|
96
|
+
? []
|
|
97
|
+
: [
|
|
98
|
+
`the inventory describes schema ${comparison.expectedVersion ?? 'unknown'}, ` +
|
|
99
|
+
`the database reports ${comparison.databaseVersion ?? 'none'} — compared anyway`,
|
|
100
|
+
];
|
|
101
|
+
const skipped = comparison.sections
|
|
102
|
+
.filter((s) => !s.installed)
|
|
103
|
+
.map((s) => `${s.code} is not installed here, so none of its objects are required`);
|
|
104
|
+
const data = { ...comparison };
|
|
105
|
+
if (faults.length === 0) {
|
|
106
|
+
return {
|
|
107
|
+
name: 'catalogue',
|
|
108
|
+
severity: comparison.extra > 0 ? 'warning' : 'ok',
|
|
109
|
+
summary: comparison.extra > 0
|
|
110
|
+
? `everything this release defines is there, and ${comparison.extra} object(s) it does not`
|
|
111
|
+
: 'every object this release defines is there, and nothing else',
|
|
112
|
+
details: [...versions, ...skipped, ...cap(information)],
|
|
113
|
+
data,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
name: 'catalogue',
|
|
118
|
+
severity: 'problem',
|
|
119
|
+
summary: `${comparison.missing} object(s) missing, ${comparison.changed} changed, ` +
|
|
120
|
+
`${comparison.extra} added`,
|
|
121
|
+
details: [...versions, ...skipped, ...cap(faults), ...cap(information)],
|
|
122
|
+
data,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Twenty lines of evidence, then a count. A doctor nobody reads is no doctor. */
|
|
126
|
+
function cap(lines, limit = 20) {
|
|
127
|
+
if (lines.length <= limit)
|
|
128
|
+
return lines;
|
|
129
|
+
return [...lines.slice(0, limit), `… and ${lines.length - limit} more (see --json)`];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The privileges the schema declares against the ones it holds.
|
|
133
|
+
*
|
|
134
|
+
* Row level security decides which rows a role sees; a grant decides whether
|
|
135
|
+
* the statement is allowed to run at all. Both are needed and only one of them
|
|
136
|
+
* used to be written down — until `20260914151207` the grants came from a
|
|
137
|
+
* Supabase project's default privileges, which hand `anon` everything and
|
|
138
|
+
* disappear the day `public` is recreated. This asks a live database whether
|
|
139
|
+
* it still matches the `grants` section of the inventory.
|
|
140
|
+
*
|
|
141
|
+
* Its own check rather than a category of `catalogue`, because the rule is not
|
|
142
|
+
* the same. A missing grant is a problem: it is invisible to every other check
|
|
143
|
+
* and it reaches a user as "permission denied for table companies". An extra
|
|
144
|
+
* grant to `anon` is a problem too — the anonymous role reaches the policy
|
|
145
|
+
* helpers and nothing else, and one more is a surface nobody meant to open. An
|
|
146
|
+
* extra grant to `authenticated` or `service_role` is a warning: often a local
|
|
147
|
+
* customisation, never nothing. A default privilege still standing is a
|
|
148
|
+
* warning wherever it is, because a privilege that comes from there comes from
|
|
149
|
+
* something no migration wrote and a recreated schema takes away.
|
|
150
|
+
*/
|
|
151
|
+
async function checkGrants(db, expected) {
|
|
152
|
+
if (expected === undefined) {
|
|
153
|
+
return {
|
|
154
|
+
name: 'grants',
|
|
155
|
+
severity: 'warning',
|
|
156
|
+
summary: 'this CLI ships no inventory, so the privileges cannot be compared',
|
|
157
|
+
details: ['Reinstall the package, or run `npm run inventory` in a checkout.'],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const findings = [];
|
|
161
|
+
const defaults = [];
|
|
162
|
+
const schemas = [];
|
|
163
|
+
for (const section of await installedSections(db, expected)) {
|
|
164
|
+
// An inventory generated before the grants joined it has no declaration to
|
|
165
|
+
// compare against, and silence is not an assertion.
|
|
166
|
+
if (section.grants === undefined)
|
|
167
|
+
continue;
|
|
168
|
+
schemas.push(section.schema);
|
|
169
|
+
const actual = await readGrants(db, section.schema);
|
|
170
|
+
findings.push(...compareSection(section.grants, actual));
|
|
171
|
+
for (const entry of actual.defaultPrivileges) {
|
|
172
|
+
defaults.push(`${section.schema}: a default privilege still stands (${entry}) — grants should come from a migration`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (schemas.length === 0) {
|
|
176
|
+
return {
|
|
177
|
+
name: 'grants',
|
|
178
|
+
severity: 'warning',
|
|
179
|
+
summary: 'the inventory this CLI ships declares no privileges',
|
|
180
|
+
details: ['It was generated before the schema granted its own rights. Upgrade the CLI.'],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const problems = findings.filter((f) => f.difference === 'missing' || f.role === 'anon');
|
|
184
|
+
const warnings = findings.filter((f) => !problems.includes(f));
|
|
185
|
+
const data = { problems, warnings, defaultPrivileges: defaults };
|
|
186
|
+
if (problems.length > 0) {
|
|
187
|
+
return {
|
|
188
|
+
name: 'grants',
|
|
189
|
+
severity: 'problem',
|
|
190
|
+
summary: `${problems.length} privilege(s) do not match what this release declares`,
|
|
191
|
+
details: cap([
|
|
192
|
+
...problems.map(describeFinding),
|
|
193
|
+
...warnings.map((f) => `${describeFinding(f)} (warning)`),
|
|
194
|
+
...defaults,
|
|
195
|
+
'A missing grant answers "permission denied" to the first client and to nothing else.',
|
|
196
|
+
'Run `ekwo migrate`; if that does not close it, the privileges were changed by hand.',
|
|
197
|
+
]),
|
|
198
|
+
data,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (warnings.length > 0 || defaults.length > 0) {
|
|
202
|
+
return {
|
|
203
|
+
name: 'grants',
|
|
204
|
+
severity: 'warning',
|
|
205
|
+
summary: `${warnings.length + defaults.length} privilege(s) wider than this release declares`,
|
|
206
|
+
details: cap([
|
|
207
|
+
...warnings.map(describeFinding),
|
|
208
|
+
...defaults,
|
|
209
|
+
'Row level security is then the only thing refusing a verb the schema meant to withhold.',
|
|
210
|
+
]),
|
|
211
|
+
data,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
name: 'grants',
|
|
216
|
+
severity: 'ok',
|
|
217
|
+
summary: `every privilege in ${schemas.join(', ')} is the one the schema grants itself`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
async function checkRowLevelSecurity(db) {
|
|
221
|
+
const rows = await db.query(`select c.relname as tablename
|
|
222
|
+
from pg_class c
|
|
223
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
224
|
+
where n.nspname = 'public' and c.relkind = 'r' and not c.relrowsecurity
|
|
225
|
+
order by c.relname`);
|
|
226
|
+
if (rows.length === 0) {
|
|
227
|
+
return { name: 'row level security', severity: 'ok', summary: 'enabled on every table' };
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
name: 'row level security',
|
|
231
|
+
severity: 'problem',
|
|
232
|
+
summary: `${rows.length} table(s) without row level security`,
|
|
233
|
+
details: rows.map((r) => r.tablename),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
async function checkPolicies(db) {
|
|
237
|
+
const rows = await db.query(`select c.relname as tablename
|
|
238
|
+
from pg_class c
|
|
239
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
240
|
+
where n.nspname = 'public' and c.relkind = 'r' and c.relrowsecurity
|
|
241
|
+
and not exists (select 1 from pg_policy p where p.polrelid = c.oid)
|
|
242
|
+
order by c.relname`);
|
|
243
|
+
if (rows.length === 0) {
|
|
244
|
+
return { name: 'policies', severity: 'ok', summary: 'every protected table has a policy' };
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
name: 'policies',
|
|
248
|
+
severity: 'problem',
|
|
249
|
+
summary: `${rows.length} table(s) with row level security and no policy — nobody can read them`,
|
|
250
|
+
details: rows.map((r) => r.tablename),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* `company_members` deliberately has no foreign key to `auth.users`: inviting
|
|
255
|
+
* someone into a company before they have an account is a normal thing to
|
|
256
|
+
* want. The price is that deleting a user leaves a row behind, and this is
|
|
257
|
+
* where that price is paid.
|
|
258
|
+
*/
|
|
259
|
+
async function checkOrphanMembers(db) {
|
|
260
|
+
const rows = await db.query(`select c.name as company, m.user_id
|
|
261
|
+
from company_members m
|
|
262
|
+
join companies c on c.id = m.company_id
|
|
263
|
+
where not exists (select 1 from auth.users u where u.id = m.user_id)
|
|
264
|
+
order by c.name`);
|
|
265
|
+
if (rows.length === 0) {
|
|
266
|
+
return {
|
|
267
|
+
name: 'company members',
|
|
268
|
+
severity: 'ok',
|
|
269
|
+
summary: 'every membership points at a real user',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
name: 'company members',
|
|
274
|
+
severity: 'warning',
|
|
275
|
+
summary: `${rows.length} membership row(s) whose user no longer exists in Supabase Auth`,
|
|
276
|
+
details: [
|
|
277
|
+
...rows.map((r) => `${r.company}: ${r.user_id}`),
|
|
278
|
+
'These grant nothing — auth.uid() can never match them — but they misreport who has access.',
|
|
279
|
+
'Delete them, or re-invite the person, once you know which it should be.',
|
|
280
|
+
],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
async function checkOrphanAdmins(db) {
|
|
284
|
+
const rows = await db.query(`select a.user_id from instance_admins a
|
|
285
|
+
where not exists (select 1 from auth.users u where u.id = a.user_id)`);
|
|
286
|
+
if (rows.length === 0) {
|
|
287
|
+
const count = await scalar(db, 'select count(*)::text from instance_admins');
|
|
288
|
+
return {
|
|
289
|
+
name: 'instance administrators',
|
|
290
|
+
severity: count === '0' ? 'warning' : 'ok',
|
|
291
|
+
summary: count === '0'
|
|
292
|
+
? 'no administrator: nobody can create a company here'
|
|
293
|
+
: `${count ?? '0'} administrator(s), all real users`,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
name: 'instance administrators',
|
|
298
|
+
severity: 'problem',
|
|
299
|
+
summary: `${rows.length} administrator(s) whose user no longer exists`,
|
|
300
|
+
details: [
|
|
301
|
+
...rows.map((r) => r.user_id),
|
|
302
|
+
'The foreign key to auth.users should have cascaded these away. Check that it is still there.',
|
|
303
|
+
],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* A company with no bank account has nowhere for a payment to land.
|
|
308
|
+
*
|
|
309
|
+
* `post_payment` resolves the money side from the payment's bank account, or
|
|
310
|
+
* from the default account of its journal — and the country template wires
|
|
311
|
+
* that second one, so this is never an error. It is the configuration an
|
|
312
|
+
* operator meant to finish and did not: without a bank account there is no
|
|
313
|
+
* IBAN on an invoice, no statement to import and nothing to reconcile
|
|
314
|
+
* against. So: a warning, naming the companies and the command that fixes it.
|
|
315
|
+
*/
|
|
316
|
+
async function checkBankAccounts(db) {
|
|
317
|
+
const rows = await db.query(`select c.name
|
|
318
|
+
from companies c
|
|
319
|
+
where not exists (
|
|
320
|
+
select 1 from bank_accounts b where b.company_id = c.id and b.active
|
|
321
|
+
)
|
|
322
|
+
order by c.name`);
|
|
323
|
+
if (rows.length === 0) {
|
|
324
|
+
const count = await scalar(db, 'select count(*)::text from companies');
|
|
325
|
+
return {
|
|
326
|
+
name: 'bank accounts',
|
|
327
|
+
severity: 'ok',
|
|
328
|
+
summary: count === '0'
|
|
329
|
+
? 'no company yet, so nothing to bank'
|
|
330
|
+
: 'every company has at least one bank account',
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
name: 'bank accounts',
|
|
335
|
+
severity: 'warning',
|
|
336
|
+
summary: `${rows.length} company/companies with no bank account`,
|
|
337
|
+
details: [
|
|
338
|
+
...rows.map((r) => r.name),
|
|
339
|
+
'Payments still book — the bank journal carries a default account — but there is no IBAN to',
|
|
340
|
+
'put on an invoice and no statement to reconcile against.',
|
|
341
|
+
'Add one with `ekwo init --iban …` on the same project, or the create_bank_account tool of the MCP server.',
|
|
342
|
+
],
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async function checkStatements(db) {
|
|
346
|
+
const rows = await db.query(`select c.name as company, s.statement_date::text,
|
|
347
|
+
s.balance_end_declared::text as declared,
|
|
348
|
+
s.balance_end_computed::text as computed
|
|
349
|
+
from bank_statements s
|
|
350
|
+
join companies c on c.id = s.company_id
|
|
351
|
+
where not s.is_consistent
|
|
352
|
+
order by s.statement_date`);
|
|
353
|
+
if (rows.length === 0) {
|
|
354
|
+
return {
|
|
355
|
+
name: 'bank statements',
|
|
356
|
+
severity: 'ok',
|
|
357
|
+
summary: 'every statement ties to its lines',
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
name: 'bank statements',
|
|
362
|
+
severity: 'warning',
|
|
363
|
+
summary: `${rows.length} statement(s) whose closing balance does not match their lines`,
|
|
364
|
+
details: rows.map((r) => `${r.company} ${r.statement_date}: declared ${r.declared}, lines give ${r.computed}`),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
async function checkPostedEntriesBalance(db) {
|
|
368
|
+
const rows = await db.query(`select e.number, e.entry_date::text
|
|
369
|
+
from entries e
|
|
370
|
+
where e.state = 'posted' and not e.is_balanced
|
|
371
|
+
order by e.entry_date`);
|
|
372
|
+
if (rows.length === 0) {
|
|
373
|
+
return { name: 'posted entries', severity: 'ok', summary: 'every posted entry balances' };
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
name: 'posted entries',
|
|
377
|
+
severity: 'problem',
|
|
378
|
+
summary: `${rows.length} posted entry/entries do not balance`,
|
|
379
|
+
details: rows.map((r) => `${r.number ?? '(no number)'} on ${r.entry_date}`),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* The audit trail still being what it claims to be.
|
|
384
|
+
*
|
|
385
|
+
* Everything here is read from the catalogue rather than from a list kept by
|
|
386
|
+
* hand: the trail is append-only, so the guard trigger has to be on the table
|
|
387
|
+
* and no policy may let a client write, update or delete a row. A migration
|
|
388
|
+
* added later that put an insert policy on `audit_log` "so the application can
|
|
389
|
+
* log too" would break the one property the table exists for, and nothing else
|
|
390
|
+
* would notice.
|
|
391
|
+
*/
|
|
392
|
+
async function checkAuditTrail(db) {
|
|
393
|
+
const installed = (await scalar(db, `select exists (
|
|
394
|
+
select 1 from information_schema.tables
|
|
395
|
+
where table_schema = 'public' and table_name = 'audit_log'
|
|
396
|
+
)`)) === true;
|
|
397
|
+
if (!installed) {
|
|
398
|
+
return {
|
|
399
|
+
name: 'audit trail',
|
|
400
|
+
severity: 'problem',
|
|
401
|
+
summary: 'audit_log is not there',
|
|
402
|
+
details: ['The migration that creates it has not been applied. Run `ekwo migrate`.'],
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const faults = [];
|
|
406
|
+
const guarded = await db.query(`select t.tgname from pg_trigger t
|
|
407
|
+
join pg_class c on c.oid = t.tgrelid
|
|
408
|
+
where c.relname = 'audit_log' and not t.tgisinternal`);
|
|
409
|
+
if (guarded.length === 0) {
|
|
410
|
+
faults.push('no trigger on audit_log: nothing refuses an update or a delete on it any more');
|
|
411
|
+
}
|
|
412
|
+
const rls = (await scalar(db, `select relrowsecurity from pg_class where relname = 'audit_log' and relnamespace = 'public'::regnamespace`)) === true;
|
|
413
|
+
if (!rls)
|
|
414
|
+
faults.push('row level security is off on audit_log: every signed-in user reads every company');
|
|
415
|
+
const writable = await db.query(`select p.polname, case p.polcmd
|
|
416
|
+
when 'a' then 'insert' when 'w' then 'update'
|
|
417
|
+
when 'd' then 'delete' when '*' then 'all' else 'select' end as cmd
|
|
418
|
+
from pg_policy p
|
|
419
|
+
join pg_class c on c.oid = p.polrelid
|
|
420
|
+
where c.relname = 'audit_log' and p.polcmd <> 'r'`);
|
|
421
|
+
for (const policy of writable) {
|
|
422
|
+
faults.push(`policy ${policy.polname} allows ${policy.cmd} on audit_log, which is append-only`);
|
|
423
|
+
}
|
|
424
|
+
const purgers = await db.query(`select a.grantee from (
|
|
425
|
+
select (aclexplode(p.proacl)).grantee as grantee
|
|
426
|
+
from pg_proc p
|
|
427
|
+
where p.proname = 'purge_audit_log'
|
|
428
|
+
) a
|
|
429
|
+
where a.grantee <> 0 and pg_get_userbyid(a.grantee) not in ('service_role', current_user)`);
|
|
430
|
+
for (const grant of purgers) {
|
|
431
|
+
faults.push(`purge_audit_log is executable by a role other than service_role (${grant.grantee})`);
|
|
432
|
+
}
|
|
433
|
+
if (faults.length === 0) {
|
|
434
|
+
const rows = await scalar(db, 'select count(*)::text from audit_log');
|
|
435
|
+
return {
|
|
436
|
+
name: 'audit trail',
|
|
437
|
+
severity: 'ok',
|
|
438
|
+
summary: `append-only, ${rows ?? '0'} row(s) recorded`,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
name: 'audit trail',
|
|
443
|
+
severity: 'problem',
|
|
444
|
+
summary: `${faults.length} thing(s) the audit trail no longer guarantees`,
|
|
445
|
+
details: faults,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/grants.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The privileges the schema declares, and how a live database is compared to
|
|
3
|
+
* them.
|
|
4
|
+
*
|
|
5
|
+
* Since `20260914151207` every table, view and function of Ekwo names the
|
|
6
|
+
* roles that may reach it, in the migration that creates it. Before that the
|
|
7
|
+
* privileges came from a Supabase project's default privileges on `public`,
|
|
8
|
+
* which gave `anon` — the role behind the publishable key — `all` on every
|
|
9
|
+
* table of the ledger, and which vanish the day somebody recreates the schema.
|
|
10
|
+
*
|
|
11
|
+
* The declaration travels in `packages/cli/assets/expected-objects.json`,
|
|
12
|
+
* under `grants` in each schema's section: the same inventory that carries the
|
|
13
|
+
* tables, the policies and the triggers a release defines, generated by
|
|
14
|
+
* `scripts/generate-expected-objects.mjs` from a freshly migrated database and
|
|
15
|
+
* committed. One file, because the privileges of an object and the object are
|
|
16
|
+
* the same release. This module reads the same catalogue out of a live
|
|
17
|
+
* installation and says where the two differ:
|
|
18
|
+
*
|
|
19
|
+
* - a **missing** grant is a problem. It is the failure PostgREST reports as
|
|
20
|
+
* "permission denied for table companies" to the first user, and it passes
|
|
21
|
+
* every other check there is.
|
|
22
|
+
* - an **extra** grant to `anon` is a problem. The anonymous role reaches
|
|
23
|
+
* the policy helpers and nothing else; anything more is a surface somebody
|
|
24
|
+
* opened by hand or a default privilege that was never taken away.
|
|
25
|
+
* - an **extra** grant to `authenticated` or `service_role` is a warning. It
|
|
26
|
+
* is usually a local customisation and it is never nothing: row level
|
|
27
|
+
* security is then the only thing refusing a verb the schema meant to
|
|
28
|
+
* withhold.
|
|
29
|
+
*
|
|
30
|
+
* The SQL that describes a schema lives here rather than in the generator, so
|
|
31
|
+
* that the file and the check can never read the catalogue two different ways.
|
|
32
|
+
* `inventory.ts` calls `describeGrants` to fill the section, and `doctor.ts`
|
|
33
|
+
* calls it again on the live database and hands both to `compareSection`.
|
|
34
|
+
*/
|
|
35
|
+
/** The three roles a Supabase project exposes to a client. */
|
|
36
|
+
export declare const GRANT_ROLES: readonly ["anon", "authenticated", "service_role"];
|
|
37
|
+
export type GrantRole = (typeof GRANT_ROLES)[number];
|
|
38
|
+
/** Privileges held on one object, by role. Lower case, sorted. */
|
|
39
|
+
export type HeldBy = Record<GrantRole, string[]>;
|
|
40
|
+
export interface GrantedObject extends HeldBy {
|
|
41
|
+
name: string;
|
|
42
|
+
/** Functions only: the identity arguments that tell an overload apart. */
|
|
43
|
+
arguments?: string;
|
|
44
|
+
/** Functions only, and true when the function returns `trigger`. */
|
|
45
|
+
trigger?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface GrantsSection extends Record<string, unknown> {
|
|
48
|
+
schema: string;
|
|
49
|
+
/** `usage` on the schema itself, which is what makes the rest reachable. */
|
|
50
|
+
usage: HeldBy;
|
|
51
|
+
tables: GrantedObject[];
|
|
52
|
+
views: GrantedObject[];
|
|
53
|
+
sequences: GrantedObject[];
|
|
54
|
+
functions: GrantedObject[];
|
|
55
|
+
/**
|
|
56
|
+
* Default privileges still standing on the schema, as
|
|
57
|
+
* `owner:objtype:role:privilege`. Empty is the point: a privilege that comes
|
|
58
|
+
* from here comes from something no migration wrote.
|
|
59
|
+
*/
|
|
60
|
+
defaultPrivileges: string[];
|
|
61
|
+
}
|
|
62
|
+
/** Rows from one parameterised query. Both PGlite and the CLI client fit. */
|
|
63
|
+
export type QueryRows = <T>(sql: string, params: unknown[]) => Promise<T[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Every privilege one schema hands to the three roles, object by object.
|
|
66
|
+
*
|
|
67
|
+
* `aclexplode` turns an `aclitem[]` into one row per grantee and privilege,
|
|
68
|
+
* which is the only shape worth comparing: the text form of an ACL depends on
|
|
69
|
+
* the owner's name and on the order the privileges happen to sit in. The
|
|
70
|
+
* owner's own grants are dropped — they are not a declaration, they are what
|
|
71
|
+
* owning a table means.
|
|
72
|
+
*/
|
|
73
|
+
export declare function describeGrants(rows: QueryRows, schema: string): Promise<GrantsSection>;
|
|
74
|
+
export type Difference = 'missing' | 'extra';
|
|
75
|
+
export interface GrantFinding {
|
|
76
|
+
schema: string;
|
|
77
|
+
/** `table entries`, `function post_entry(p_entry_id uuid)`. */
|
|
78
|
+
object: string;
|
|
79
|
+
role: GrantRole;
|
|
80
|
+
difference: Difference;
|
|
81
|
+
/** The privileges that are missing, or the ones that are too many. */
|
|
82
|
+
privileges: string[];
|
|
83
|
+
}
|
|
84
|
+
/** Every difference between what a schema declares and what it holds. */
|
|
85
|
+
export declare function compareSection(expected: GrantsSection, actual: GrantsSection): GrantFinding[];
|
|
86
|
+
/** A finding as one line of the doctor's output. */
|
|
87
|
+
export declare function describeFinding(finding: GrantFinding): string;
|
|
88
|
+
//# sourceMappingURL=grants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,8DAA8D;AAC9D,eAAO,MAAM,WAAW,oDAAqD,CAAC;AAC9E,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,kEAAkE;AAClE,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAc,SAAQ,MAAM;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAc,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AAI5E;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiG5F;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,sEAAsE;IACtE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AA2BD,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,GAAG,YAAY,EAAE,CAgC7F;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAG7D"}
|