@sudobility/sider_lib 0.0.2 → 0.0.3

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./templating";
1
+ export { templatePath, graphqlOperationOf, endpointKey, clusterObservations, hashRecipe, canonicalizeRecipe, } from "@sudobility/sider_types";
2
2
  export * from "./tokenize";
3
3
  export * from "./recipe";
4
4
  export * from "./gates";
package/dist/index.js CHANGED
@@ -8,7 +8,9 @@
8
8
  //
9
9
  // No Chrome/DOM/server dependencies — safe to import from sider_api,
10
10
  // sider_extension, and sider_app alike.
11
- export * from "./templating";
11
+ // Clustering/templating + recipe hashing moved to sider_types (shared with the
12
+ // backend); re-exported here so sider_lib's public API stays stable.
13
+ export { templatePath, graphqlOperationOf, endpointKey, clusterObservations, hashRecipe, canonicalizeRecipe, } from "@sudobility/sider_types";
12
14
  export * from "./tokenize";
13
15
  export * from "./recipe";
14
16
  export * from "./gates";
package/dist/recipe.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CompiledRequest, InvocationRecipe, ToolSpec } from "@sudobility/sider_types";
1
+ import type { CompiledRequest, ToolSpec } from "@sudobility/sider_types";
2
2
  export declare function secretSentinel(slotId: string): string;
3
3
  export declare class RecipeCompileError extends Error {
4
4
  }
@@ -13,6 +13,3 @@ export interface CompileContext {
13
13
  export declare function compileRecipe(tool: ToolSpec, ctx: CompileContext): CompiledRequest;
14
14
  /** Read a dotted/bracketed path from an object: "data.sections[0].id". */
15
15
  export declare function getByPath(obj: unknown, path: string): unknown;
16
- /** Stable stringify (sorted keys) so two users' equivalent recipes hash equal. */
17
- export declare function canonicalizeRecipe(recipe: InvocationRecipe): string;
18
- export declare function hashRecipe(recipe: InvocationRecipe): string;
package/dist/recipe.js CHANGED
@@ -92,24 +92,5 @@ export function getByPath(obj, path) {
92
92
  }
93
93
  return cur;
94
94
  }
95
- // --- Recipe canonicalization for corroboration hashing --------------------
96
- /** Stable stringify (sorted keys) so two users' equivalent recipes hash equal. */
97
- export function canonicalizeRecipe(recipe) {
98
- return stableStringify(recipe);
99
- }
100
- export function hashRecipe(recipe) {
101
- const s = canonicalizeRecipe(recipe);
102
- let h = 0;
103
- for (let i = 0; i < s.length; i++)
104
- h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
105
- return (h >>> 0).toString(16);
106
- }
107
- function stableStringify(v) {
108
- if (v === null || typeof v !== "object")
109
- return JSON.stringify(v) ?? "null";
110
- if (Array.isArray(v))
111
- return `[${v.map(stableStringify).join(",")}]`;
112
- const obj = v;
113
- const keys = Object.keys(obj).sort();
114
- return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
115
- }
95
+ // Recipe canonicalization + corroboration hashing now live in sider_types
96
+ // (shared by the backend trust engine); re-exported from sider_lib/index.ts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/sider_lib",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Sider business logic — recipe compiler, egress gates, tokenizer, clustering. Pure, no Chrome/DOM/server.",
5
5
  "license": "BUSL-1.1",
6
6
  "publishConfig": {
@@ -19,7 +19,7 @@
19
19
  "test": "bun test"
20
20
  },
