@ekwo-ai/core 0.4.0

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.
Files changed (66) hide show
  1. package/README.md +76 -0
  2. package/dist/books/backend.d.ts +118 -0
  3. package/dist/books/backend.d.ts.map +1 -0
  4. package/dist/books/backend.js +69 -0
  5. package/dist/books/backend.js.map +1 -0
  6. package/dist/books/columns.d.ts +68 -0
  7. package/dist/books/columns.d.ts.map +1 -0
  8. package/dist/books/columns.js +466 -0
  9. package/dist/books/columns.js.map +1 -0
  10. package/dist/books/contacts.d.ts +43 -0
  11. package/dist/books/contacts.d.ts.map +1 -0
  12. package/dist/books/contacts.js +67 -0
  13. package/dist/books/contacts.js.map +1 -0
  14. package/dist/books/documents.d.ts +87 -0
  15. package/dist/books/documents.d.ts.map +1 -0
  16. package/dist/books/documents.js +413 -0
  17. package/dist/books/documents.js.map +1 -0
  18. package/dist/books/format.d.ts +21 -0
  19. package/dist/books/format.d.ts.map +1 -0
  20. package/dist/books/format.js +49 -0
  21. package/dist/books/format.js.map +1 -0
  22. package/dist/books/index.d.ts +18 -0
  23. package/dist/books/index.d.ts.map +1 -0
  24. package/dist/books/index.js +18 -0
  25. package/dist/books/index.js.map +1 -0
  26. package/dist/books/payments.d.ts +57 -0
  27. package/dist/books/payments.d.ts.map +1 -0
  28. package/dist/books/payments.js +255 -0
  29. package/dist/books/payments.js.map +1 -0
  30. package/dist/books/rounding.d.ts +25 -0
  31. package/dist/books/rounding.d.ts.map +1 -0
  32. package/dist/books/rounding.js +35 -0
  33. package/dist/books/rounding.js.map +1 -0
  34. package/dist/books/shared.d.ts +33 -0
  35. package/dist/books/shared.d.ts.map +1 -0
  36. package/dist/books/shared.js +98 -0
  37. package/dist/books/shared.js.map +1 -0
  38. package/dist/client.d.ts +73 -0
  39. package/dist/client.d.ts.map +1 -0
  40. package/dist/client.js +138 -0
  41. package/dist/client.js.map +1 -0
  42. package/dist/fec.d.ts +14 -0
  43. package/dist/fec.d.ts.map +1 -0
  44. package/dist/fec.js +13 -0
  45. package/dist/fec.js.map +1 -0
  46. package/dist/identity.d.ts +46 -0
  47. package/dist/identity.d.ts.map +1 -0
  48. package/dist/identity.js +62 -0
  49. package/dist/identity.js.map +1 -0
  50. package/dist/index.d.ts +12 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +12 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/refusal.d.ts +27 -0
  55. package/dist/refusal.d.ts.map +1 -0
  56. package/dist/refusal.js +38 -0
  57. package/dist/refusal.js.map +1 -0
  58. package/dist/schema.d.ts +23 -0
  59. package/dist/schema.d.ts.map +1 -0
  60. package/dist/schema.js +42 -0
  61. package/dist/schema.js.map +1 -0
  62. package/dist/types.d.ts +466 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +170 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +34 -0
