@sealkeeper/schema 0.4.3 → 0.4.5

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 (54) hide show
  1. package/dist/agent-name.d.ts +1 -1
  2. package/dist/agent-name.js +4 -4
  3. package/dist/api.d.ts +744 -27
  4. package/dist/api.js +232 -43
  5. package/dist/badge.d.ts +1 -0
  6. package/dist/badge.js +7 -0
  7. package/dist/credential.d.ts +186 -1
  8. package/dist/credential.js +55 -8
  9. package/dist/db/agents.d.ts +70 -0
  10. package/dist/db/agents.js +18 -0
  11. package/dist/db/client.d.ts +398 -0
  12. package/dist/db/client.js +2 -0
  13. package/dist/db/events.js +7 -0
  14. package/dist/db/index.d.ts +17 -1
  15. package/dist/db/index.js +5 -1
  16. package/dist/db/migrate.js +5 -1
  17. package/dist/db/migrator.d.ts +3 -1
  18. package/dist/db/migrator.js +8 -2
  19. package/dist/db/operator-identities.d.ts +211 -0
  20. package/dist/db/operator-identities.js +49 -0
  21. package/dist/db/operator-level-grants.d.ts +109 -0
  22. package/dist/db/operator-level-grants.js +30 -0
  23. package/dist/db/operator-slugs.d.ts +92 -0
  24. package/dist/db/operator-slugs.js +25 -0
  25. package/dist/db/operators.d.ts +119 -0
  26. package/dist/db/operators.js +30 -2
  27. package/dist/db/standing.d.ts +36 -0
  28. package/dist/db/standing.js +5 -0
  29. package/dist/db/task-claim-failures.d.ts +143 -0
  30. package/dist/db/task-claim-failures.js +37 -0
  31. package/dist/db/task-outcomes.d.ts +19 -0
  32. package/dist/db/task-outcomes.js +8 -1
  33. package/dist/db/tasks.d.ts +79 -0
  34. package/dist/db/tasks.js +19 -1
  35. package/dist/envelope.d.ts +3 -0
  36. package/dist/envelope.js +16 -11
  37. package/dist/goal.d.ts +149 -0
  38. package/dist/goal.js +171 -0
  39. package/dist/index.d.ts +6 -0
  40. package/dist/index.js +6 -0
  41. package/dist/moderation.d.ts +28 -0
  42. package/dist/moderation.js +450 -0
  43. package/dist/operator-domains.d.ts +104 -0
  44. package/dist/operator-domains.js +85 -0
  45. package/dist/policy.d.ts +4 -0
  46. package/dist/policy.js +10 -0
  47. package/dist/runtime.d.ts +17 -0
  48. package/dist/runtime.js +61 -0
  49. package/dist/seal-conformance.js +32 -1
  50. package/dist/standing.d.ts +68 -0
  51. package/dist/standing.js +132 -6
  52. package/dist/tasks.d.ts +23 -1
  53. package/dist/tasks.js +29 -5
  54. package/package.json +1 -1
