@hadoku/worker-utils 1.6.20 → 1.7.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 +30 -1
- package/dist/identity.js +18 -5
- package/package.json +1 -1
package/dist/identity.d.ts
CHANGED
|
@@ -161,7 +161,33 @@ interface KeyRegistryRecord {
|
|
|
161
161
|
* SESSIONS_KV binding.
|
|
162
162
|
*/
|
|
163
163
|
repo?: string | null;
|
|
164
|
+
/**
|
|
165
|
+
* The PERSON holding this credential (added 2026-10-31, service grants):
|
|
166
|
+
* their own userId, while `userId` above stays the service identity's. A
|
|
167
|
+
* row with `heldBy` was minted for that person alone, so revoking them
|
|
168
|
+
* retires their rows and leaves every other holder of the identity working.
|
|
169
|
+
* Absent on a row from before grants, and on human sign-in keys.
|
|
170
|
+
*/
|
|
171
|
+
heldBy?: string;
|
|
172
|
+
/**
|
|
173
|
+
* ms epoch after which the key authenticates nothing (added 2026-10-31).
|
|
174
|
+
* Every grant and fleet credential carries one (7 days from mint). Liveness
|
|
175
|
+
* is {@link isLiveKey}, never a bare `retiredAt` read; a sweep later stamps
|
|
176
|
+
* `retiredAt = expiresAt` so bookkeeping readers agree.
|
|
177
|
+
*/
|
|
178
|
+
expiresAt?: number;
|
|
179
|
+
/** How a `heldBy` row was minted: a person under a grant, or fleet automation. */
|
|
180
|
+
via?: 'grant' | 'fleet';
|
|
164
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Does this row authenticate right now? Not retired, and not past `expiresAt`.
|
|
184
|
+
*
|
|
185
|
+
* THE liveness test. A path that decides whether a presented key is accepted
|
|
186
|
+
* must call this, never read `retiredAt` alone: an expired credential carries
|
|
187
|
+
* no `retiredAt` until the sweep reaches it, so a bare read would keep it
|
|
188
|
+
* working for up to a day past its expiry.
|
|
189
|
+
*/
|
|
190
|
+
declare function isLiveKey(record: Pick<Partial<KeyRegistryRecord>, 'retiredAt' | 'expiresAt'> | null | undefined, now?: number): boolean;
|
|
165
191
|
/** The minimum SESSIONS_KV surface this module uses. Read-only, by construction.
|
|
166
192
|
*
|
|
167
193
|
* `list` declares the PAGINATION fields, not just `keys`. Omitting them was not
|
|
@@ -273,6 +299,9 @@ interface RegistryRowMetadata {
|
|
|
273
299
|
repo?: string | null;
|
|
274
300
|
/** Present only as `true`, on a sign-up key (KeyRegistryRecord.internal). */
|
|
275
301
|
internal?: true;
|
|
302
|
+
heldBy?: string;
|
|
303
|
+
expiresAt?: number;
|
|
304
|
+
via?: 'grant' | 'fleet';
|
|
276
305
|
}
|
|
277
306
|
/** Workers KV's ceiling on serialized list metadata, in bytes. */
|
|
278
307
|
declare const REGISTRY_METADATA_MAX_BYTES = 1024;
|
|
@@ -474,4 +503,4 @@ declare function resolveGrantee(env: RegistryEnv, input: {
|
|
|
474
503
|
key?: string;
|
|
475
504
|
}): Promise<IdentityResult>;
|
|
476
505
|
|
|
477
|
-
export { type EdgeIdentityBinding, type EdgeIdentityCall, type IdentityError, type IdentityResult, type KeyRegistryRecord, type KeyTier, MIN_SEARCH_QUERY_LENGTH, NO_REGISTRY, NO_USER_ID, REGISTRY_METADATA_MAX_BYTES, type RegistryEnv, type RegistryKvNamespace, type RegistryRowMetadata, type ResolvedIdentity, answersToName, isIdentityError, isRegistryNameTaken, liveRowsByName, readLiveRows, recordFromRegistryMetadata, registryNameMap, registryRowMetadata, resolveGrantee, resolveGranteeVia, resolveRegistryName, searchRegistryNames };
|
|
506
|
+
export { type EdgeIdentityBinding, type EdgeIdentityCall, type IdentityError, type IdentityResult, type KeyRegistryRecord, type KeyTier, MIN_SEARCH_QUERY_LENGTH, NO_REGISTRY, NO_USER_ID, REGISTRY_METADATA_MAX_BYTES, type RegistryEnv, type RegistryKvNamespace, type RegistryRowMetadata, type ResolvedIdentity, answersToName, isIdentityError, isLiveKey, isRegistryNameTaken, liveRowsByName, readLiveRows, recordFromRegistryMetadata, registryNameMap, registryRowMetadata, resolveGrantee, resolveGranteeVia, resolveRegistryName, searchRegistryNames };
|
package/dist/identity.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
// identity.ts
|
|
2
|
+
function isLiveKey(record, now = Date.now()) {
|
|
3
|
+
if (!record || record.retiredAt) return false;
|
|
4
|
+
return record.expiresAt === void 0 || record.expiresAt > now;
|
|
5
|
+
}
|
|
2
6
|
function isIdentityError(result) {
|
|
3
7
|
return "error" in result;
|
|
4
8
|
}
|
|
@@ -36,6 +40,9 @@ function registryRowMetadata(record) {
|
|
|
36
40
|
if (typeof record.retiredAt === "number") meta.retiredAt = record.retiredAt;
|
|
37
41
|
if (typeof record.repo === "string" || record.repo === null) meta.repo = record.repo;
|
|
38
42
|
if (record.internal === true) meta.internal = true;
|
|
43
|
+
if (typeof record.heldBy === "string") meta.heldBy = record.heldBy;
|
|
44
|
+
if (typeof record.expiresAt === "number") meta.expiresAt = record.expiresAt;
|
|
45
|
+
if (record.via === "grant" || record.via === "fleet") meta.via = record.via;
|
|
39
46
|
const bytes = new TextEncoder().encode(JSON.stringify(meta)).length;
|
|
40
47
|
return bytes <= REGISTRY_METADATA_MAX_BYTES ? meta : void 0;
|
|
41
48
|
}
|
|
@@ -47,8 +54,10 @@ function recordFromRegistryMetadata(raw) {
|
|
|
47
54
|
const out = { name: m.name };
|
|
48
55
|
const str = (k) => m[k] === void 0 || typeof m[k] === "string";
|
|
49
56
|
const num = (k) => m[k] === void 0 || typeof m[k] === "number";
|
|
50
|
-
if (!str("tier") || !str("userId")) return null;
|
|
51
|
-
if (!num("createdAt") || !num("lastSeenAt") || !num("retiredAt")
|
|
57
|
+
if (!str("tier") || !str("userId") || !str("heldBy")) return null;
|
|
58
|
+
if (!num("createdAt") || !num("lastSeenAt") || !num("retiredAt") || !num("expiresAt"))
|
|
59
|
+
return null;
|
|
60
|
+
if (m.via !== void 0 && m.via !== "grant" && m.via !== "fleet") return null;
|
|
52
61
|
if (m.repo !== void 0 && m.repo !== null && typeof m.repo !== "string") return null;
|
|
53
62
|
if (m.internal !== void 0 && m.internal !== true) return null;
|
|
54
63
|
if (m.tier !== void 0) out.tier = m.tier;
|
|
@@ -58,6 +67,9 @@ function recordFromRegistryMetadata(raw) {
|
|
|
58
67
|
if (m.retiredAt !== void 0) out.retiredAt = m.retiredAt;
|
|
59
68
|
if (m.repo !== void 0) out.repo = m.repo;
|
|
60
69
|
if (m.internal === true) out.internal = true;
|
|
70
|
+
if (m.heldBy !== void 0) out.heldBy = m.heldBy;
|
|
71
|
+
if (m.expiresAt !== void 0) out.expiresAt = m.expiresAt;
|
|
72
|
+
if (m.via !== void 0) out.via = m.via;
|
|
61
73
|
return out;
|
|
62
74
|
}
|
|
63
75
|
function registryKv(env) {
|
|
@@ -93,7 +105,7 @@ async function scanRegistry(env) {
|
|
|
93
105
|
async function readLiveRows(env) {
|
|
94
106
|
const rows = [];
|
|
95
107
|
for (const { record } of await scanRegistry(env)) {
|
|
96
|
-
if (record
|
|
108
|
+
if (!isLiveKey(record) || !record.userId) continue;
|
|
97
109
|
rows.push({ userId: record.userId, name: record.name ?? null, tier: record.tier });
|
|
98
110
|
}
|
|
99
111
|
return rows;
|
|
@@ -104,7 +116,7 @@ async function isRegistryNameTaken(env, name, exclude) {
|
|
|
104
116
|
const excludeEntry = `${KEY_PREFIX}${exclude.rawKey}`;
|
|
105
117
|
for (const { entry, record } of await scanRegistry(env)) {
|
|
106
118
|
if (entry === excludeEntry) continue;
|
|
107
|
-
if (record
|
|
119
|
+
if (!isLiveKey(record)) continue;
|
|
108
120
|
if (exclude.userId && record.userId === exclude.userId) continue;
|
|
109
121
|
if (record.name && foldName(record.name) === target) return true;
|
|
110
122
|
}
|
|
@@ -119,7 +131,7 @@ async function resolveRegistryName(env, name) {
|
|
|
119
131
|
const target = foldName(name);
|
|
120
132
|
if (!target) return null;
|
|
121
133
|
for (const { record } of await scanRegistry(env)) {
|
|
122
|
-
if (record
|
|
134
|
+
if (!isLiveKey(record) || !answersToName(record.tier)) continue;
|
|
123
135
|
if (record.name && foldName(record.name) === target) {
|
|
124
136
|
return { name: record.name, userId: record.userId ?? null, tier: record.tier };
|
|
125
137
|
}
|
|
@@ -226,6 +238,7 @@ export {
|
|
|
226
238
|
REGISTRY_METADATA_MAX_BYTES,
|
|
227
239
|
answersToName,
|
|
228
240
|
isIdentityError,
|
|
241
|
+
isLiveKey,
|
|
229
242
|
isRegistryNameTaken,
|
|
230
243
|
liveRowsByName,
|
|
231
244
|
readLiveRows,
|