@ekwo-ai/core 0.7.0 → 0.8.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.
@@ -10,7 +10,8 @@
10
10
  *
11
11
  * This lived in the MCP server. The command line needs the same test and the
12
12
  * same sentence, so it moved here and both read it; like `refusal.ts`, this
13
- * file imports nothing.
13
+ * file imports nothing — and, since a browser reads it too, it reaches for no
14
+ * global that only one runtime has.
14
15
  */
15
16
  /** The variables a surface reads its user from. One spelling, for every surface. */
16
17
  export declare const IDENTITY_ENV: {
@@ -26,6 +27,20 @@ export declare const IDENTITY_ENV: {
26
27
  * Both shapes Supabase has issued: the signed JWT whose payload carries
27
28
  * `"role": "service_role"`, and the newer `sb_secret_…`. Neither is a mistake
28
29
  * we should let an operator make by pasting the wrong line of the dashboard.
30
+ *
31
+ * **A key shaped like a JWT whose payload cannot be read answers yes.** This
32
+ * used to decode that payload with `Buffer` inside a `try`, and a browser has
33
+ * no `Buffer`: the call raised, the `catch` swallowed it, and the function
34
+ * said *this is not a service_role key* about every JWT it was ever shown —
35
+ * the one answer that is never safe to guess. The shape is the claim, and a
36
+ * claim this function cannot read is one it cannot clear.
37
+ *
38
+ * **The header is what says the shape**, and not three pieces with dots
39
+ * between them: `postgresql://user@db.example.test:5432/postgres` has two dots
40
+ * and so does a password somebody chose, and neither of them is presenting a
41
+ * token. A JWT opens with a JSON object naming its algorithm; nothing here
42
+ * verifies the signature that algorithm produced, because the question is what
43
+ * the key claims and not whether the claim is true.
29
44
  */
30
45
  export declare function isServiceRoleKey(key: string): boolean;
31
46
  /** Where a key was put, which decides what the refusal has to explain. */
@@ -1 +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"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,oFAAoF;AACpF,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAoDX;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWrD;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"}
package/dist/identity.js CHANGED
@@ -10,7 +10,8 @@
10
10
  *
11
11
  * This lived in the MCP server. The command line needs the same test and the
12
12
  * same sentence, so it moved here and both read it; like `refusal.ts`, this
13
- * file imports nothing.
13
+ * file imports nothing — and, since a browser reads it too, it reaches for no
14
+ * global that only one runtime has.
14
15
  */
15
16
  /** The variables a surface reads its user from. One spelling, for every surface. */
16
17
  export const IDENTITY_ENV = {
@@ -20,26 +21,92 @@ export const IDENTITY_ENV = {
20
21
  password: 'EKWO_PASSWORD',
21
22
  accessToken: 'EKWO_ACCESS_TOKEN',
22
23
  };
24
+ /** The base64url alphabet, in value order. `indexOf` is the decoding table. */
25
+ const BASE64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
26
+ /**
27
+ * One segment of a JWT, as text — or nothing when it is not one.
28
+ *
29
+ * Written out rather than handed to `Buffer`, which a browser does not have,
30
+ * or to `atob`, which reads base64 and not base64url and throws on the two
31
+ * characters that differ. Six bits at a time into a byte buffer, then
32
+ * `TextDecoder`, which every runtime this code runs in carries. Nothing here
33
+ * verifies a signature: the question is what the key *claims* to be.
34
+ */
35
+ function decodeSegment(segment) {
36
+ const text = segment.replace(/=+$/, '');
37
+ const bytes = [];
38
+ let buffer = 0;
39
+ let bits = 0;
40
+ for (const character of text) {
41
+ const value = BASE64URL.indexOf(character);
42
+ if (value < 0)
43
+ return undefined;
44
+ buffer = (buffer << 6) | value;
45
+ bits += 6;
46
+ if (bits >= 8) {
47
+ bits -= 8;
48
+ bytes.push((buffer >> bits) & 0xff);
49
+ }
50
+ }
51
+ try {
52
+ return new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(bytes));
53
+ }
54
+ catch {
55
+ return undefined;
56
+ }
57
+ }
58
+ /** A segment that carries a JSON object, as that object. Anything else: nothing. */
59
+ function readSegment(segment) {
60
+ if (segment === undefined || segment.length === 0)
61
+ return undefined;
62
+ const text = decodeSegment(segment);
63
+ if (text === undefined)
64
+ return undefined;
65
+ let value;
66
+ try {
67
+ value = JSON.parse(text);
68
+ }
69
+ catch {
70
+ return undefined;
71
+ }
72
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
73
+ ? value
74
+ : undefined;
75
+ }
23
76
  /**
24
77
  * True when a key is, or claims to be, a `service_role` key.
25
78
  *
26
79
  * Both shapes Supabase has issued: the signed JWT whose payload carries
27
80
  * `"role": "service_role"`, and the newer `sb_secret_…`. Neither is a mistake
28
81
  * we should let an operator make by pasting the wrong line of the dashboard.
82
+ *
83
+ * **A key shaped like a JWT whose payload cannot be read answers yes.** This
84
+ * used to decode that payload with `Buffer` inside a `try`, and a browser has
85
+ * no `Buffer`: the call raised, the `catch` swallowed it, and the function
86
+ * said *this is not a service_role key* about every JWT it was ever shown —
87
+ * the one answer that is never safe to guess. The shape is the claim, and a
88
+ * claim this function cannot read is one it cannot clear.
89
+ *
90
+ * **The header is what says the shape**, and not three pieces with dots
91
+ * between them: `postgresql://user@db.example.test:5432/postgres` has two dots
92
+ * and so does a password somebody chose, and neither of them is presenting a
93
+ * token. A JWT opens with a JSON object naming its algorithm; nothing here
94
+ * verifies the signature that algorithm produced, because the question is what
95
+ * the key claims and not whether the claim is true.
29
96
  */
30
97
  export function isServiceRoleKey(key) {
31
98
  if (key.startsWith('sb_secret_'))
32
99
  return true;
33
100
  const parts = key.split('.');
34
- if (parts.length !== 3 || parts[1] === undefined)
101
+ if (parts.length !== 3)
35
102
  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 {
103
+ const header = readSegment(parts[0]);
104
+ if (header === undefined || typeof header.alg !== 'string')
41
105
  return false;
42
- }
106
+ const payload = readSegment(parts[1]);
107
+ if (payload === undefined)
108
+ return true;
109
+ return payload.role === 'service_role';
43
110
  }
44
111
  /**
45
112
  * The refusal, word for word, for whichever surface met the key.
@@ -1 +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"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
package/dist/schema.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * is not a script, it is a restore.
16
16
  */
17
17
  /** The oldest schema `@ekwo-ai/core` reads. */
18
- export declare const SCHEMA_MIN = "0.7.0";
18
+ export declare const SCHEMA_MIN = "0.8.0";
19
19
  /** `-1`, `0` or `1`, comparing two `major.minor.patch` versions numerically. */
20
20
  export declare function compareSchemaVersions(left: string, right: string): number;
21
21
  /** Whether a database at `installed` is new enough for a package needing `minimum`. */
package/dist/schema.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * is not a script, it is a restore.
16
16
  */
17
17
  /** The oldest schema `@ekwo-ai/core` reads. */
18
- export const SCHEMA_MIN = '0.7.0';
18
+ export const SCHEMA_MIN = '0.8.0';
19
19
  /** `-1`, `0` or `1`, comparing two `major.minor.patch` versions numerically. */
20
20
  export function compareSchemaVersions(left, right) {
21
21
  const parse = (version) => version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekwo-ai/core",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "TypeScript row types and a typed Supabase client for the Ekwo OS accounting schema.",
5
5
  "keywords": ["accounting", "supabase", "postgres", "pcmn", "pcg", "bookkeeping", "ekwo"],
6
6
  "license": "AGPL-3.0-only",
@@ -11,7 +11,7 @@
11
11
  "type": "module",
12
12
  "sideEffects": false,
13
13
  "engines": { "node": ">=20" },
14
- "ekwo": { "schemaMin": "0.7.0" },
14
+ "ekwo": { "schemaMin": "0.8.0" },
15
15
  "files": ["dist", "README.md", "CHANGELOG.md"],
16
16
  "main": "./dist/index.js",
17
17
  "types": "./dist/index.d.ts",
@@ -23,13 +23,13 @@
23
23
  "typecheck": "tsc -p tsconfig.json --noEmit"
24
24
  },
25
25
  "dependencies": {
26
- "@ekwo-ai/camt053": "^0.7.0",
27
- "@ekwo-ai/cfonb120": "^0.7.0",
28
- "@ekwo-ai/coda": "^0.7.0",
29
- "@ekwo-ai/fec": "^0.7.0",
30
- "@ekwo-ai/journal-items": "^0.7.0",
31
- "@ekwo-ai/journal-report": "^0.7.0",
32
- "@ekwo-ai/trial-balance": "^0.7.0"
26
+ "@ekwo-ai/camt053": "^0.8.0",
27
+ "@ekwo-ai/cfonb120": "^0.8.0",
28
+ "@ekwo-ai/coda": "^0.8.0",
29
+ "@ekwo-ai/fec": "^0.8.0",
30
+ "@ekwo-ai/journal-items": "^0.8.0",
31
+ "@ekwo-ai/journal-report": "^0.8.0",
32
+ "@ekwo-ai/trial-balance": "^0.8.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@supabase/supabase-js": "^2.0.0"