package/dist/client.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * A thin client over the Ekwo schema.
3
+ *
4
+ * It is deliberately small: Supabase already exposes every table as REST with
5
+ * an OpenAPI description, so there is nothing to wrap there. What is worth
6
+ * wrapping is the handful of functions that carry the accounting rules, and
7
+ * the FEC export.
8
+ *
9
+ * `@supabase/supabase-js` is an optional peer dependency; only the two
10
+ * methods used here are required, so any structurally compatible client
11
+ * works and this package keeps zero runtime dependencies.
12
+ */
13
+ import { fromQueryRow, generateFec } from '@ekwo-ai/fec';
14
+ export class EkwoError extends Error {
15
+ name = 'EkwoError';
16
+ code;
17
+ constructor(message, code) {
18
+ super(message);
19
+ this.code = code;
20
+ }
21
+ }
22
+ function unwrap(result, call) {
23
+ if (result.error !== null) {
24
+ throw new EkwoError(`${call} failed: ${result.error.message}`, result.error.code);
25
+ }
26
+ if (result.data === null) {
27
+ throw new EkwoError(`${call} returned nothing`);
28
+ }
29
+ return result.data;
30
+ }
31
+ export class EkwoClient {
32
+ db;
33
+ constructor(db) {
34
+ this.db = db;
35
+ }
36
+ /** Books a document and returns the entry it produced. */
37
+ async postDocument(documentId) {
38
+ const result = await this.db.rpc('post_document', { p_document_id: documentId });
39
+ return unwrap(result, 'post_document');
40
+ }
41
+ /** Numbers and posts an entry that was built by hand. */
42
+ async postEntry(entryId) {
43
+ const result = await this.db.rpc('post_entry', { p_entry_id: entryId });
44
+ return unwrap(result, 'post_entry');
45
+ }
46
+ /**
47
+ * Matches a debit line against a credit line. With no amount, the smaller
48
+ * of the two open amounts is used.
49
+ */
50
+ async reconcile(debitLineId, creditLineId, amount) {
51
+ const result = await this.db.rpc('reconcile', {
52
+ p_line_a: debitLineId,
53
+ p_line_b: creditLineId,
54
+ p_amount: amount ?? null,
55
+ });
56
+ return unwrap(result, 'reconcile');
57
+ }
58
+ async trialBalance(range) {
59
+ const result = await this.db.rpc('trial_balance', {
60
+ p_company_id: range.companyId,
61
+ p_from: range.from,
62
+ p_to: range.to,
63
+ });
64
+ return unwrap(result, 'trial_balance');
65
+ }
66
+ async vatReturn(range) {
67
+ const result = await this.db.rpc('vat_return', {
68
+ p_company_id: range.companyId,
69
+ p_from: range.from,
70
+ p_to: range.to,
71
+ });
72
+ return unwrap(result, 'vat_return');
73
+ }
74
+ async agedBalance(companyId, at, group = 'receivable') {
75
+ const result = await this.db.rpc('aged_balance', {
76
+ p_company_id: companyId,
77
+ p_at: at,
78
+ p_group: group,
79
+ });
80
+ return unwrap(result, 'aged_balance');
81
+ }
82
+ /** The French FEC for a period, ready to be written to disk. */
83
+ async generateFec(range, options) {
84
+ const result = await this.db.rpc('fec_lines', {
85
+ p_company_id: range.companyId,
86
+ p_from: range.from,
87
+ p_to: range.to,
88
+ });
89
+ return generateFec(unwrap(result, 'fec_lines').map(fromQueryRow), options);
90
+ }
91
+ /**
92
+ * Records the installation. Called once, by the installer; raises if this
93
+ * database has already been set up.
94
+ */
95
+ async initInstance(organizationName, country, edition = 'community') {
96
+ const result = await this.db.rpc('init_instance', {
97
+ p_organization_name: organizationName,
98
+ p_country: country,
99
+ p_edition: edition,
100
+ });
101
+ return unwrap(result, 'init_instance');
102
+ }
103
+ /**
104
+ * Makes a user an instance administrator. The first claim is open; after
105
+ * that only an administrator may appoint another.
106
+ */
107
+ async claimInstanceAdmin(userId) {
108
+ const result = await this.db.rpc('claim_instance_admin', {
109
+ p_user_id: userId ?? null,
110
+ });
111
+ return unwrap(result, 'claim_instance_admin');
112
+ }
113
+ /**
114
+ * Opt-in: records an address so Ekwo can reach the operator. Never
115
+ * required, and `unregisterInstance()` undoes it.
116
+ */
117
+ async registerInstance(contactEmail) {
118
+ const result = await this.db.rpc('register_instance', {
119
+ p_contact_email: contactEmail,
120
+ });
121
+ return unwrap(result, 'register_instance');
122
+ }
123
+ async unregisterInstance() {
124
+ const result = await this.db.rpc('unregister_instance', {});
125
+ return unwrap(result, 'unregister_instance');
126
+ }
127
+ /** Installs a country chart of accounts, journals and taxes into a company. */
128
+ async installCountryTemplate(companyId, country) {
129
+ const result = await this.db.rpc('install_country_template', {
130
+ p_company_id: companyId,
131
+ p_country: country,
132
+ });
133
+ if (result.error !== null) {
134
+ throw new EkwoError(`install_country_template failed: ${result.error.message}`);
135
+ }
136
+ }
137
+ }
138
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/fec.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @deprecated The FEC left this package. It is a file format, and a file
3
+ * format is MIT: install [`@ekwo-ai/fec`](https://www.npmjs.com/package/@ekwo-ai/fec)
4
+ * and import from there.
5
+ *
6
+ * This module re-exports it unchanged so that `@ekwo-ai/core` and
7
+ * `@ekwo-ai/core/fec` keep working for one version. It goes away in the next.
8
+ *
9
+ * `IsoDate` and `Decimal` are deliberately *not* re-exported here: they are
10
+ * types of the schema and this package already exports its own.
11
+ */
12
+ export { checkFec, FEC_COLUMNS, FecError, fecFileName, formatFecAmount, formatFecDate, fromQueryRow, generateFec, } from '@ekwo-ai/fec';
13
+ export type { FecColumn, FecLine, FecOptions, FecQueryRow, FecViolation, } from '@ekwo-ai/fec';
14
+ //# sourceMappingURL=fec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fec.d.ts","sourceRoot":"","sources":["../src/fec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EACL,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,WAAW,EACX,eAAe,EACf,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,WAAW,EACX,YAAY,GACb,MAAM,cAAc,CAAC"}
package/dist/fec.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @deprecated The FEC left this package. It is a file format, and a file
3
+ * format is MIT: install [`@ekwo-ai/fec`](https://www.npmjs.com/package/@ekwo-ai/fec)
4
+ * and import from there.
5
+ *
6
+ * This module re-exports it unchanged so that `@ekwo-ai/core` and
7
+ * `@ekwo-ai/core/fec` keep working for one version. It goes away in the next.
8
+ *
9
+ * `IsoDate` and `Decimal` are deliberately *not* re-exported here: they are
10
+ * types of the schema and this package already exports its own.
11
+ */
12
+ export { checkFec, FEC_COLUMNS, FecError, fecFileName, formatFecAmount, formatFecDate, fromQueryRow, generateFec, } from '@ekwo-ai/fec';
13
+ //# sourceMappingURL=fec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fec.js","sourceRoot":"","sources":["../src/fec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EACL,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,WAAW,EACX,eAAe,EACf,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,cAAc,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Who a surface acts as — and the one key none of them accepts.
3
+ *
4
+ * The MCP server and the command line both sign in as a person and let row
5
+ * level security decide from there. Both are handed keys by somebody pasting
6
+ * from a dashboard, and both have to refuse the same paste: a `service_role`
7
+ * key. It would work — that is the problem. Every policy in the schema would
8
+ * be bypassed, and a tool that can read every company of an installation is
9
+ * not the thing anybody asked for.
10
+ *
11
+ * This lived in the MCP server. The command line needs the same test and the
12
+ * same sentence, so it moved here and both read it; like `refusal.ts`, this
13
+ * file imports nothing.
14
+ */
15
+ /** The variables a surface reads its user from. One spelling, for every surface. */
16
+ export declare const IDENTITY_ENV: {
17
+ readonly supabaseUrl: "SUPABASE_URL";
18
+ readonly anonKey: "SUPABASE_ANON_KEY";
19
+ readonly email: "EKWO_EMAIL";
20
+ readonly password: "EKWO_PASSWORD";
21
+ readonly accessToken: "EKWO_ACCESS_TOKEN";
22
+ };
23
+ /**
24
+ * True when a key is, or claims to be, a `service_role` key.
25
+ *
26
+ * Both shapes Supabase has issued: the signed JWT whose payload carries
27
+ * `"role": "service_role"`, and the newer `sb_secret_…`. Neither is a mistake
28
+ * we should let an operator make by pasting the wrong line of the dashboard.
29
+ */
30
+ export declare function isServiceRoleKey(key: string): boolean;
31
+ /** Where a key was put, which decides what the refusal has to explain. */
32
+ export type KeySlot = 'apikey' | 'authorization';
33
+ /**
34
+ * The refusal, word for word, for whichever surface met the key.
35
+ *
36
+ * There are two doors and not one. The `apikey` slot is the obvious paste. The
37
+ * `Authorization` header is the quiet one: PostgREST takes the role from that
38
+ * header and not from `apikey`, so a service_role token put there overrides a
39
+ * perfectly good anon key sitting next to it.
40
+ *
41
+ * `where` is what the operator typed — a variable, a flag — and `surface` is
42
+ * who is speaking: `this server`, `this command line`. `signIn` is how that
43
+ * surface gets a real session instead.
44
+ */
45
+ export declare function serviceRoleRefusal(where: string, slot: KeySlot, surface: string, signIn: string): string;
46
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,oFAAoF;AACpF,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAYrD;AAED,0EAA0E;AAC1E,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,CAKR"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Who a surface acts as — and the one key none of them accepts.
3
+ *
4
+ * The MCP server and the command line both sign in as a person and let row
5
+ * level security decide from there. Both are handed keys by somebody pasting
6
+ * from a dashboard, and both have to refuse the same paste: a `service_role`
7
+ * key. It would work — that is the problem. Every policy in the schema would
8
+ * be bypassed, and a tool that can read every company of an installation is
9
+ * not the thing anybody asked for.
10
+ *
11
+ * This lived in the MCP server. The command line needs the same test and the
12
+ * same sentence, so it moved here and both read it; like `refusal.ts`, this
13
+ * file imports nothing.
14
+ */
15
+ /** The variables a surface reads its user from. One spelling, for every surface. */
16
+ export const IDENTITY_ENV = {
17
+ supabaseUrl: 'SUPABASE_URL',
18
+ anonKey: 'SUPABASE_ANON_KEY',
19
+ email: 'EKWO_EMAIL',
20
+ password: 'EKWO_PASSWORD',
21
+ accessToken: 'EKWO_ACCESS_TOKEN',
22
+ };
23
+ /**
24
+ * True when a key is, or claims to be, a `service_role` key.
25
+ *
26
+ * Both shapes Supabase has issued: the signed JWT whose payload carries
27
+ * `"role": "service_role"`, and the newer `sb_secret_…`. Neither is a mistake
28
+ * we should let an operator make by pasting the wrong line of the dashboard.
29
+ */
30
+ export function isServiceRoleKey(key) {
31
+ if (key.startsWith('sb_secret_'))
32
+ return true;
33
+ const parts = key.split('.');
34
+ if (parts.length !== 3 || parts[1] === undefined)
35
+ return false;
36
+ try {
37
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
38
+ return payload.role === 'service_role';
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ /**
45
+ * The refusal, word for word, for whichever surface met the key.
46
+ *
47
+ * There are two doors and not one. The `apikey` slot is the obvious paste. The
48
+ * `Authorization` header is the quiet one: PostgREST takes the role from that
49
+ * header and not from `apikey`, so a service_role token put there overrides a
50
+ * perfectly good anon key sitting next to it.
51
+ *
52
+ * `where` is what the operator typed — a variable, a flag — and `surface` is
53
+ * who is speaking: `this server`, `this command line`. `signIn` is how that
54
+ * surface gets a real session instead.
55
+ */
56
+ export function serviceRoleRefusal(where, slot, surface, signIn) {
57
+ if (slot === 'apikey') {
58
+ return `service_role_refused: ${where} holds a service_role key. That key bypasses every row level security policy, so ${surface} would answer for companies its user was never invited to. Use the anon (publishable) key and sign in as a user.`;
59
+ }
60
+ return `service_role_refused: ${where} holds a service_role key. It travels in the Authorization header, which is where PostgREST reads the role from, so it would bypass every row level security policy and ${surface} would answer for companies its user was never invited to. Use a session token for a real user — ${signIn}.`;
61
+ }
62
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,oFAAoF;AACpF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,WAAW,EAAE,cAAc;IAC3B,OAAO,EAAE,mBAAmB;IAC5B,KAAK,EAAE,YAAY;IACnB,QAAQ,EAAE,eAAe;IACzB,WAAW,EAAE,mBAAmB;CACxB,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAE7E,CAAC;QACF,OAAO,OAAO,CAAC,IAAI,KAAK,cAAc,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAKD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,IAAa,EACb,OAAe,EACf,MAAc;IAEd,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO,yBAAyB,KAAK,oFAAoF,OAAO,kHAAkH,CAAC;IACrP,CAAC;IACD,OAAO,yBAAyB,KAAK,2KAA2K,OAAO,oGAAoG,MAAM,GAAG,CAAC;AACvU,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Ekwo OS — TypeScript core.
3
+ *
4
+ * @see https://github.com/Ekwo-ai/ekwo-os
5
+ */
6
+ export * from './types.js';
7
+ export * from './client.js';
8
+ export { SCHEMA_MIN, compareSchemaVersions, schemaIsAtLeast } from './schema.js';
9
+ export { isRefusalState, socleCode } from './refusal.js';
10
+ export { IDENTITY_ENV, isServiceRoleKey, serviceRoleRefusal, type KeySlot } from './identity.js';
11
+ export * from './books/index.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AACjG,cAAc,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Ekwo OS — TypeScript core.
3
+ *
4
+ * @see https://github.com/Ekwo-ai/ekwo-os
5
+ */
6
+ export * from './types.js';
7
+ export * from './client.js';
8
+ export { SCHEMA_MIN, compareSchemaVersions, schemaIsAtLeast } from './schema.js';
9
+ export { isRefusalState, socleCode } from './refusal.js';
10
+ export { IDENTITY_ENV, isServiceRoleKey, serviceRoleRefusal } from './identity.js';
11
+ export * from './books/index.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAgB,MAAM,eAAe,CAAC;AACjG,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Reading a refusal of the database — once, for every surface.
3
+ *
4
+ * The accounting rules live in the schema and they raise with a prefixed
5
+ * name: `period_locked: 2026-03-31 is …`, `tax_territory_mismatch: …`. The
6
+ * MCP server hands that name to a model and the command line turns it into an
7
+ * exit code, so both have to agree on what a name is and on what counts as
8
+ * the books saying no. That agreement is this file. It imports nothing, on
9
+ * purpose: the CLI is handed a database password, and what it loads from here
10
+ * has to be readable in one sitting.
11
+ */
12
+ /** The identifier of a raise like `period_locked: 2026-03-31 is …`, or undefined. */
13
+ export declare function socleCode(message: string): string | undefined;
14
+ /**
15
+ * Whether an SQLSTATE is the database declining, as opposed to failing.
16
+ *
17
+ * The schema refuses in a small number of ways and every one of them is a
18
+ * decision: a `raise exception` of its own (`P0001`, and `P0002` where a
19
+ * function names something that is not there or not visible), a capability
20
+ * the caller does not hold or a row level security policy (`42501`), a lock
21
+ * on a period or a closed year (`55006`), and a constraint (class `23`).
22
+ * Everything else — a connection that dropped (`08…`), a relation that does
23
+ * not exist (`42P01`), a syntax error — is the software failing, and a caller
24
+ * that retries or reports a bug is right to.
25
+ */
26
+ export declare function isRefusalState(sqlstate: string | undefined): boolean;
27
+ //# sourceMappingURL=refusal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refusal.d.ts","sourceRoot":"","sources":["../src/refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG7D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAKpE"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Reading a refusal of the database — once, for every surface.
3
+ *
4
+ * The accounting rules live in the schema and they raise with a prefixed
5
+ * name: `period_locked: 2026-03-31 is …`, `tax_territory_mismatch: …`. The
6
+ * MCP server hands that name to a model and the command line turns it into an
7
+ * exit code, so both have to agree on what a name is and on what counts as
8
+ * the books saying no. That agreement is this file. It imports nothing, on
9
+ * purpose: the CLI is handed a database password, and what it loads from here
10
+ * has to be readable in one sitting.
11
+ */
12
+ /** The identifier of a raise like `period_locked: 2026-03-31 is …`, or undefined. */
13
+ export function socleCode(message) {
14
+ const match = /^([a-z][a-z0-9_]{3,}):/.exec(message.trim());
15
+ return match?.[1];
16
+ }
17
+ /**
18
+ * Whether an SQLSTATE is the database declining, as opposed to failing.
19
+ *
20
+ * The schema refuses in a small number of ways and every one of them is a
21
+ * decision: a `raise exception` of its own (`P0001`, and `P0002` where a
22
+ * function names something that is not there or not visible), a capability
23
+ * the caller does not hold or a row level security policy (`42501`), a lock
24
+ * on a period or a closed year (`55006`), and a constraint (class `23`).
25
+ * Everything else — a connection that dropped (`08…`), a relation that does
26
+ * not exist (`42P01`), a syntax error — is the software failing, and a caller
27
+ * that retries or reports a bug is right to.
28
+ */
29
+ export function isRefusalState(sqlstate) {
30
+ if (sqlstate === undefined)
31
+ return false;
32
+ if (sqlstate === 'P0001' || sqlstate === 'P0002')
33
+ return true;
34
+ if (sqlstate === '42501' || sqlstate === '55006')
35
+ return true;
36
+ return sqlstate.startsWith('23');
37
+ }
38
+ //# sourceMappingURL=refusal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refusal.js","sourceRoot":"","sources":["../src/refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,qFAAqF;AACrF,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,QAA4B;IACzD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC9D,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The schema version this package is written against.
3
+ *
4
+ * A country pack declares `schema_min` in its manifest, and for the same
5
+ * reason a published npm package has to: the rows it reads and the functions
6
+ * it calls are a contract with a version of the database, and installing a
7
+ * newer client against an older database is the one upgrade order nobody
8
+ * plans for. The number is declared in `package.json` under `ekwo.schemaMin`
9
+ * and repeated here as a constant, so a bundler that never reads a manifest
10
+ * still carries it; a test keeps the two equal.
11
+ *
12
+ * Forward only. A release of Ekwo is a tag carrying migrations, packs and
13
+ * packages together; there is no "down" migration and there never will be,
14
+ * because undoing a schema change on a database with a year of entries in it
15
+ * is not a script, it is a restore.
16
+ */
17
+ /** The oldest schema `@ekwo-ai/core` reads. */
18
+ export declare const SCHEMA_MIN = "0.4.0";
19
+ /** `-1`, `0` or `1`, comparing two `major.minor.patch` versions numerically. */
20
+ export declare function compareSchemaVersions(left: string, right: string): number;
21
+ /** Whether a database at `installed` is new enough for a package needing `minimum`. */
22
+ export declare function schemaIsAtLeast(installed: string, minimum: string): boolean;
23
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,+CAA+C;AAC/C,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBzE;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3E"}
package/dist/schema.js ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The schema version this package is written against.
3
+ *
4
+ * A country pack declares `schema_min` in its manifest, and for the same
5
+ * reason a published npm package has to: the rows it reads and the functions
6
+ * it calls are a contract with a version of the database, and installing a
7
+ * newer client against an older database is the one upgrade order nobody
8
+ * plans for. The number is declared in `package.json` under `ekwo.schemaMin`
9
+ * and repeated here as a constant, so a bundler that never reads a manifest
10
+ * still carries it; a test keeps the two equal.
11
+ *
12
+ * Forward only. A release of Ekwo is a tag carrying migrations, packs and
13
+ * packages together; there is no "down" migration and there never will be,
14
+ * because undoing a schema change on a database with a year of entries in it
15
+ * is not a script, it is a restore.
16
+ */
17
+ /** The oldest schema `@ekwo-ai/core` reads. */
18
+ export const SCHEMA_MIN = '0.4.0';
19
+ /** `-1`, `0` or `1`, comparing two `major.minor.patch` versions numerically. */
20
+ export function compareSchemaVersions(left, right) {
21
+ const parse = (version) => version
22
+ .trim()
23
+ .split('.')
24
+ .map((part) => Number.parseInt(part, 10));
25
+ const a = parse(left);
26
+ const b = parse(right);
27
+ for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
28
+ const one = a[index] ?? 0;
29
+ const other = b[index] ?? 0;
30
+ if (Number.isNaN(one) || Number.isNaN(other)) {
31
+ throw new Error(`bad_schema_version: ${left} and ${right} are not both major.minor.patch`);
32
+ }
33
+ if (one !== other)
34
+ return one < other ? -1 : 1;
35
+ }
36
+ return 0;
37
+ }
38
+ /** Whether a database at `installed` is new enough for a package needing `minimum`. */
39
+ export function schemaIsAtLeast(installed, minimum) {
40
+ return compareSchemaVersions(installed, minimum) >= 0;
41
+ }
42
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,+CAA+C;AAC/C,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC;AAElC,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,KAAa;IAC/D,MAAM,KAAK,GAAG,CAAC,OAAe,EAAY,EAAE,CAC1C,OAAO;SACJ,IAAI,EAAE;SACN,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrE,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,QAAQ,KAAK,iCAAiC,CAAC,CAAC;QAC7F,CAAC;QACD,IAAI,GAAG,KAAK,KAAK;YAAE,OAAO,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,OAAe;IAChE,OAAO,qBAAqB,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC"}