@@ -1,11 +1,39 @@
1
- import { bigint, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
1
+ import { bigint, pgTable, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
2
2
  import { timestamps } from './timestamps.js';
3
+ // github_id is the identity, since logins change. login is kept fresh from
4
+ // GitHub and shows only in the operator section of a profile. slug is what
5
+ // the operator is known by, the first half of every handle, as in
6
+ // alice/claude-code. It defaults to the login lowercased and changes on the
7
+ // web only (PATCH /v1/me). display_name is free text, and display_name_key
8
+ // is its normalised form (displayNameKey in @sealkeeper/schema), unique so
9
+ // two operators never hold names that read the same, even under concurrent
10
+ // writes. slug_changed_at is when the slug last changed, null until the
11
+ // first change, for the one change every SLUG_CHANGE_DAYS.
12
+ // slug_notice_dismissed_at is when the operator dismissed the one time
13
+ // notice on /me that says what its slug is and that it can change, null
14
+ // until then (VOU-190). Held here, not in the browser, so it shows once
15
+ // across devices. terms_version and privacy_version are the "Last updated"
16
+ // dates of the terms and the privacy policy (POLICY_VERSIONS in
17
+ // @sealkeeper/schema) in force when the API created the row, which is when
18
+ // the operator accepted them (VOU-121). Null for operators created before.
3
19
  export const operators = pgTable('operators', {
4
20
  id: uuid('id').primaryKey().defaultRandom(),
5
21
  githubId: bigint('github_id', { mode: 'number' }).notNull().unique(),
6
22
  login: text('login').notNull(),
23
+ slug: text('slug').notNull(),
24
+ displayName: text('display_name').notNull(),
25
+ displayNameKey: text('display_name_key').notNull(),
26
+ slugChangedAt: timestamp('slug_changed_at', { withTimezone: true }),
27
+ slugNoticeDismissedAt: timestamp('slug_notice_dismissed_at', {
28
+ withTimezone: true,
29
+ }),
7
30
  accountCreatedAt: timestamp('account_created_at', {
8
31
  withTimezone: true,
9
32
  }).notNull(),
33
+ termsVersion: text('terms_version'),
34
+ privacyVersion: text('privacy_version'),
10
35
  ...timestamps,
11
- });
36
+ }, (t) => [
37
+ uniqueIndex('operators_slug_unique').on(t.slug),
38
+ uniqueIndex('operators_display_name_key_unique').on(t.displayNameKey),
39
+ ]);
@@ -10,6 +10,23 @@ export type StoredCounts = {
10
10
  confirmed_operators: number;
11
11
  history_span_days: number;
12
12
  safety_incidents_180d: number;
13
+ clean_days: number;
14
+ gold_confirmed_tasks: number;
15
+ gold_confirmed_operators: number;
16
+ counted_verified_tasks: number;
17
+ counted_seed_tasks: number;
18
+ counted_server_checked_tasks: number;
19
+ counted_confirmed_tasks: number;
20
+ counted_gold_confirmed_tasks: number;
21
+ };
22
+ export type StoredLevelInputs = {
23
+ reliability: number | null;
24
+ safety: number | null;
25
+ provenance: number | null;
26
+ model_declared: boolean;
27
+ operator_verified: boolean;
28
+ ratings: number;
29
+ ratings_net_positive: boolean;
13
30
  };
14
31
  export declare const standing: import("drizzle-orm/pg-core").PgTableWithColumns<{
15
32
  name: "standing";
@@ -121,6 +138,25 @@ export declare const standing: import("drizzle-orm/pg-core").PgTableWithColumns<
121
138
  }, {}, {
122
139
  $type: StoredCounts;
123
140
  }>;
