@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.
- package/dist/identity.d.ts +16 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +75 -8
- package/dist/identity.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +1 -1
- package/package.json +9 -9
package/dist/identity.d.ts
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 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. */
|
package/dist/identity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA
|
|
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
|
|
101
|
+
if (parts.length !== 3)
|
|
35
102
|
return false;
|
|
36
|
-
|
|
37
|
-
|
|
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.
|
package/dist/identity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
27
|
-
"@ekwo-ai/cfonb120": "^0.
|
|
28
|
-
"@ekwo-ai/coda": "^0.
|
|
29
|
-
"@ekwo-ai/fec": "^0.
|
|
30
|
-
"@ekwo-ai/journal-items": "^0.
|
|
31
|
-
"@ekwo-ai/journal-report": "^0.
|
|
32
|
-
"@ekwo-ai/trial-balance": "^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"
|