@ultimat3/core 25.0.0 → 25.1.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/README.md CHANGED
@@ -11,6 +11,7 @@ Zero dependencies, zero `@ultimat3/*` imports.
11
11
  | the one lazy `AsyncLocalStorage`, every ambient scope in the framework | `async-context.ts` |
12
12
  | request context on that seam | `context.ts` |
13
13
  | `Actor` (`user \| service \| agent \| anonymous`) | `actor.ts` |
14
+ | whether a permission grant reaches a name — `grantCovers`, `*` and `<prefix>:*` | `actor.ts` |
14
15
  | acting as another actor, with an origin and a reason | `impersonate.ts` |
15
16
  | is an error worth retrying? one classification per code | `error-retry.ts` |
16
17
  | how long to wait before the next attempt — one curve, one jitter table | `backoff.ts` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/core",
3
- "version": "25.0.0",
3
+ "version": "25.1.0",
4
4
  "description": "Ultimate's foundation: errors, context, env, config, clock, ids, logging, telemetry, lifecycle",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,6 +37,6 @@
37
37
  "test": "bun test"
38
38
  },
39
39
  "dependencies": {
40
- "@ultimat3/schema": "25.0.0"
40
+ "@ultimat3/schema": "25.1.0"
41
41
  }
42
42
  }
package/src/actor.ts CHANGED
@@ -202,6 +202,18 @@ export function hasScope(actor: Actor, scope: string): boolean {
202
202
  return actor.scopes.includes(scope);
203
203
  }
204
204
 
205
+ /**
206
+ * Whether one permission grant reaches `wanted`: `*` reaches everything, `<prefix>:*` every name
207
+ * beginning `<prefix>:` (any depth), anything else only itself. The PREFIX, never the first
208
+ * segment: `billing:invoice:*` read as "all of `billing`" granted refunds to an invoice clerk.
209
+ * Here, at tier 0, because `@ultimat3/policy` (`can()`) and `@ultimat3/auth` (an API key's cut
210
+ * to its owner) are one tier and may not share a copy — two copies were how it drifted.
211
+ */
212
+ export function grantCovers(grant: string, wanted: string): boolean {
213
+ if (grant === '*' || grant === wanted) return true;
214
+ return grant.endsWith(':*') && wanted.startsWith(grant.slice(0, -1));
215
+ }
216
+
205
217
  /**
206
218
  * Attach resolved facts to an actor, once, at the request boundary — the one place that already
207
219
  * awaited the database. Returns a new frozen actor, so the actor a predicate reads later cannot
package/src/index.ts CHANGED
@@ -36,6 +36,7 @@ export {
36
36
  actorOrigin,
37
37
  agentActor,
38
38
  anonymousActor,
39
+ grantCovers,
39
40
  hasRole,
40
41
  hasScope,
41
42
  isActorKind,
@@ -6,8 +6,8 @@
6
6
  * One method, positional parameters. **`Bun.sql` does not satisfy it** — `Bun.sql.query` is
7
7
  * `undefined`; it is a tagged template whose positional form is `unsafe`, so `{ executor: Bun.sql }`
8
8
  * would `TypeError` on the first statement. What satisfies it is a client that already speaks
9
- * `(text, values)`, wrapped in one line — `@ultimat3/cli`'s `pgExecutorFor(client)` over
10
- * `@ultimat3/db`'s `DbClient.query({ text, values })` is the framework's own — or a transaction
9
+ * `(text, values)` — `@ultimat3/db`'s `dbExecutor()` over `DbClient.query({ text, values })` is
10
+ * the framework's own, and its one builder — or a transaction
11
11
  * handle, which is a client on its own connection. It answers rows, never a command tag.
12
12
  */
13
13
  export interface PgExecutor {