141
+ levelInputs: import("drizzle-orm/pg-core").PgColumn<{
142
+ name: "level_inputs";
143
+ tableName: "standing";
144
+ dataType: "json";
145
+ columnType: "PgJsonb";
146
+ data: StoredLevelInputs;
147
+ driverParam: unknown;
148
+ notNull: false;
149
+ hasDefault: false;
150
+ isPrimaryKey: false;
151
+ isAutoincrement: false;
152
+ hasRuntimeDefault: false;
153
+ enumValues: undefined;
154
+ baseColumn: never;
155
+ identity: undefined;
156
+ generated: undefined;
157
+ }, {}, {
158
+ $type: StoredLevelInputs;
159
+ }>;
124
160
  historyDays: import("drizzle-orm/pg-core").PgColumn<{
125
161
  name: "history_days";
126
162
  tableName: "standing";
@@ -9,6 +9,8 @@ import { timestamps } from './timestamps.js';
9
9
  // true at 90 or more, when the level is none and no SEAL is issued until
10
10
  // activity resumes. counts are the ones the level stands on, which for a
11
11
  // new version include what it inherited from the previous one.
12
+ // level_inputs are the scores and flags the level read, null on a row
13
+ // written before they were stored, until the agent's next run.
12
14
  export const standing = pgTable('standing', {
13
15
  agentId: text('agent_id')
14
16
  .notNull()
@@ -16,6 +18,7 @@ export const standing = pgTable('standing', {
16
18
  version: text('version').notNull(),
17
19
  level: text('level').$type().notNull(),
18
20
  counts: jsonb('counts').$type().notNull(),
21
+ levelInputs: jsonb('level_inputs').$type(),
19
22
  historyDays: integer('history_days').notNull(),
20
23
  lastActive: timestamp('last_active', { withTimezone: true }),
21
24
  dormantDays: integer('dormant_days'),
@@ -25,5 +28,7 @@ export const standing = pgTable('standing', {
25
28
  ...timestamps,
26
29
  }, (t) => [
27
30
  primaryKey({ columns: [t.agentId, t.version] }),
31
+ // The issued levels only. Platinum is reserved on LADDER and has no
32
+ // standing row until it is issued with a SEAL ver bump.
28
33
  check('standing_level', sql `${t.level} in ('none', 'bronze', 'silver', 'gold')`),
29
34
  ]);
@@ -0,0 +1,143 @@
1
+ export declare const taskClaimFailures: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "task_claim_failures";
3
+ schema: undefined;
4
+ columns: {
5
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "created_at";
7
+ tableName: "task_claim_failures";
8
+ dataType: "date";
9
+ columnType: "PgTimestamp";
10
+ data: Date;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: true;
14
+ isPrimaryKey: false;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: undefined;
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "updated_at";
24
+ tableName: "task_claim_failures";
25
+ dataType: "date";
26
+ columnType: "PgTimestamp";
27
+ data: Date;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: true;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: undefined;
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ id: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "id";
41
+ tableName: "task_claim_failures";
42
+ dataType: "string";
43
+ columnType: "PgUUID";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
47
+ hasDefault: true;
48
+ isPrimaryKey: true;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: undefined;
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ taskId: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "task_id";
58
+ tableName: "task_claim_failures";
59
+ dataType: "string";
60
+ columnType: "PgUUID";
61
+ data: string;
62
+ driverParam: string;
63
+ notNull: true;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: undefined;
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ agentId: import("drizzle-orm/pg-core").PgColumn<{
74
+ name: "agent_id";
75
+ tableName: "task_claim_failures";
76
+ dataType: "string";
77
+ columnType: "PgText";
78
+ data: string;
79
+ driverParam: string;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: [string, ...string[]];
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {}>;
90
+ version: import("drizzle-orm/pg-core").PgColumn<{
91
+ name: "version";
92
+ tableName: "task_claim_failures";
93
+ dataType: "string";
94
+ columnType: "PgText";
95
+ data: string;
96
+ driverParam: string;
97
+ notNull: true;
98
+ hasDefault: false;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: [string, ...string[]];
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ claimedAt: import("drizzle-orm/pg-core").PgColumn<{
108
+ name: "claimed_at";
109
+ tableName: "task_claim_failures";
110
+ dataType: "date";
111
+ columnType: "PgTimestamp";
112
+ data: Date;
113
+ driverParam: string;
114
+ notNull: true;
115
+ hasDefault: false;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: undefined;
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ failedAt: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "failed_at";
126
+ tableName: "task_claim_failures";
127
+ dataType: "date";
128
+ columnType: "PgTimestamp";
129
+ data: Date;
130
+ driverParam: string;
131
+ notNull: true;
132
+ hasDefault: true;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: undefined;
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ };
142
+ dialect: 'pg';
143
+ }>;
@@ -0,0 +1,37 @@
1
+ import { index, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
2
+ import { agents } from './agents.js';
3
+ import { tasks } from './tasks.js';
4
+ import { timestamps } from './timestamps.js';
5
+ /*
6
+ * Claims released at the failed submit cap (TASK_MAX_FAILED_SUBMITS in
7
+ * apps/api/src/routes/tasks.ts). The release nulls the task's claimant and
8
+ * claimed_at, so without this row the claim would drop out of the agent's
9
+ * reliability. The scoring run counts each row as a claim that never
10
+ * verified, like a claim held until expiry.
11
+ *
12
+ * claimed_at is the released claim's, and version the claimant's version
13
+ * at that claim, worked out at the release the same way the run credits a
14
+ * task (claimVersion in apps/api/src/scoring/run.ts). failed_at is the
15
+ * submit that reached the cap. Deleting the agent or the task deletes the
16
+ * row (deleteAgents in apps/api/src/account.ts).
17
+ */
18
+ export const taskClaimFailures = pgTable('task_claim_failures', {
19
+ id: uuid('id').primaryKey().defaultRandom(),
20
+ taskId: uuid('task_id')
21
+ .notNull()
22
+ .references(() => tasks.id),
23
+ agentId: text('agent_id')
24
+ .notNull()
25
+ .references(() => agents.id),
26
+ version: text('version').notNull(),
27
+ claimedAt: timestamp('claimed_at', { withTimezone: true }).notNull(),
28
+ failedAt: timestamp('failed_at', { withTimezone: true })
29
+ .notNull()
30
+ .defaultNow(),
31
+ ...timestamps,
32
+ }, (t) => [
33
+ // The scoring run's read, one agent's released claims by claim time.
34
+ index('task_claim_failures_agent_claimed_at_idx').on(t.agentId, t.claimedAt),
35
+ // A deletion's rows by task.
36
+ index('task_claim_failures_task_id_idx').on(t.taskId),
37
+ ]);
@@ -121,6 +121,25 @@ export declare const taskOutcomes: import("drizzle-orm/pg-core").PgTableWithColu
121
121
  identity: undefined;
122
122
  generated: undefined;
123
123
  }, {}, {}>;
124
+ origin: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "origin";
126
+ tableName: "task_outcomes";
127
+ dataType: "string";
128
+ columnType: "PgText";
129
+ data: "manual" | "routine" | "template";
130
+ driverParam: string;
131
+ notNull: true;
132
+ hasDefault: true;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: [string, ...string[]];
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {
141
+ $type: "manual" | "routine" | "template";
142
+ }>;
124
143
  };
125
144
  dialect: 'pg';
126
145
  }>;
@@ -1,4 +1,5 @@
1
- import { jsonb, pgTable, text, unique, uuid } from 'drizzle-orm/pg-core';
1
+ import { sql } from 'drizzle-orm';
2
+ import { check, jsonb, pgTable, text, unique, uuid } from 'drizzle-orm/pg-core';
2
3
  import { agents } from './agents.js';
3
4
  import { tasks } from './tasks.js';
4
5
  import { timestamps } from './timestamps.js';
@@ -12,7 +13,13 @@ export const taskOutcomes = pgTable('task_outcomes', {
12
13
  .references(() => agents.id),
13
14
  outcome: text('outcome').notNull(),
14
15
  evidence: jsonb('evidence'),
16
+ // Where this report came from, TaskOrigin in ../api.ts, from the signed
17
+ // outcome payload. manual when the payload has none. A new report
18
+ // replaces it with the rest. Gold leaves out a confirmed task either of
19
+ // whose reports is routine.
20
+ origin: text('origin').$type().notNull().default('manual'),
15
21
  ...timestamps,
16
22
  }, (t) => [
23
+ check('task_outcomes_origin', sql `${t.origin} in ('manual', 'template', 'routine')`),
17
24
  unique('task_outcomes_task_id_reporting_agent_id_unique').on(t.taskId, t.reportingAgentId),
18
25
  ]);
@@ -138,6 +138,25 @@ export declare const tasks: import("drizzle-orm/pg-core").PgTableWithColumns<{
138
138
  identity: undefined;
139
139
  generated: undefined;
140
140
  }, {}, {}>;
141
+ origin: import("drizzle-orm/pg-core").PgColumn<{
142
+ name: "origin";
143
+ tableName: "tasks";
144
+ dataType: "string";
145
+ columnType: "PgText";
146
+ data: "manual" | "routine" | "template";
147
+ driverParam: string;
148
+ notNull: true;
149
+ hasDefault: true;
150
+ isPrimaryKey: false;
151
+ isAutoincrement: false;
152
+ hasRuntimeDefault: false;
153
+ enumValues: [string, ...string[]];
154
+ baseColumn: never;
155
+ identity: undefined;
156
+ generated: undefined;
157
+ }, {}, {
158
+ $type: "manual" | "routine" | "template";
159
+ }>;
141
160
  taskType: import("drizzle-orm/pg-core").PgColumn<{
142
161
  name: "task_type";
143
162
  tableName: "tasks";
@@ -280,6 +299,66 @@ export declare const tasks: import("drizzle-orm/pg-core").PgTableWithColumns<{
280
299
  text: string;
281
300
  };
282
301
  }>;
302
+ failedSubmits: import("drizzle-orm/pg-core").PgColumn<{
303
+ name: "failed_submits";
304
+ tableName: "tasks";
305
+ dataType: "number";
306
+ columnType: "PgInteger";
307
+ data: number;
308
+ driverParam: string | number;
309
+ notNull: true;
310
+ hasDefault: true;
311
+ isPrimaryKey: false;
312
+ isAutoincrement: false;
313
+ hasRuntimeDefault: false;
314
+ enumValues: undefined;
315
+ baseColumn: never;
316
+ identity: undefined;
317
+ generated: undefined;
318
+ }, {}, {}>;
319
+ barredAgentIds: import("drizzle-orm/pg-core").PgColumn<{
320
+ name: "barred_agent_ids";
321
+ tableName: "tasks";
322
+ dataType: "array";
323
+ columnType: "PgArray";
324
+ data: string[];
325
+ driverParam: string | string[];
326
+ notNull: true;
327
+ hasDefault: true;
328
+ isPrimaryKey: false;
329
+ isAutoincrement: false;
330
+ hasRuntimeDefault: false;
331
+ enumValues: [string, ...string[]];
332
+ baseColumn: import("drizzle-orm").Column<{
333
+ name: "barred_agent_ids";
334
+ tableName: "tasks";
335
+ dataType: "string";
336
+ columnType: "PgText";
337
+ data: string;
338
+ driverParam: string;
339
+ notNull: false;
340
+ hasDefault: false;
341
+ isPrimaryKey: false;
342
+ isAutoincrement: false;
343
+ hasRuntimeDefault: false;
344
+ enumValues: [string, ...string[]];
345
+ baseColumn: never;
346
+ identity: undefined;
347
+ generated: undefined;
348
+ }, {}, {}>;
349
+ identity: undefined;
350
+ generated: undefined;
351
+ }, {}, {
352
+ baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
353
+ name: "barred_agent_ids";
354
+ dataType: 'string';
355
+ columnType: 'PgText';
356
+ data: string;
357
+ enumValues: [string, ...string[]];
358
+ driverParam: string;
359
+ }, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
360
+ size: undefined;
361
+ }>;
283
362
  expiresAt: import("drizzle-orm/pg-core").PgColumn<{
284
363
  name: "expires_at";
285
364
  tableName: "tasks";
package/dist/db/tasks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { sql } from 'drizzle-orm';
2
- import { boolean, index, jsonb, pgTable, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
2
+ import { boolean, check, index, integer, jsonb, pgTable, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
3
3
  import { agents } from './agents.js';
4
4
  import { timestamps } from './timestamps.js';
5
5
  export const tasks = pgTable('tasks', {
@@ -23,6 +23,11 @@ export const tasks = pgTable('tasks', {
23
23
  // cap, so it weighs nothing as evidence, success or failure. False for
24
24
  // every other task.
25
25
  overPairCap: boolean('over_pair_cap').notNull().default(false),
26
+ // Where the post came from, TaskOrigin in ../api.ts, from the signed
27
+ // post payload. manual when the payload has none, and for every task
28
+ // posted before origin existed. Gold counts a confirmed task only when
29
+ // this is manual (apps/api/src/scoring/evidence.ts).
30
+ origin: text('origin').$type().notNull().default('manual'),
26
31
  taskType: text('task_type').notNull(),
27
32
  spec: jsonb('spec').notNull(),
28
33
  verification: jsonb('verification').notNull(),
@@ -37,6 +42,18 @@ export const tasks = pgTable('tasks', {
37
42
  // does not survive a read. postgres-js parses jsonb and Drizzle then
38
43
  // JSON.parses any string it gets back, so "123" would return as 123.
39
44
  submission: jsonb('submission').$type(),
45
+ // Failed submits on the current claim of a hash or schema task. The
46
+ // submit route counts them up, and at the cap (TASK_MAX_FAILED_SUBMITS
47
+ // in apps/api/src/routes/tasks.ts) it releases the claim and sets this
48
+ // back to 0 for the next claimant.
49
+ failedSubmits: integer('failed_submits').notNull().default(0),
50
+ // Agents whose claim was released at the failed submit cap. None of them
51
+ // can claim this task again, so guessing at a public hash costs a claim
52
+ // each three tries. Deleting an agent takes its id out.
53
+ barredAgentIds: text('barred_agent_ids')
54
+ .array()
55
+ .notNull()
56
+ .default(sql `'{}'::text[]`),
40
57
  expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
41
58
  ...timestamps,
42
59
  },
@@ -47,6 +64,7 @@ export const tasks = pgTable('tasks', {
47
64
  * state derivation in apps/api/src/routes/tasks.ts (stateWhere).
48
65
  */
49
66
  (t) => [
67
+ check('tasks_origin', sql `${t.origin} in ('manual', 'template', 'routine')`),
50
68
  // The open task cap per poster, and a deletion's tasks by poster.
51
69
  index('tasks_poster_agent_id_idx').on(t.posterAgentId),
52
70
  // The claim cap per claimant, activity counts, scoring and evidence.
@@ -5,8 +5,11 @@ export declare const JwsHeader: z.ZodObject<{
5
5
  kid: z.ZodString;
6
6
  }, z.core.$strict>;
7
7
  export type JwsHeader = z.infer<typeof JwsHeader>;
8
+ export type EnvelopeErrorCode = 'malformed' | 'bad_signature' | 'invalid_input';
8
9
  export declare class EnvelopeError extends Error {
10
+ readonly code: EnvelopeErrorCode;
9
11
  name: string;
12
+ constructor(code: EnvelopeErrorCode, message: string);
10
13
  }
11
14
  export declare function sign(payload: unknown, privateKey: Uint8Array, kid: string): Promise<string>;
12
15
  export declare function decodeHeader(jws: string): JwsHeader;
package/dist/envelope.js CHANGED
@@ -11,11 +11,16 @@ export const JwsHeader = z.strictObject({
11
11
  kid: z.string().min(1).max(128),
12
12
  });
13
13
  export class EnvelopeError extends Error {
14
+ code;
14
15
  name = 'EnvelopeError';
16
+ constructor(code, message) {
17
+ super(message);
18
+ this.code = code;
19
+ }
15
20
  }
16
21
  function split(jws) {
17
22
  if (!Jws.safeParse(jws).success) {
18
- throw new EnvelopeError('Envelope is not three base64url parts');
23
+ throw new EnvelopeError('malformed', 'Envelope is not three base64url parts');
19
24
  }
20
25
  const [header, payload, signature] = jws.split('.');
21
26
  return { header, payload, signature };
@@ -25,13 +30,13 @@ function parseJson(part, what) {
25
30
  return JSON.parse(utf8Decode(base64urlDecode(part)));
26
31
  }
27
32
  catch {
28
- throw new EnvelopeError(`Envelope ${what} is not base64url JSON`);
33
+ throw new EnvelopeError('malformed', `Envelope ${what} is not base64url JSON`);
29
34
  }
30
35
  }
31
36
  function parseHeader(part) {
32
37
  const result = JwsHeader.safeParse(parseJson(part, 'header'));
33
38
  if (!result.success) {
34
- throw new EnvelopeError('Envelope header must be alg EdDSA with a kid');
39
+ throw new EnvelopeError('malformed', 'Envelope header must be alg EdDSA with a kid');
35
40
  }
36
41
  return result.data;
37
42
  }
@@ -39,7 +44,7 @@ export async function sign(payload, privateKey, kid) {
39
44
  const header = JwsHeader.parse({ alg: 'EdDSA', kid });
40
45
  const body = JSON.stringify(payload);
41
46
  if (body === undefined)
42
- throw new EnvelopeError('Payload is not JSON');
47
+ throw new EnvelopeError('invalid_input', 'Payload is not JSON');
43
48
  const signingInput = `${base64urlEncode(utf8Encode(JSON.stringify(header)))}.${base64urlEncode(utf8Encode(body))}`;
44
49
  const signature = await signAsync(utf8Encode(signingInput), privateKey);
45
50
  return `${signingInput}.${base64urlEncode(signature)}`;
@@ -64,7 +69,7 @@ export async function verify(jws, publicKey) {
64
69
  ok = false;
65
70
  }
66
71
  if (!ok)
67
- throw new EnvelopeError('Envelope signature is invalid');
72
+ throw new EnvelopeError('bad_signature', 'Envelope signature is invalid');
68
73
  const header = parseHeader(parts.header);
69
74
  return { header, payload: parseJson(parts.payload, 'payload') };
70
75
  }
@@ -95,26 +100,26 @@ export function audienceOf(apiUrl) {
95
100
  url = new URL(apiUrl);
96
101
  }
97
102
  catch {
98
- throw new EnvelopeError('API URL is not a URL');
103
+ throw new EnvelopeError('invalid_input', 'API URL is not a URL');
99
104
  }
100
105
  if (url.protocol !== 'http:' && url.protocol !== 'https:') {
101
- throw new EnvelopeError('API URL must be http or https');
106
+ throw new EnvelopeError('invalid_input', 'API URL must be http or https');
102
107
  }
103
108
  if (url.origin === 'null') {
104
- throw new EnvelopeError('API URL has no origin');
109
+ throw new EnvelopeError('invalid_input', 'API URL has no origin');
105
110
  }
106
111
  return url.origin;
107
112
  }
108
113
  // A copy of the payload with `aud` added. Never mutates the input.
109
114
  export function withAudience(payload, aud) {
110
115
  if (!Audience.safeParse(aud).success) {
111
- throw new EnvelopeError('aud must be an origin');
116
+ throw new EnvelopeError('invalid_input', 'aud must be an origin');
112
117
  }
113
118
  if (Array.isArray(payload)) {
114
- throw new EnvelopeError('A signed request payload must be an object');
119
+ throw new EnvelopeError('invalid_input', 'A signed request payload must be an object');
115
120
  }
116
121
  if (Object.hasOwn(payload, 'aud')) {
117
- throw new EnvelopeError('Payload already has an aud');
122
+ throw new EnvelopeError('invalid_input', 'Payload already has an aud');
118
123
  }
119
124
  return { ...payload, aud };
120
125
  }