@konneal/engine 0.2.21 → 0.2.23
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/ask-U3DLB37U.js +12 -0
- package/dist/{chunk-RESCBSX6.js → chunk-434NRPSS.js} +1 -1
- package/dist/{chunk-BV7IKH3N.js → chunk-FYGJDJPN.js} +2 -2
- package/dist/{chunk-EE6V6X6D.js → chunk-GZZWHWGM.js} +6 -6
- package/dist/chunk-HXWDVAVN.js +24441 -0
- package/dist/{chunk-YTWPZE5O.js → chunk-HYI32HMI.js} +0 -32
- package/dist/{chunk-COKARI7O.js → chunk-IOZAZA7Y.js} +2 -2
- package/dist/{chunk-SLZ4TZNO.js → chunk-PZLYPUYJ.js} +4 -4
- package/dist/{chunk-FNJ457SE.js → chunk-RZ3UZVRR.js} +1 -1
- package/dist/livedata.d.ts +3 -4
- package/dist/oidc-refresh.d.ts +19 -0
- package/dist/oidc.d.ts +1 -0
- package/dist/refresh-core.d.ts +28 -0
- package/dist/search-OJ776DMH.js +12 -0
- package/dist/worker_mcp/src/index.js +3 -3
- package/dist/worker_public/src/config.js +2 -2
- package/dist/worker_public/src/index.js +9 -9
- package/dist/worker_public/src/profile.js +1 -1
- package/dist/worker_public/src/refusal.js +2 -2
- package/dist/worker_public/src/requestScope.js +3 -3
- package/package.json +6 -7
- package/workers/worker_public/src/auth.ts +45 -10
- package/workers/worker_public/src/codecs.ts +69 -15
- package/workers/worker_public/src/livedata.ts +35 -0
- package/workers/worker_public/src/oidc-refresh.ts +76 -0
- package/workers/worker_public/src/oidc.ts +1 -0
- package/workers/worker_public/src/refresh-core.ts +67 -0
- package/dist/ask-36JS3555.js +0 -12
- package/dist/chunk-DGFJUA2C.js +0 -2816
- package/dist/search-YA6TD6QJ.js +0 -12
|
@@ -1,33 +1,3 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
-
try {
|
|
9
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
-
} catch (e) {
|
|
11
|
-
throw mod = 0, e;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
var __copyProps = (to, from, except, desc) => {
|
|
15
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
-
for (let key of __getOwnPropNames(from))
|
|
17
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
-
}
|
|
20
|
-
return to;
|
|
21
|
-
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
-
mod
|
|
29
|
-
));
|
|
30
|
-
|
|
31
1
|
// workers/worker_public/src/profile.gen.ts
|
|
32
2
|
var PROFILE = {
|
|
33
3
|
"publisher": {
|
|
@@ -176,8 +146,6 @@ function P() {
|
|
|
176
146
|
}
|
|
177
147
|
|
|
178
148
|
export {
|
|
179
|
-
__commonJS,
|
|
180
|
-
__toESM,
|
|
181
149
|
setProfile,
|
|
182
150
|
P
|
|
183
151
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DATASETS,
|
|
3
3
|
datasetAllowed
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-434NRPSS.js";
|
|
5
5
|
import {
|
|
6
6
|
P
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HYI32HMI.js";
|
|
8
8
|
|
|
9
9
|
// workers/worker_public/src/requestScope.ts
|
|
10
10
|
function licenseDeclared() {
|
|
@@ -7,24 +7,24 @@ import {
|
|
|
7
7
|
sessionFrom,
|
|
8
8
|
telemetry,
|
|
9
9
|
understandQuery
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HXWDVAVN.js";
|
|
11
11
|
import {
|
|
12
12
|
corsHeaders,
|
|
13
13
|
err,
|
|
14
14
|
json,
|
|
15
15
|
readJson,
|
|
16
16
|
validateQuery
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-FYGJDJPN.js";
|
|
18
18
|
import {
|
|
19
19
|
entitlementScope,
|
|
20
20
|
standardKeysFrom
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-IOZAZA7Y.js";
|
|
22
22
|
import {
|
|
23
23
|
LIMITS,
|
|
24
24
|
MODELS,
|
|
25
25
|
num,
|
|
26
26
|
sha256Hex
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-434NRPSS.js";
|
|
28
28
|
|
|
29
29
|
// workers/worker_public/src/search.ts
|
|
30
30
|
async function handleSearch(env, ctx, req, tier, key) {
|
package/dist/livedata.d.ts
CHANGED
|
@@ -12,10 +12,9 @@ export interface LiveDataConfig {
|
|
|
12
12
|
clientSecret?: string;
|
|
13
13
|
}
|
|
14
14
|
export declare function liveDataConfig(env: any): LiveDataConfig | null;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* the window. */
|
|
15
|
+
export declare function retainRefreshToken(env: any, sessionRaw: string, refreshToken: string): Promise<void>;
|
|
16
|
+
export declare function readRefreshToken(env: any, sessionRaw: string): Promise<string | null>;
|
|
17
|
+
export declare function dropRefreshToken(env: any, sessionRaw: string): Promise<void>;
|
|
19
18
|
export declare function retainOpAccessToken(env: any, sessionRaw: string, opAccessToken: string, expiresInSec: number): Promise<void>;
|
|
20
19
|
/** Drop the retained subject + the cached exchanged token (the sign-out
|
|
21
20
|
* closes the window deliberately, never by expiry alone). */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SessionClaims } from "./session";
|
|
2
|
+
export interface RenewedSession {
|
|
3
|
+
claims: Omit<SessionClaims, "iat" | "exp">;
|
|
4
|
+
refreshToken: string;
|
|
5
|
+
}
|
|
6
|
+
export type RenewOutcome = {
|
|
7
|
+
kind: "ok";
|
|
8
|
+
renewed: RenewedSession;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "revoked";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "unavailable";
|
|
13
|
+
};
|
|
14
|
+
export declare function renewSessionClaims(env: any, cfg: {
|
|
15
|
+
issuer: string;
|
|
16
|
+
clientId: string;
|
|
17
|
+
clientSecret?: string;
|
|
18
|
+
sessionSecret: string;
|
|
19
|
+
}, session: SessionClaims, sessionRaw: string): Promise<RenewOutcome>;
|
package/dist/oidc.d.ts
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { OidcIdTokenClaims, OidcTokenResponse } from "./oidc.ts";
|
|
2
|
+
import type { SessionClaims } from "./session";
|
|
3
|
+
export interface OidcMetadata {
|
|
4
|
+
token_endpoint: string;
|
|
5
|
+
jwks_uri: string;
|
|
6
|
+
}
|
|
7
|
+
export type RefreshResult = {
|
|
8
|
+
kind: "ok";
|
|
9
|
+
token: OidcTokenResponse;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "refused";
|
|
12
|
+
} | {
|
|
13
|
+
kind: "unavailable";
|
|
14
|
+
};
|
|
15
|
+
/** The refresh grant's backchannel: the same shape as the code
|
|
16
|
+
* exchange's POST (the OP's WAF wants the issuer's own origin), with
|
|
17
|
+
* the three outcomes the renewal policy needs — a 400 is the OP
|
|
18
|
+
* REFUSING (revocation, expiry: dead), a network throw is
|
|
19
|
+
* unavailability (keep the session, retry next load). */
|
|
20
|
+
export declare function refreshGrant(meta: OidcMetadata, cfg: {
|
|
21
|
+
clientId: string;
|
|
22
|
+
clientSecret?: string;
|
|
23
|
+
}, refreshToken: string): Promise<RefreshResult>;
|
|
24
|
+
/** The fresh session claims from the refresh grant's ID token. The sub
|
|
25
|
+
* MUST match (a different account is a provisioning error, never an
|
|
26
|
+
* update); every profile field falls back to the standing session's so
|
|
27
|
+
* a claim the OP omits never erases what the user already had. */
|
|
28
|
+
export declare function renewedSessionClaims(session: SessionClaims, claims: OidcIdTokenClaims): Omit<SessionClaims, "iat" | "exp"> | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleSearch
|
|
3
|
+
} from "./chunk-PZLYPUYJ.js";
|
|
4
|
+
import "./chunk-HXWDVAVN.js";
|
|
5
|
+
import "./chunk-FYGJDJPN.js";
|
|
6
|
+
import "./chunk-RZ3UZVRR.js";
|
|
7
|
+
import "./chunk-IOZAZA7Y.js";
|
|
8
|
+
import "./chunk-434NRPSS.js";
|
|
9
|
+
import "./chunk-HYI32HMI.js";
|
|
10
|
+
export {
|
|
11
|
+
handleSearch
|
|
12
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
authenticate
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-
|
|
3
|
+
} from "../../chunk-FYGJDJPN.js";
|
|
4
|
+
import "../../chunk-434NRPSS.js";
|
|
5
5
|
import {
|
|
6
6
|
P,
|
|
7
7
|
setProfile
|
|
8
|
-
} from "../../chunk-
|
|
8
|
+
} from "../../chunk-HYI32HMI.js";
|
|
9
9
|
|
|
10
10
|
// workers/worker_mcp/src/index.ts
|
|
11
11
|
var PROTOCOL_VERSION = "2025-06-18";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleSearch
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-PZLYPUYJ.js";
|
|
4
4
|
import {
|
|
5
5
|
checkQuoteAnchors,
|
|
6
6
|
handleAsk,
|
|
7
7
|
handleMemories,
|
|
8
8
|
scoreJudge,
|
|
9
9
|
standardForDocNumber
|
|
10
|
-
} from "../../chunk-
|
|
10
|
+
} from "../../chunk-GZZWHWGM.js";
|
|
11
11
|
import {
|
|
12
12
|
buildMessages,
|
|
13
13
|
citations,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
sessionFrom,
|
|
30
30
|
telemetry,
|
|
31
31
|
understandQuery
|
|
32
|
-
} from "../../chunk-
|
|
32
|
+
} from "../../chunk-HXWDVAVN.js";
|
|
33
33
|
import {
|
|
34
34
|
authenticate,
|
|
35
35
|
corsHeaders,
|
|
@@ -38,14 +38,14 @@ import {
|
|
|
38
38
|
readJson,
|
|
39
39
|
validateQuery,
|
|
40
40
|
withCors
|
|
41
|
-
} from "../../chunk-
|
|
41
|
+
} from "../../chunk-FYGJDJPN.js";
|
|
42
42
|
import {
|
|
43
43
|
canonicalRefusal
|
|
44
|
-
} from "../../chunk-
|
|
44
|
+
} from "../../chunk-RZ3UZVRR.js";
|
|
45
45
|
import {
|
|
46
46
|
entitlementScope,
|
|
47
47
|
standardKeysFrom
|
|
48
|
-
} from "../../chunk-
|
|
48
|
+
} from "../../chunk-IOZAZA7Y.js";
|
|
49
49
|
import {
|
|
50
50
|
LIMITS,
|
|
51
51
|
MODELS,
|
|
@@ -56,11 +56,11 @@ import {
|
|
|
56
56
|
roleModel,
|
|
57
57
|
sha256Hex,
|
|
58
58
|
today
|
|
59
|
-
} from "../../chunk-
|
|
59
|
+
} from "../../chunk-434NRPSS.js";
|
|
60
60
|
import {
|
|
61
61
|
P,
|
|
62
62
|
setProfile
|
|
63
|
-
} from "../../chunk-
|
|
63
|
+
} from "../../chunk-HYI32HMI.js";
|
|
64
64
|
|
|
65
65
|
// workers/worker_public/src/conversations.ts
|
|
66
66
|
var ID_RE = /^[a-zA-Z0-9_-]{8,64}$/;
|
|
@@ -979,7 +979,7 @@ async function handleMcp(env, ctx, req, tier, key) {
|
|
|
979
979
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
980
980
|
body: JSON.stringify({ ...args, stream: false })
|
|
981
981
|
});
|
|
982
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
982
|
+
const res = name === "ask" ? await (await import("../../ask-U3DLB37U.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-OJ776DMH.js")).handleSearch(env, ctx, inner, tier, key);
|
|
983
983
|
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
984
984
|
});
|
|
985
985
|
if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
requestSalt,
|
|
5
5
|
resolveRequestScope,
|
|
6
6
|
standardKeysFrom
|
|
7
|
-
} from "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
9
|
-
import "../../chunk-
|
|
7
|
+
} from "../../chunk-IOZAZA7Y.js";
|
|
8
|
+
import "../../chunk-434NRPSS.js";
|
|
9
|
+
import "../../chunk-HYI32HMI.js";
|
|
10
10
|
export {
|
|
11
11
|
entitlementScope,
|
|
12
12
|
licenseDeclared,
|
package/package.json
CHANGED
|
@@ -16,19 +16,18 @@
|
|
|
16
16
|
"gen:profile": "node scripts/gen_profile.mjs"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
+
"@astrojs/markdown-satteri": "^0.4.1",
|
|
20
|
+
"@astrojs/mdx": "^4.3.14",
|
|
19
21
|
"@cloudflare/workers-types": "^5.20260911.1",
|
|
22
|
+
"@konneal/client": "^0.2.1",
|
|
23
|
+
"@pubid/pubid": "^0.1.5",
|
|
20
24
|
"esbuild": "^0.28.2",
|
|
21
25
|
"jsdom": "^29.1.1",
|
|
22
26
|
"openapi-typescript": "^7.13.0",
|
|
23
27
|
"playwright": "^1.62.1",
|
|
24
|
-
"yaml": "^2.9.1"
|
|
25
|
-
"@astrojs/markdown-satteri": "^0.4.1",
|
|
26
|
-
"@astrojs/mdx": "^4.3.14",
|
|
27
|
-
"@konneal/client": "^0.2.1",
|
|
28
|
-
"@oimlsmart/oiml-pubid": "^1.2.1"
|
|
28
|
+
"yaml": "^2.9.1"
|
|
29
29
|
},
|
|
30
|
-
"
|
|
31
|
-
"version": "0.2.21",
|
|
30
|
+
"version": "0.2.23",
|
|
32
31
|
"description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
|
|
33
32
|
"license": "BSD-3-Clause",
|
|
34
33
|
"type": "module",
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
OidcError,
|
|
15
15
|
} from "./oidc";
|
|
16
16
|
import { clearSessionCookie, mintSessionCookie, mintSessionToken, rawSessionToken, readSession, sessionCookieFromToken, SessionClaims } from "./session";
|
|
17
|
+
import { renewSessionClaims } from "./oidc-refresh";
|
|
18
|
+
import { retainRefreshToken } from "./livedata";
|
|
17
19
|
import { bubbleConfirmPage, isAllowedBubbleOrigin } from "./bubble";
|
|
18
20
|
import { dropOpAccessToken, retainOpAccessToken } from "./livedata";
|
|
19
21
|
|
|
@@ -47,6 +49,9 @@ export interface AuthConfig {
|
|
|
47
49
|
sessionSecret: string;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
// how long a session serves before renewal re-judges it against the OP
|
|
53
|
+
const RENEW_AFTER_MS = 60 * 60 * 1000;
|
|
54
|
+
|
|
50
55
|
function authConfig(env: any): AuthConfig | null {
|
|
51
56
|
const issuer = (env.OIDC_ISSUER ?? "").trim().replace(/\/$/, "");
|
|
52
57
|
const clientId = env.OIDC_CLIENT_ID;
|
|
@@ -89,7 +94,7 @@ export async function handleLogin(env: any, req: Request): Promise<Response> {
|
|
|
89
94
|
const url = buildAuthorizationUrl(meta, {
|
|
90
95
|
clientId: cfg.clientId,
|
|
91
96
|
redirectUri: cfg.redirectUri,
|
|
92
|
-
scopes: "openid profile email roles",
|
|
97
|
+
scopes: "openid profile email roles offline_access",
|
|
93
98
|
state,
|
|
94
99
|
nonce,
|
|
95
100
|
codeChallenge: pkce.challenge,
|
|
@@ -181,6 +186,11 @@ export async function handleCallback(env: any, req: Request): Promise<Response>
|
|
|
181
186
|
if (typeof token.access_token === "string" && typeof token.expires_in === "number") {
|
|
182
187
|
await retainOpAccessToken(env, session.token, token.access_token, token.expires_in);
|
|
183
188
|
}
|
|
189
|
+
// the session's lifeline (oidc-refresh.ts): the OP's refresh token
|
|
190
|
+
// is what makes renewal a RE-JUDGMENT instead of a re-stamp
|
|
191
|
+
if (typeof token.refresh_token === "string" && token.refresh_token) {
|
|
192
|
+
await retainRefreshToken(env, session.token, token.refresh_token);
|
|
193
|
+
}
|
|
184
194
|
// Bubble bridge: hand the session to the embedded panel as a Bearer
|
|
185
195
|
// token via the confirm page — postMessage to the validated origin
|
|
186
196
|
// ONLY, and only on the user's explicit click (bubble.ts). The
|
|
@@ -211,9 +221,34 @@ export async function handleMe(env: any, req: Request): Promise<Response> {
|
|
|
211
221
|
const cfg = authConfig(env);
|
|
212
222
|
const session = cfg ? await readSession(req, cfg.sessionSecret) : null;
|
|
213
223
|
const headers: Record<string, string> = { "content-type": "application/json" };
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
|
|
224
|
+
// Renewal is a RE-JUDGMENT, not a re-stamp (oidc-refresh.ts): a
|
|
225
|
+
// session older than the threshold presents the OP's refresh token,
|
|
226
|
+
// whose grant re-judges the live standing — a deactivated account's
|
|
227
|
+
// grant family is dead, the OP refuses, and the session ENDS here
|
|
228
|
+
// instead of silently serving stale claims for a week. A session with
|
|
229
|
+
// no stored refresh token (pre-offline_access logins) falls back to
|
|
230
|
+
// the legacy re-stamp; a network refusal keeps the session and
|
|
231
|
+
// retries on the next load.
|
|
232
|
+
let effective: SessionClaims | null = session;
|
|
233
|
+
if (session && cfg && Date.now() - session.iat > RENEW_AFTER_MS) {
|
|
234
|
+
const raw = rawSessionToken(req);
|
|
235
|
+
if (raw) {
|
|
236
|
+
const renewed = await renewSessionClaims(env, { issuer: cfg.issuer, clientId: cfg.clientId, clientSecret: env.OIDC_CLIENT_SECRET, sessionSecret: cfg.sessionSecret }, session, raw);
|
|
237
|
+
if (renewed.kind === "ok") {
|
|
238
|
+
const freshClaims = renewed.renewed.claims;
|
|
239
|
+
const minted = await mintSessionToken(cfg.sessionSecret, freshClaims);
|
|
240
|
+
effective = { ...freshClaims, iat: Date.now(), exp: minted.expiresAt };
|
|
241
|
+
headers["set-cookie"] = sessionCookieFromToken(minted.token);
|
|
242
|
+
} else if (renewed.kind === "revoked") {
|
|
243
|
+
effective = null;
|
|
244
|
+
headers["set-cookie"] = clearSessionCookie();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (effective && cfg && session && effective === session && Date.now() - session.iat > 24 * 3600 * 1000) {
|
|
249
|
+
// the legacy re-stamp: no refresh token available (pre-offline_access
|
|
250
|
+
// sessions) — the claims are what they were, but the 7-day wall still
|
|
251
|
+
// slides for active users
|
|
217
252
|
headers["set-cookie"] = await mintSessionCookie(cfg.sessionSecret, {
|
|
218
253
|
sub: session.sub,
|
|
219
254
|
name: session.name,
|
|
@@ -224,12 +259,12 @@ export async function handleMe(env: any, req: Request): Promise<Response> {
|
|
|
224
259
|
}
|
|
225
260
|
return new Response(
|
|
226
261
|
JSON.stringify({
|
|
227
|
-
authenticated: !!
|
|
228
|
-
name:
|
|
229
|
-
email:
|
|
230
|
-
picture:
|
|
231
|
-
roles:
|
|
232
|
-
tier:
|
|
262
|
+
authenticated: !!effective,
|
|
263
|
+
name: effective?.name ?? null,
|
|
264
|
+
email: effective?.email ?? null,
|
|
265
|
+
picture: effective?.picture ?? null,
|
|
266
|
+
roles: effective?.roles ?? [],
|
|
267
|
+
tier: effective ? "member" : "anon",
|
|
233
268
|
sign_in_available: !!cfg,
|
|
234
269
|
}),
|
|
235
270
|
{ headers },
|
|
@@ -24,14 +24,57 @@ export interface RefCodec {
|
|
|
24
24
|
familyOf(docidentifier: string): string | null;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// The OIML grammar is @
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
27
|
+
// The OIML grammar is the unified pubid grammar (@pubid/pubid's oiml
|
|
28
|
+
// flavor — single-flavor import so the worker bundle carries the OIML
|
|
29
|
+
// grammar and nothing else). This codec's job is the ADAPTATION: the
|
|
30
|
+
// grammar takes the prefixed form and returns null for everything
|
|
31
|
+
// else; our callers also send bare forms ("R 60-1:2021"), the URN
|
|
32
|
+
// provenance shape, and the dual-published print ("ISO
|
|
33
|
+
// 4064-1:2024|OIML R 49-1:2024") whose retrieval spine is its OIML
|
|
34
|
+
// side, whichever side prints first. The estate's former grammar
|
|
35
|
+
// (@oimlsmart/oiml-pubid) retired into this one (pubid/pubid-ts#63).
|
|
36
|
+
import { oimlGrammarImplementation } from "@pubid/pubid/dist/flavors/oiml/implementation.js";
|
|
37
|
+
|
|
38
|
+
const oimlParser = oimlGrammarImplementation();
|
|
39
|
+
|
|
40
|
+
const TYPE_LETTER: Record<string, string> = {
|
|
41
|
+
recommendation: "R", document: "D", basic_publication: "B",
|
|
42
|
+
"basic-publication": "B", guide: "G", expert_report: "E",
|
|
43
|
+
"expert-report": "E", vocabulary: "V", seminar_report: "S",
|
|
44
|
+
"seminar-report": "S",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
interface PubidHash {
|
|
48
|
+
_type?: string;
|
|
49
|
+
number?: string | number;
|
|
50
|
+
part?: string;
|
|
51
|
+
year?: string;
|
|
52
|
+
edition?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The spine of a printed identifier through the unified grammar: the
|
|
56
|
+
* type letter, the normalized number, the part and the edition year.
|
|
57
|
+
* A dual-published print resolves to its OIML member (the caller picks
|
|
58
|
+
* the side); unknown type kinds refuse — never a fabricated letter. */
|
|
59
|
+
function parseOimlSpine(display: string): { doc_number: string; edition?: string; label: string } | null {
|
|
60
|
+
try {
|
|
61
|
+
const h = oimlParser.parse(display.trim()).toHash() as PubidHash;
|
|
62
|
+
if (h.number === undefined) return null;
|
|
63
|
+
const kind = String(h._type ?? "").split(":").pop() ?? "";
|
|
64
|
+
const letter = TYPE_LETTER[kind] ?? "";
|
|
65
|
+
if (!letter) return null;
|
|
66
|
+
const num = String(Number(h.number));
|
|
67
|
+
const part = h.part !== undefined ? String(h.part) : undefined;
|
|
68
|
+
const ed = h.year !== undefined ? String(h.year) : h.edition !== undefined ? String(h.edition) : undefined;
|
|
69
|
+
return {
|
|
70
|
+
doc_number: num,
|
|
71
|
+
...(ed ? { edition: ed } : {}),
|
|
72
|
+
label: `OIML ${letter} ${num}${part ? `-${part}` : ""}${ed ? `:${ed}` : ""}`,
|
|
73
|
+
};
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
35
78
|
|
|
36
79
|
/** urn:oiml:pub:r:60-1:2021 (pub) / urn:oiml:pub:cs:pd-06 (CS) → the
|
|
37
80
|
* prefixed display form the parser takes. */
|
|
@@ -44,8 +87,15 @@ const urnToDisplay = (u: string) => {
|
|
|
44
87
|
};
|
|
45
88
|
|
|
46
89
|
const parsePubid = (doc: string) => {
|
|
90
|
+
// a dual-published print: the OIML side is the spine, whichever side
|
|
91
|
+
// prints first — pick it before the bare-form normalization, which
|
|
92
|
+
// would mis-prefix the co-publisher's side
|
|
93
|
+
if (!/^urn:/i.test(doc) && doc.includes("|")) {
|
|
94
|
+
const side = doc.split("|").map((s) => s.trim()).find((s) => /^(?:OIML|oiml)\b/i.test(s));
|
|
95
|
+
return side ? parseOimlSpine(side) : null;
|
|
96
|
+
}
|
|
47
97
|
const src = /^urn:/i.test(doc) ? urnToDisplay(doc) : /^(?:OIML|oiml)\b/i.test(doc) ? doc : `OIML ${doc}`;
|
|
48
|
-
return src ?
|
|
98
|
+
return src ? parseOimlSpine(src) : null;
|
|
49
99
|
};
|
|
50
100
|
|
|
51
101
|
/** OIML's grammar (delegated): type letter + 1–3 digits, optional part,
|
|
@@ -54,11 +104,12 @@ const parsePubid = (doc: string) => {
|
|
|
54
104
|
export const oimlPubid: RefCodec = {
|
|
55
105
|
parse(doc, edition) {
|
|
56
106
|
const p = parsePubid(doc);
|
|
57
|
-
if (!p
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
107
|
+
if (!p) return null;
|
|
108
|
+
// the caller's edition is bibdata's truth — it wins over the print's
|
|
109
|
+
if (edition && p.edition !== edition) {
|
|
110
|
+
return { ...p, edition, label: p.label.split(":")[0] + `:${edition}` };
|
|
111
|
+
}
|
|
112
|
+
return p;
|
|
62
113
|
},
|
|
63
114
|
scanQuestion(query) {
|
|
64
115
|
const re = /\b(OIML\s+)?([RDBGE])(\s*)0*(\d{1,3})(?:\s*[-–]\s*\d+)?(?:\s*:\s*(\d{4}))?/gi;
|
|
@@ -78,7 +129,10 @@ export const oimlPubid: RefCodec = {
|
|
|
78
129
|
},
|
|
79
130
|
familyOf(di) {
|
|
80
131
|
const p = parsePubid(di);
|
|
81
|
-
if (p
|
|
132
|
+
if (p) {
|
|
133
|
+
const m = /^OIML ([A-Z]+) (\d{1,3})/.exec(p.label);
|
|
134
|
+
return m ? `${m[1]}-${m[2]}` : null;
|
|
135
|
+
}
|
|
82
136
|
const m = /^(?:OIML\s+)?([A-Z])\s?(\d{1,3})(?:[-–]([0-9A-Za-z]+))?/.exec(di);
|
|
83
137
|
return m ? `${m[1]}-${m[2]}` : null;
|
|
84
138
|
},
|
|
@@ -65,6 +65,41 @@ const EXCHANGED_KEY = (sessionHash: string) => `ossx:${sessionHash}`;
|
|
|
65
65
|
* window (the OP token's own TTL, minus a small margin). Called ONCE
|
|
66
66
|
* per sign-in from the auth callback; the token never persists past
|
|
67
67
|
* the window. */
|
|
68
|
+
// The session's refresh-token store (oidc-refresh.ts): the OP's refresh
|
|
69
|
+
// grant is what makes session renewal a RE-JUDGMENT of live standing
|
|
70
|
+
// instead of a re-stamp of frozen claims. Same KV discipline as the
|
|
71
|
+
// access token: keyed by the session token's hash, TTL = the grant's
|
|
72
|
+
// own life, never D1.
|
|
73
|
+
const REFRESH_KEY = (sessionHash: string) => `ort:${sessionHash}`;
|
|
74
|
+
const REFRESH_TTL_SEC = 30 * 24 * 60 * 60; // the OP's grant TTL default
|
|
75
|
+
|
|
76
|
+
export async function retainRefreshToken(env: any, sessionRaw: string, refreshToken: string): Promise<void> {
|
|
77
|
+
try {
|
|
78
|
+
await env.CACHE.put(REFRESH_KEY(await sha256Hex(sessionRaw)), JSON.stringify({ token: refreshToken }), {
|
|
79
|
+
expirationTtl: REFRESH_TTL_SEC,
|
|
80
|
+
});
|
|
81
|
+
} catch {
|
|
82
|
+
// a KV hiccup degrades to the legacy staleness, never blocks sign-in
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function readRefreshToken(env: any, sessionRaw: string): Promise<string | null> {
|
|
87
|
+
try {
|
|
88
|
+
const hit = await env.CACHE.get(REFRESH_KEY(await sha256Hex(sessionRaw)), "json");
|
|
89
|
+
return typeof hit?.token === "string" ? hit.token : null;
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function dropRefreshToken(env: any, sessionRaw: string): Promise<void> {
|
|
96
|
+
try {
|
|
97
|
+
await env.CACHE.delete(REFRESH_KEY(await sha256Hex(sessionRaw)));
|
|
98
|
+
} catch {
|
|
99
|
+
// best effort
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
68
103
|
export async function retainOpAccessToken(
|
|
69
104
|
env: any,
|
|
70
105
|
sessionRaw: string,
|