21
21
  "dependencies": {
22
- "@sudobility/sider_types": "^0.0.2"
22
+ "@sudobility/sider_types": "^0.0.3"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/bun": "^1.3.14",
package/src/index.ts CHANGED
@@ -9,7 +9,16 @@
9
9
  // No Chrome/DOM/server dependencies — safe to import from sider_api,
10
10
  // sider_extension, and sider_app alike.
11
11
 
12
- export * from "./templating";
12
+ // Clustering/templating + recipe hashing moved to sider_types (shared with the
13
+ // backend); re-exported here so sider_lib's public API stays stable.
14
+ export {
15
+ templatePath,
16
+ graphqlOperationOf,
17
+ endpointKey,
18
+ clusterObservations,
19
+ hashRecipe,
20
+ canonicalizeRecipe,
21
+ } from "@sudobility/sider_types";
13
22
  export * from "./tokenize";
14
23
  export * from "./recipe";
15
24
  export * from "./gates";
package/src/recipe.ts CHANGED
@@ -7,7 +7,6 @@
7
7
 
8
8
  import type {
9
9
  CompiledRequest,
10
- InvocationRecipe,
11
10
  ParamBinding,
12
11
  ToolSpec,
13
12
  } from "@sudobility/sider_types";
@@ -120,24 +119,5 @@ export function getByPath(obj: unknown, path: string): unknown {
120
119
  return cur;
121
120
  }
122
121
 
123
- // --- Recipe canonicalization for corroboration hashing --------------------
124
-
125
- /** Stable stringify (sorted keys) so two users' equivalent recipes hash equal. */
126
- export function canonicalizeRecipe(recipe: InvocationRecipe): string {
127
- return stableStringify(recipe);
128
- }
129
-
130
- export function hashRecipe(recipe: InvocationRecipe): string {
131
- const s = canonicalizeRecipe(recipe);
132
- let h = 0;
133
- for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
134
- return (h >>> 0).toString(16);
135
- }
136
-
137
- function stableStringify(v: unknown): string {
138
- if (v === null || typeof v !== "object") return JSON.stringify(v) ?? "null";
139
- if (Array.isArray(v)) return `[${v.map(stableStringify).join(",")}]`;
140
- const obj = v as Record<string, unknown>;
141
- const keys = Object.keys(obj).sort();
142
- return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
143
- }
122
+ // Recipe canonicalization + corroboration hashing now live in sider_types
123
+ // (shared by the backend trust engine); re-exported from sider_lib/index.ts.
@@ -1,13 +0,0 @@
1
- import type { Observation } from "@sudobility/sider_types";
2
- /** Replace id-like path segments with `{id}`: /sections/120/seats → /sections/{id}/seats */
3
- export declare function templatePath(rawUrl: string): string;
4
- /**
5
- * GraphQL keying: one URL serves many operations, so cluster on operation name,
6
- * not path. Returns undefined for non-GraphQL requests.
7
- */
8
- export declare function graphqlOperationOf(url: string, body: unknown): string | undefined;
9
- type Clusterable = Pick<Observation, "method" | "url" | "requestBody">;
10
- /** Stable key identifying the endpoint template an observation belongs to. */
11
- export declare function endpointKey(o: Clusterable): string;
12
- export declare function clusterObservations<T extends Clusterable>(observations: T[]): Map<string, T[]>;
13
- export {};
@@ -1,66 +0,0 @@
1
- // Deterministic clustering & path templating. Runs BEFORE any AI: groups raw
2
- // observations into endpoint templates so the brain infers semantics once per
3
- // endpoint, not once per request. (This is the logic distill.ts inlined.)
4
- const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5
- const LONGHEX_RE = /^[0-9a-f]{16,}$/i;
6
- /** Replace id-like path segments with `{id}`: /sections/120/seats → /sections/{id}/seats */
7
- export function templatePath(rawUrl) {
8
- let pathname = rawUrl;
9
- try {
10
- pathname = new URL(rawUrl, "http://sider.local").pathname;
11
- }
12
- catch {
13
- /* keep raw */
14
- }
15
- return pathname
16
- .split("/")
17
- .map((seg) => {
18
- if (seg === "")
19
- return seg;
20
- if (/^\d+$/.test(seg))
21
- return "{id}";
22
- if (UUID_RE.test(seg))
23
- return "{id}";
24
- if (LONGHEX_RE.test(seg))
25
- return "{id}";
26
- return seg;
27
- })
28
- .join("/");
29
- }
30
- /**
31
- * GraphQL keying: one URL serves many operations, so cluster on operation name,
32
- * not path. Returns undefined for non-GraphQL requests.
33
- */
34
- export function graphqlOperationOf(url, body) {
35
- if (body && typeof body === "object") {
36
- const b = body;
37
- if (typeof b["operationName"] === "string" && b["operationName"]) {
38
- return b["operationName"];
39
- }
40
- if (typeof b["query"] === "string") {
41
- const m = /\b(query|mutation|subscription)\s+([A-Za-z0-9_]+)/.exec(b["query"]);
42
- if (m && m[2])
43
- return m[2];
44
- }
45
- }
46
- if (/\/graphql\b/i.test(url))
47
- return "anonymous";
48
- return undefined;
49
- }
50
- /** Stable key identifying the endpoint template an observation belongs to. */
51
- export function endpointKey(o) {
52
- const op = graphqlOperationOf(o.url, o.requestBody);
53
- return `${o.method} ${templatePath(o.url)}${op ? ` #${op}` : ""}`;
54
- }
55
- export function clusterObservations(observations) {
56
- const groups = new Map();
57
- for (const o of observations) {
58
- const key = endpointKey(o);
59
- const g = groups.get(key);
60
- if (g)
61
- g.push(o);
62
- else
63
- groups.set(key, [o]);
64
- }
65
- return groups;
66
- }
package/src/templating.ts DELETED
@@ -1,69 +0,0 @@
1
- // Deterministic clustering & path templating. Runs BEFORE any AI: groups raw
2
- // observations into endpoint templates so the brain infers semantics once per
3
- // endpoint, not once per request. (This is the logic distill.ts inlined.)
4
-
5
- import type { Observation } from "@sudobility/sider_types";
6
-
7
- const UUID_RE =
8
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9
- const LONGHEX_RE = /^[0-9a-f]{16,}$/i;
10
-
11
- /** Replace id-like path segments with `{id}`: /sections/120/seats → /sections/{id}/seats */
12
- export function templatePath(rawUrl: string): string {
13
- let pathname = rawUrl;
14
- try {
15
- pathname = new URL(rawUrl, "http://sider.local").pathname;
16
- } catch {
17
- /* keep raw */
18
- }
19
- return pathname
20
- .split("/")
21
- .map((seg) => {
22
- if (seg === "") return seg;
23
- if (/^\d+$/.test(seg)) return "{id}";
24
- if (UUID_RE.test(seg)) return "{id}";
25
- if (LONGHEX_RE.test(seg)) return "{id}";
26
- return seg;
27
- })
28
- .join("/");
29
- }
30
-
31
- /**
32
- * GraphQL keying: one URL serves many operations, so cluster on operation name,
33
- * not path. Returns undefined for non-GraphQL requests.
34
- */
35
- export function graphqlOperationOf(url: string, body: unknown): string | undefined {
36
- if (body && typeof body === "object") {
37
- const b = body as Record<string, unknown>;
38
- if (typeof b["operationName"] === "string" && b["operationName"]) {
39
- return b["operationName"];
40
- }
41
- if (typeof b["query"] === "string") {
42
- const m = /\b(query|mutation|subscription)\s+([A-Za-z0-9_]+)/.exec(b["query"]);
43
- if (m && m[2]) return m[2];
44
- }
45
- }
46
- if (/\/graphql\b/i.test(url)) return "anonymous";
47
- return undefined;
48
- }
49
-
50
- type Clusterable = Pick<Observation, "method" | "url" | "requestBody">;
51
-
52
- /** Stable key identifying the endpoint template an observation belongs to. */
53
- export function endpointKey(o: Clusterable): string {
54
- const op = graphqlOperationOf(o.url, o.requestBody);
55
- return `${o.method} ${templatePath(o.url)}${op ? ` #${op}` : ""}`;
56
- }
57
-
58
- export function clusterObservations<T extends Clusterable>(
59
- observations: T[],
60
- ): Map<string, T[]> {
61
- const groups = new Map<string, T[]>();
62
- for (const o of observations) {
63
- const key = endpointKey(o);
64
- const g = groups.get(key);
65
- if (g) g.push(o);
66
- else groups.set(key, [o]);
67
- }
68
- return groups;
69
- }