@ryusenkei/maezae 0.1.0 → 0.3.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 +14 -0
- package/dist/analytics.d.ts +1 -0
- package/dist/analytics.js +33 -0
- package/dist/body.d.ts +1 -24
- package/dist/body.js +40 -238
- package/dist/declared-9_jI3JXH.js +336 -0
- package/dist/delivery.d.ts +1 -211
- package/dist/integrations.d.ts +1 -0
- package/dist/integrations.js +16 -0
- package/dist/model.d.ts +1 -2
- package/dist/model.js +676 -1
- package/dist/preview.d.ts +1 -44
- package/dist/preview.js +1 -103
- package/dist/src-Baj-EC2G.js +205 -0
- package/dist/src-BrLwIWaT.js +7 -0
- package/dist/trace.d.ts +1 -0
- package/dist/trace.js +1 -0
- package/dist/types/access/src/index.d.ts +5 -0
- package/dist/types/access/src/jwt.d.ts +42 -0
- package/dist/types/access/src/oidc.d.ts +47 -0
- package/dist/types/access/src/signed.d.ts +47 -0
- package/dist/types/access/src/span.d.ts +23 -0
- package/dist/types/access/src/traceparent.d.ts +21 -0
- package/dist/types/analytics/src/day.d.ts +2 -0
- package/dist/types/analytics/src/index.d.ts +4 -0
- package/dist/types/analytics/src/record.d.ts +22 -0
- package/dist/types/analytics/src/schedule.d.ts +2 -0
- package/dist/types/analytics/src/source.d.ts +33 -0
- package/dist/types/analytics/src/store.d.ts +24 -0
- package/dist/types/body/src/components.d.ts +25 -0
- package/dist/types/body/src/declared.d.ts +34 -0
- package/dist/types/body/src/derive.d.ts +13 -0
- package/dist/types/body/src/description.d.ts +8 -0
- package/dist/types/body/src/href.d.ts +4 -0
- package/dist/types/body/src/index.d.ts +11 -0
- package/dist/types/body/src/library.d.ts +94 -0
- package/dist/types/body/src/markdown.d.ts +14 -0
- package/dist/types/body/src/normalize.d.ts +7 -0
- package/dist/types/body/src/profile.d.ts +48 -0
- package/dist/types/body/src/schema.d.ts +6 -0
- package/dist/types/body/src/standard.d.ts +20 -0
- package/dist/types/body/src/types.d.ts +91 -0
- package/dist/types/cms-contract/src/apiKeys.d.ts +13 -0
- package/dist/types/cms-contract/src/assets.d.ts +10 -0
- package/dist/types/cms-contract/src/builtins.d.ts +17 -0
- package/dist/types/cms-contract/src/embeds.d.ts +56 -0
- package/dist/types/cms-contract/src/errorLabels.d.ts +22 -0
- package/dist/types/cms-contract/src/errors.d.ts +97 -0
- package/dist/types/cms-contract/src/extensions.d.ts +75 -0
- package/dist/types/cms-contract/src/fieldRules.d.ts +37 -0
- package/dist/types/cms-contract/src/functions.d.ts +71 -0
- package/dist/types/cms-contract/src/index.d.ts +19 -0
- package/dist/types/cms-contract/src/labels.d.ts +6 -0
- package/dist/types/cms-contract/src/modules.d.ts +144 -0
- package/dist/types/cms-contract/src/paths.d.ts +6 -0
- package/dist/types/cms-contract/src/pending.d.ts +4 -0
- package/dist/types/cms-contract/src/permissions.d.ts +69 -0
- package/dist/types/cms-contract/src/roles.d.ts +35 -0
- package/dist/types/cms-contract/src/settings.d.ts +19 -0
- package/dist/types/cms-contract/src/transfer.d.ts +48 -0
- package/dist/types/cms-contract/src/views.d.ts +21 -0
- package/dist/types/cms-contract/src/webhooks.d.ts +45 -0
- package/dist/types/delivery-client/src/client.d.ts +62 -0
- package/dist/types/delivery-client/src/index.d.ts +5 -0
- package/dist/types/delivery-client/src/media.d.ts +12 -0
- package/dist/types/delivery-client/src/query.d.ts +68 -0
- package/dist/types/delivery-client/src/schema.d.ts +19 -0
- package/dist/types/delivery-client/src/values.d.ts +63 -0
- package/dist/types/model/src/asset.d.ts +89 -0
- package/dist/types/model/src/capabilities.d.ts +20 -0
- package/dist/types/model/src/changes.d.ts +23 -0
- package/dist/types/model/src/check.d.ts +10 -0
- package/dist/types/model/src/image.d.ts +38 -0
- package/dist/types/model/src/index.d.ts +14 -0
- package/dist/types/model/src/input.d.ts +26 -0
- package/dist/types/model/src/media.d.ts +5 -0
- package/dist/types/model/src/model.d.ts +190 -0
- package/dist/types/model/src/page.d.ts +47 -0
- package/dist/types/model/src/rules.d.ts +15 -0
- package/dist/types/model/src/schema.d.ts +3 -0
- package/dist/types/model/src/screens.d.ts +72 -0
- package/dist/types/model/src/time.d.ts +25 -0
- package/dist/types/model/src/values.d.ts +24 -0
- package/dist/types/sdk/src/analytics.d.ts +2 -0
- package/dist/types/sdk/src/body.d.ts +2 -0
- package/dist/types/sdk/src/defaults.d.ts +5 -0
- package/dist/types/sdk/src/delivery.d.ts +2 -0
- package/dist/types/sdk/src/integrations.d.ts +4 -0
- package/dist/types/sdk/src/model.d.ts +5 -0
- package/dist/types/sdk/src/preview.d.ts +2 -0
- package/dist/types/sdk/src/trace.d.ts +2 -0
- package/package.json +14 -2
- package/dist/index-57nirXxn.d.ts +0 -335
- package/dist/index-D11fUsoi.d.ts +0 -131
package/dist/preview.d.ts
CHANGED
|
@@ -1,44 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const SIGNED_HEADERS: {
|
|
3
|
-
/** MCP → CMS の署名(鍵は MCP_TOKEN) */
|
|
4
|
-
readonly mcp: "x-mcp-signature";
|
|
5
|
-
/** MCP → CMS のログインのメールアドレス(署名の主体) */
|
|
6
|
-
readonly mcpEmail: "x-mcp-email";
|
|
7
|
-
/** CMS ⇄ サイトのプレビューと下書きの署名(鍵は PREVIEW_TOKEN) */
|
|
8
|
-
readonly preview: "x-preview-signature";
|
|
9
|
-
/** CMS → 外の送信先の Webhook・外の関数の署名(鍵は登録ごと、受け手は verifyParts と同じ手順で照合) */
|
|
10
|
-
readonly outbound: "x-cms-signature";
|
|
11
|
-
};
|
|
12
|
-
/** 署名の対象(body は本文、なしは空) */
|
|
13
|
-
type SignedParts = Readonly<{
|
|
14
|
-
method: string;
|
|
15
|
-
url: string;
|
|
16
|
-
body?: string | ArrayBuffer | Uint8Array | null;
|
|
17
|
-
/** 主体(MCP のログインのメールアドレスなど、なしは空) */
|
|
18
|
-
subject?: string | null;
|
|
19
|
-
}>;
|
|
20
|
-
/** 署名のヘッダーの値(t=<秒>,v1=<署名>) */
|
|
21
|
-
export declare function signParts(secret: string, parts: SignedParts, now?: number): Promise<string>;
|
|
22
|
-
/** 署名の照合(形の誤り・時刻のずれの超過・不一致は false) */
|
|
23
|
-
export declare function verifyParts(secret: string, header: string | null | undefined, parts: SignedParts, { now, maxSkewSec }?: {
|
|
24
|
-
now?: number;
|
|
25
|
-
maxSkewSec?: number;
|
|
26
|
-
}): Promise<boolean>;
|
|
27
|
-
/** 署名を付けたヘッダー(init のヘッダーに署名と主体を追加した新しい Headers) */
|
|
28
|
-
export declare function signedHeaders(secret: string, { method, url, body, subject }: SignedParts, { header, subjectHeader, init, now }: Readonly<{
|
|
29
|
-
header: string;
|
|
30
|
-
subjectHeader?: string;
|
|
31
|
-
init?: HeadersInit;
|
|
32
|
-
now?: number;
|
|
33
|
-
}>): Promise<Headers>;
|
|
34
|
-
/** 署名つきのリクエストを照合する設定 */
|
|
35
|
-
type VerifyRequestOptions = Readonly<{
|
|
36
|
-
/** 署名のヘッダーの名前 */
|
|
37
|
-
header: string;
|
|
38
|
-
/** 主体のヘッダーの名前(なしは主体なしで照合) */
|
|
39
|
-
subjectHeader?: string;
|
|
40
|
-
now?: number;
|
|
41
|
-
}>;
|
|
42
|
-
/** リクエストの署名の照合(本文は複製から読み込み、元のリクエストの本文は残す) */
|
|
43
|
-
export declare function verifyRequest(secret: string | undefined, request: Request, { header, subjectHeader, now }: VerifyRequestOptions): Promise<boolean>;
|
|
44
|
-
export type { SignedParts, VerifyRequestOptions };
|
|
1
|
+
export * from './types/sdk/src/preview.js';
|
package/dist/preview.js
CHANGED
|
@@ -1,103 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const CERTS_TTL_MS = 3e5;
|
|
3
|
-
const REFRESH_INTERVAL_MS = 3e4;
|
|
4
|
-
/** 公開鍵の一覧を保持する取得(取得の失敗は保持しない) */
|
|
5
|
-
function cachedCerts(fetcher, clock = Date.now) {
|
|
6
|
-
const cache = /* @__PURE__ */ new Map();
|
|
7
|
-
return async (url, refresh = false) => {
|
|
8
|
-
const hit = cache.get(url);
|
|
9
|
-
const age = hit ? clock() - hit.at : Infinity;
|
|
10
|
-
if (hit && age < (refresh ? REFRESH_INTERVAL_MS : CERTS_TTL_MS)) return hit;
|
|
11
|
-
const fresh = await fetcher(url);
|
|
12
|
-
cache.set(url, {
|
|
13
|
-
keys: fresh.keys,
|
|
14
|
-
at: clock()
|
|
15
|
-
});
|
|
16
|
-
return fresh;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
cachedCerts(async (url) => {
|
|
20
|
-
const res = await fetch(url);
|
|
21
|
-
if (!res.ok) throw new Error(`JWKS の取得に失敗(${res.status})`);
|
|
22
|
-
return res.json();
|
|
23
|
-
});
|
|
24
|
-
/** base64url の復号 */
|
|
25
|
-
const decodeBase64Url = (s) => Uint8Array.from(atob(s.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(s.length / 4) * 4, "=")), (c) => c.charCodeAt(0));
|
|
26
|
-
// ../access/src/signed.ts
|
|
27
|
-
const enc = new TextEncoder();
|
|
28
|
-
/** 既定で許す時刻のずれ(秒) */
|
|
29
|
-
const MAX_SKEW_SEC = 300;
|
|
30
|
-
/** 経路ごとのヘッダーの名前(署名と主体) */
|
|
31
|
-
const SIGNED_HEADERS = {
|
|
32
|
-
/** MCP → CMS の署名(鍵は MCP_TOKEN) */
|
|
33
|
-
mcp: "x-mcp-signature",
|
|
34
|
-
/** MCP → CMS のログインのメールアドレス(署名の主体) */
|
|
35
|
-
mcpEmail: "x-mcp-email",
|
|
36
|
-
/** CMS ⇄ サイトのプレビューと下書きの署名(鍵は PREVIEW_TOKEN) */
|
|
37
|
-
preview: "x-preview-signature",
|
|
38
|
-
/** CMS → 外の送信先の Webhook・外の関数の署名(鍵は登録ごと、受け手は verifyParts と同じ手順で照合) */
|
|
39
|
-
outbound: "x-cms-signature"
|
|
40
|
-
};
|
|
41
|
-
const base64Url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
42
|
-
const hmacKey = (secret) => crypto.subtle.importKey("raw", enc.encode(secret), {
|
|
43
|
-
name: "HMAC",
|
|
44
|
-
hash: "SHA-256"
|
|
45
|
-
}, false, ["sign", "verify"]);
|
|
46
|
-
async function messageOf(t, { method, url, body, subject }) {
|
|
47
|
-
const { pathname, search } = new URL(url);
|
|
48
|
-
const bytes = typeof body === "string" ? enc.encode(body) : body ? Uint8Array.from(new Uint8Array(body)) : /* @__PURE__ */ new Uint8Array();
|
|
49
|
-
const digest = base64Url(await crypto.subtle.digest("SHA-256", bytes));
|
|
50
|
-
return enc.encode([
|
|
51
|
-
"v1",
|
|
52
|
-
t,
|
|
53
|
-
method.toUpperCase(),
|
|
54
|
-
`${pathname}${search}`,
|
|
55
|
-
subject ?? "",
|
|
56
|
-
digest
|
|
57
|
-
].join("\n"));
|
|
58
|
-
}
|
|
59
|
-
/** 署名のヘッダーの値(t=<秒>,v1=<署名>) */
|
|
60
|
-
async function signParts(secret, parts, now = Date.now()) {
|
|
61
|
-
const t = Math.floor(now / 1e3);
|
|
62
|
-
const sig = await crypto.subtle.sign("HMAC", await hmacKey(secret), await messageOf(t, parts));
|
|
63
|
-
return `t=${t},v1=${base64Url(sig)}`;
|
|
64
|
-
}
|
|
65
|
-
/** 署名の照合(形の誤り・時刻のずれの超過・不一致は false) */
|
|
66
|
-
async function verifyParts(secret, header, parts, { now = Date.now(), maxSkewSec = MAX_SKEW_SEC } = {}) {
|
|
67
|
-
if (!secret || !header) return false;
|
|
68
|
-
const fields = Object.fromEntries(header.split(",").map((f) => f.trim().split("=", 2)));
|
|
69
|
-
const t = Number(fields.t);
|
|
70
|
-
if (!Number.isInteger(t) || !fields.v1 || Math.abs(now / 1e3 - t) > maxSkewSec) return false;
|
|
71
|
-
try {
|
|
72
|
-
return await crypto.subtle.verify("HMAC", await hmacKey(secret), decodeBase64Url(fields.v1), await messageOf(t, parts));
|
|
73
|
-
} catch {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/** 署名を付けたヘッダー(init のヘッダーに署名と主体を追加した新しい Headers) */
|
|
78
|
-
async function signedHeaders(secret, { method, url, body, subject }, { header, subjectHeader, init, now }) {
|
|
79
|
-
const headers = new Headers(init);
|
|
80
|
-
if (subjectHeader && subject) headers.set(subjectHeader, subject);
|
|
81
|
-
headers.set(header, await signParts(secret, {
|
|
82
|
-
method,
|
|
83
|
-
url,
|
|
84
|
-
body: body ?? null,
|
|
85
|
-
subject: subjectHeader ? subject : null
|
|
86
|
-
}, now));
|
|
87
|
-
return headers;
|
|
88
|
-
}
|
|
89
|
-
/** リクエストの署名の照合(本文は複製から読み込み、元のリクエストの本文は残す) */
|
|
90
|
-
async function verifyRequest(secret, request, { header, subjectHeader, now }) {
|
|
91
|
-
const value = request.headers.get(header);
|
|
92
|
-
if (!secret || !value) return false;
|
|
93
|
-
const body = request.body ? await request.clone().arrayBuffer() : null;
|
|
94
|
-
return verifyParts(secret, value, {
|
|
95
|
-
method: request.method,
|
|
96
|
-
url: request.url,
|
|
97
|
-
body,
|
|
98
|
-
subject: subjectHeader ? request.headers.get(subjectHeader) : null
|
|
99
|
-
}, now === void 0 ? {} : { now });
|
|
100
|
-
}
|
|
101
|
-
"0".repeat(32);
|
|
102
|
-
"0".repeat(16);
|
|
103
|
-
export { SIGNED_HEADERS, signParts, signedHeaders, verifyParts, verifyRequest };
|
|
1
|
+
export { i as SIGNED_HEADERS, o as signParts, s as signedHeaders, c as verifyParts, l as verifyRequest } from "./src-Baj-EC2G.js";
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// ../access/src/jwt.ts
|
|
2
|
+
const CERTS_TTL_MS = 3e5;
|
|
3
|
+
const REFRESH_INTERVAL_MS = 3e4;
|
|
4
|
+
/** 公開鍵の一覧を保持する取得(取得の失敗は保持しない) */
|
|
5
|
+
function cachedCerts(fetcher, clock = Date.now) {
|
|
6
|
+
const cache = /* @__PURE__ */ new Map();
|
|
7
|
+
return async (url, refresh = false) => {
|
|
8
|
+
const hit = cache.get(url);
|
|
9
|
+
const age = hit ? clock() - hit.at : Infinity;
|
|
10
|
+
if (hit && age < (refresh ? REFRESH_INTERVAL_MS : CERTS_TTL_MS)) return hit;
|
|
11
|
+
const fresh = await fetcher(url);
|
|
12
|
+
cache.set(url, {
|
|
13
|
+
keys: fresh.keys,
|
|
14
|
+
at: clock()
|
|
15
|
+
});
|
|
16
|
+
return fresh;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
cachedCerts(async (url) => {
|
|
20
|
+
const res = await fetch(url);
|
|
21
|
+
if (!res.ok) throw new Error(`JWKS の取得に失敗(${res.status})`);
|
|
22
|
+
return res.json();
|
|
23
|
+
});
|
|
24
|
+
/** base64url の復号 */
|
|
25
|
+
const decodeBase64Url = (s) => Uint8Array.from(atob(s.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(s.length / 4) * 4, "=")), (c) => c.charCodeAt(0));
|
|
26
|
+
// ../access/src/signed.ts
|
|
27
|
+
const enc = new TextEncoder();
|
|
28
|
+
/** 既定で許す時刻のずれ(秒) */
|
|
29
|
+
const MAX_SKEW_SEC = 300;
|
|
30
|
+
/** 経路ごとのヘッダーの名前(署名と主体) */
|
|
31
|
+
const SIGNED_HEADERS = {
|
|
32
|
+
/** MCP → CMS の署名(鍵は MCP_TOKEN) */
|
|
33
|
+
mcp: "x-mcp-signature",
|
|
34
|
+
/** MCP → CMS のログインのメールアドレス(署名の主体) */
|
|
35
|
+
mcpEmail: "x-mcp-email",
|
|
36
|
+
/** CMS ⇄ サイトのプレビューと下書きの署名(鍵は PREVIEW_TOKEN) */
|
|
37
|
+
preview: "x-preview-signature",
|
|
38
|
+
/** CMS → 外の送信先の Webhook・外の関数の署名(鍵は登録ごと、受け手は verifyParts と同じ手順で照合) */
|
|
39
|
+
outbound: "x-cms-signature"
|
|
40
|
+
};
|
|
41
|
+
const base64Url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
42
|
+
const hmacKey = (secret) => crypto.subtle.importKey("raw", enc.encode(secret), {
|
|
43
|
+
name: "HMAC",
|
|
44
|
+
hash: "SHA-256"
|
|
45
|
+
}, false, ["sign", "verify"]);
|
|
46
|
+
async function messageOf(t, { method, url, body, subject }) {
|
|
47
|
+
const { pathname, search } = new URL(url);
|
|
48
|
+
const bytes = typeof body === "string" ? enc.encode(body) : body ? Uint8Array.from(new Uint8Array(body)) : /* @__PURE__ */ new Uint8Array();
|
|
49
|
+
const digest = base64Url(await crypto.subtle.digest("SHA-256", bytes));
|
|
50
|
+
return enc.encode([
|
|
51
|
+
"v1",
|
|
52
|
+
t,
|
|
53
|
+
method.toUpperCase(),
|
|
54
|
+
`${pathname}${search}`,
|
|
55
|
+
subject ?? "",
|
|
56
|
+
digest
|
|
57
|
+
].join("\n"));
|
|
58
|
+
}
|
|
59
|
+
/** 署名のヘッダーの値(t=<秒>,v1=<署名>) */
|
|
60
|
+
async function signParts(secret, parts, now = Date.now()) {
|
|
61
|
+
const t = Math.floor(now / 1e3);
|
|
62
|
+
const sig = await crypto.subtle.sign("HMAC", await hmacKey(secret), await messageOf(t, parts));
|
|
63
|
+
return `t=${t},v1=${base64Url(sig)}`;
|
|
64
|
+
}
|
|
65
|
+
/** 署名の照合(形の誤り・時刻のずれの超過・不一致は false) */
|
|
66
|
+
async function verifyParts(secret, header, parts, { now = Date.now(), maxSkewSec = MAX_SKEW_SEC } = {}) {
|
|
67
|
+
if (!secret || !header) return false;
|
|
68
|
+
const fields = Object.fromEntries(header.split(",").map((f) => f.trim().split("=", 2)));
|
|
69
|
+
const t = Number(fields.t);
|
|
70
|
+
if (!Number.isInteger(t) || !fields.v1 || Math.abs(now / 1e3 - t) > maxSkewSec) return false;
|
|
71
|
+
try {
|
|
72
|
+
return await crypto.subtle.verify("HMAC", await hmacKey(secret), decodeBase64Url(fields.v1), await messageOf(t, parts));
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** 署名を付けたヘッダー(init のヘッダーに署名と主体を追加した新しい Headers) */
|
|
78
|
+
async function signedHeaders(secret, { method, url, body, subject }, { header, subjectHeader, init, now }) {
|
|
79
|
+
const headers = new Headers(init);
|
|
80
|
+
if (subjectHeader && subject) headers.set(subjectHeader, subject);
|
|
81
|
+
headers.set(header, await signParts(secret, {
|
|
82
|
+
method,
|
|
83
|
+
url,
|
|
84
|
+
body: body ?? null,
|
|
85
|
+
subject: subjectHeader ? subject : null
|
|
86
|
+
}, now));
|
|
87
|
+
return headers;
|
|
88
|
+
}
|
|
89
|
+
/** リクエストの署名の照合(本文は複製から読み込み、元のリクエストの本文は残す) */
|
|
90
|
+
async function verifyRequest(secret, request, { header, subjectHeader, now }) {
|
|
91
|
+
const value = request.headers.get(header);
|
|
92
|
+
if (!secret || !value) return false;
|
|
93
|
+
const body = request.body ? await request.clone().arrayBuffer() : null;
|
|
94
|
+
return verifyParts(secret, value, {
|
|
95
|
+
method: request.method,
|
|
96
|
+
url: request.url,
|
|
97
|
+
body,
|
|
98
|
+
subject: subjectHeader ? request.headers.get(subjectHeader) : null
|
|
99
|
+
}, now === void 0 ? {} : { now });
|
|
100
|
+
}
|
|
101
|
+
/** 封印した値(署名の不一致・期限切れ・形の誤りは null) */
|
|
102
|
+
async function openValue(secret, token, now = Date.now()) {
|
|
103
|
+
const [body, sig] = token?.split(".") ?? [];
|
|
104
|
+
if (!secret || !body || !sig) return null;
|
|
105
|
+
try {
|
|
106
|
+
if (!await crypto.subtle.verify("HMAC", await hmacKey(secret), decodeBase64Url(sig), enc.encode(body))) return null;
|
|
107
|
+
const { v, exp } = JSON.parse(new TextDecoder().decode(decodeBase64Url(body)));
|
|
108
|
+
return typeof exp === "number" && exp * 1e3 > now ? v : null;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ../access/src/traceparent.ts
|
|
114
|
+
/** traceparent のヘッダーの名前 */
|
|
115
|
+
const TRACEPARENT_HEADER = "traceparent";
|
|
116
|
+
const PATTERN = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})(-.*)?$/;
|
|
117
|
+
const ZERO_TRACE = "0".repeat(32);
|
|
118
|
+
const ZERO_SPAN = "0".repeat(16);
|
|
119
|
+
/** 受け取った traceparent の解釈(なし・形の違反は null) */
|
|
120
|
+
function parseTraceparent(value) {
|
|
121
|
+
const m = value ? PATTERN.exec(value.trim()) : null;
|
|
122
|
+
if (!m) return null;
|
|
123
|
+
const [, version, traceId, spanId, flags, rest] = m;
|
|
124
|
+
if (version === "ff" || version === "00" && rest !== void 0) return null;
|
|
125
|
+
if (traceId === ZERO_TRACE || spanId === ZERO_SPAN) return null;
|
|
126
|
+
return {
|
|
127
|
+
traceId,
|
|
128
|
+
spanId,
|
|
129
|
+
sampled: (parseInt(flags, 16) & 1) === 1
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** traceparent の値(版は00) */
|
|
133
|
+
const formatTraceparent = (ctx) => `00-${ctx.traceId}-${ctx.spanId}-${ctx.sampled ? "01" : "00"}`;
|
|
134
|
+
function randomHexId(bytes) {
|
|
135
|
+
const values = crypto.getRandomValues(new Uint8Array(bytes));
|
|
136
|
+
let hex = "";
|
|
137
|
+
for (const b of values) hex += b.toString(16).padStart(2, "0");
|
|
138
|
+
return hex;
|
|
139
|
+
}
|
|
140
|
+
/** 新しいトレースの ID(32桁) */
|
|
141
|
+
const newTraceId = () => randomHexId(16);
|
|
142
|
+
/** 新しい区間の ID(16桁) */
|
|
143
|
+
const newSpanId = () => randomHexId(8);
|
|
144
|
+
// ../access/src/span.ts
|
|
145
|
+
/** 記録の割合(0〜1、なし・範囲の外・数でない値は1) */
|
|
146
|
+
const sampleRateOf = (value) => {
|
|
147
|
+
const rate = value === void 0 || value.trim() === "" ? NaN : Number(value);
|
|
148
|
+
return rate >= 0 && rate <= 1 ? rate : 1;
|
|
149
|
+
};
|
|
150
|
+
/** エントリポイントの区間の開始(受け取った traceparent の子か、新しいトレースの根) */
|
|
151
|
+
function startEdgeSpan({ name, request, sampleRate = 1, write = (line) => console.info(line) }) {
|
|
152
|
+
const parent = parseTraceparent(request.headers.get(TRACEPARENT_HEADER));
|
|
153
|
+
const context = {
|
|
154
|
+
traceId: parent?.traceId ?? newTraceId(),
|
|
155
|
+
spanId: newSpanId(),
|
|
156
|
+
sampled: parent ? parent.sampled : Math.random() < sampleRate
|
|
157
|
+
};
|
|
158
|
+
const attributes = {
|
|
159
|
+
method: request.method,
|
|
160
|
+
path: new URL(request.url).pathname
|
|
161
|
+
};
|
|
162
|
+
const started = performance.now();
|
|
163
|
+
let ended = false;
|
|
164
|
+
return {
|
|
165
|
+
context,
|
|
166
|
+
traceparent: formatTraceparent(context),
|
|
167
|
+
end(result) {
|
|
168
|
+
if (ended) return;
|
|
169
|
+
ended = true;
|
|
170
|
+
if (!context.sampled) return;
|
|
171
|
+
const status = result === "error" || result >= 500 ? "error" : "ok";
|
|
172
|
+
const durationMs = Math.round((performance.now() - started) * 10) / 10;
|
|
173
|
+
const span = {
|
|
174
|
+
traceId: context.traceId,
|
|
175
|
+
spanId: context.spanId,
|
|
176
|
+
parentSpanId: parent?.spanId ?? null,
|
|
177
|
+
name,
|
|
178
|
+
durationMs,
|
|
179
|
+
status,
|
|
180
|
+
attributes: {
|
|
181
|
+
...attributes,
|
|
182
|
+
...result !== "error" && { status: result }
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
write(JSON.stringify({
|
|
186
|
+
level: "info",
|
|
187
|
+
msg: "span",
|
|
188
|
+
...span
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/** 処理をエントリポイントの区間で包む(応答のステータスか例外で終了、例外は再送出) */
|
|
194
|
+
async function withEdgeSpan(options, handle) {
|
|
195
|
+
const span = startEdgeSpan(options);
|
|
196
|
+
try {
|
|
197
|
+
const res = await handle(span);
|
|
198
|
+
span.end(res.status);
|
|
199
|
+
return res;
|
|
200
|
+
} catch (e) {
|
|
201
|
+
span.end("error");
|
|
202
|
+
throw e;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export { openValue as a, verifyParts as c, SIGNED_HEADERS as i, verifyRequest as l, withEdgeSpan as n, signParts as o, TRACEPARENT_HEADER as r, signedHeaders as s, sampleRateOf as t };
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/sdk/src/trace.js';
|
package/dist/trace.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { r as TRACEPARENT_HEADER, t as sampleRateOf, n as withEdgeSpan } from "./src-Baj-EC2G.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { accessJwtOptions, type FetchCerts, type JwtClaims, type JwtOptions, verifyAccessJwt, verifyJwt } from './jwt.js';
|
|
2
|
+
export { emailOfClaims, type LoginAuth, oidcLogin, type OidcLoginOptions, tokenAuth, type TokenAuthOptions } from './oidc.js';
|
|
3
|
+
export { type EdgeSpan, type EdgeSpanOptions, type EdgeSpanStatus, sampleRateOf, startEdgeSpan, withEdgeSpan } from './span.js';
|
|
4
|
+
export { openValue, sealValue, SIGNED_HEADERS, signedHeaders, signParts, type SignedParts, verifyParts, verifyRequest, type VerifyRequestOptions } from './signed.js';
|
|
5
|
+
export { childTraceContext, formatTraceparent, newSpanId, newTraceId, parseTraceparent, type TraceContext, TRACEPARENT_HEADER } from './traceparent.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type Jwk = JsonWebKey & {
|
|
2
|
+
kid?: string;
|
|
3
|
+
};
|
|
4
|
+
/** 公開鍵の一覧の取得(引数は JWKS の URL、refresh は保持した値を使用せずに再取得、テストで差し替え) */
|
|
5
|
+
export type FetchCerts = (jwksUrl: string, refresh?: boolean) => Promise<{
|
|
6
|
+
keys: Jwk[];
|
|
7
|
+
}>;
|
|
8
|
+
/** JWT のクレーム(検証を通った値) */
|
|
9
|
+
export type JwtClaims = Readonly<Record<string, unknown>>;
|
|
10
|
+
/** JWT の検証の設定 */
|
|
11
|
+
export type JwtOptions = Readonly<{
|
|
12
|
+
/** 発行元(iss と完全に一致) */
|
|
13
|
+
issuer: string;
|
|
14
|
+
/** 宛先(aud のどれかと一致、一覧はどれか1つと一致) */
|
|
15
|
+
audience: string | readonly string[];
|
|
16
|
+
/** 公開鍵の一覧の URL */
|
|
17
|
+
jwksUrl: string;
|
|
18
|
+
now?: number;
|
|
19
|
+
certs?: FetchCerts;
|
|
20
|
+
}>;
|
|
21
|
+
/** 公開鍵の一覧を保持する取得(取得の失敗は保持しない) */
|
|
22
|
+
export declare function cachedCerts(fetcher: FetchCerts, clock?: () => number): FetchCerts;
|
|
23
|
+
/** base64url の復号 */
|
|
24
|
+
export declare const decodeBase64Url: (s: string) => Uint8Array<ArrayBuffer>;
|
|
25
|
+
/** 検証を通った JWT のクレーム(署名・発行元・宛先・期限のどれかが違えば null) */
|
|
26
|
+
export declare function verifyJwt(token: string, { issuer, audience, jwksUrl, now, certs }: JwtOptions): Promise<JwtClaims | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Cloudflare Access の JWT を検証する設定(teamDomain は <チーム>.cloudflareaccess.com、aud はアプリの AUD)
|
|
29
|
+
* - aud: カンマ区切りで複数のアプリ(ホスト名ごとにアプリが分かれる管理画面、どのアプリを通すかは Access がホスト名で判定)
|
|
30
|
+
*/
|
|
31
|
+
export declare const accessJwtOptions: ({ teamDomain, aud }: Readonly<{
|
|
32
|
+
teamDomain: string;
|
|
33
|
+
aud: string;
|
|
34
|
+
}>) => Pick<JwtOptions, "issuer" | "audience" | "jwksUrl">;
|
|
35
|
+
/** Access の検証を通ったログインのメールアドレス、サービストークンは service:<クライアントの ID>(通らなければ null) */
|
|
36
|
+
export declare function verifyAccessJwt(token: string, { teamDomain, aud, now, certs }: {
|
|
37
|
+
teamDomain: string;
|
|
38
|
+
aud: string;
|
|
39
|
+
now?: number;
|
|
40
|
+
certs?: FetchCerts;
|
|
41
|
+
}): Promise<string | null>;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type FetchCerts, type JwtClaims, type JwtOptions } from './jwt.js';
|
|
2
|
+
/** ログイン(Auth のポートと同じ形) */
|
|
3
|
+
export type LoginAuth = {
|
|
4
|
+
emailOf(request: Request): Promise<string | null>;
|
|
5
|
+
/** ログインの入り口の応答(対象外のパスは null) */
|
|
6
|
+
handle?(request: Request): Promise<Response | null>;
|
|
7
|
+
/** ログアウトの応答 */
|
|
8
|
+
logout?(request: Request): Promise<Response>;
|
|
9
|
+
};
|
|
10
|
+
/** クレームのメールアドレス(なし・未検証は null、小文字に統一) */
|
|
11
|
+
export declare function emailOfClaims(claims: JwtClaims | null, claim?: string): string | null;
|
|
12
|
+
/** ヘッダーの JWT で判定するログインの設定 */
|
|
13
|
+
export type TokenAuthOptions = Readonly<{
|
|
14
|
+
issuer: string;
|
|
15
|
+
audience: JwtOptions['audience'];
|
|
16
|
+
/** 公開鍵の一覧の URL(なしは発行元の OIDC の設定から) */
|
|
17
|
+
jwksUrl?: string;
|
|
18
|
+
/** JWT のヘッダー(既定 authorization、authorization は Bearer を除外) */
|
|
19
|
+
header?: string;
|
|
20
|
+
/** メールアドレスのクレーム(既定 email) */
|
|
21
|
+
emailClaim?: string;
|
|
22
|
+
certs?: FetchCerts;
|
|
23
|
+
fetch?: typeof fetch;
|
|
24
|
+
}>;
|
|
25
|
+
/** ヘッダーの JWT を検証するログイン */
|
|
26
|
+
export declare function tokenAuth({ issuer, audience, jwksUrl, header, emailClaim, certs, fetch: fetcher }: TokenAuthOptions): LoginAuth;
|
|
27
|
+
/** アプリ自身が行う OIDC のログインの設定 */
|
|
28
|
+
export type OidcLoginOptions = Readonly<{
|
|
29
|
+
issuer: string;
|
|
30
|
+
clientId: string;
|
|
31
|
+
/** クライアントの秘密の値(公開のクライアントは なし、PKCE だけ) */
|
|
32
|
+
clientSecret?: string;
|
|
33
|
+
/** Cookie の署名の鍵 */
|
|
34
|
+
secret: string;
|
|
35
|
+
/** 入り口のパスの頭(既定 /auth) */
|
|
36
|
+
basePath?: string;
|
|
37
|
+
/** 要求するスコープ(既定 openid email) */
|
|
38
|
+
scope?: string;
|
|
39
|
+
/** セッションの秒数(既定は8時間) */
|
|
40
|
+
sessionTtlSec?: number;
|
|
41
|
+
certs?: FetchCerts;
|
|
42
|
+
fetch?: typeof fetch;
|
|
43
|
+
}>;
|
|
44
|
+
/** 同じオリジンの戻り先(外のオリジンへの転送を防ぐ) */
|
|
45
|
+
export declare const safeNext: (next: string | null) => string;
|
|
46
|
+
/** アプリ自身の OIDC のログイン(認可コードのフローと署名つきの Cookie のセッション) */
|
|
47
|
+
export declare function oidcLogin({ issuer, clientId, clientSecret, secret, basePath, scope, sessionTtlSec, certs, fetch: fetcher }: OidcLoginOptions): LoginAuth;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** 経路ごとのヘッダーの名前(署名と主体) */
|
|
2
|
+
export declare const SIGNED_HEADERS: {
|
|
3
|
+
/** MCP → CMS の署名(鍵は MCP_TOKEN) */
|
|
4
|
+
readonly mcp: "x-mcp-signature";
|
|
5
|
+
/** MCP → CMS のログインのメールアドレス(署名の主体) */
|
|
6
|
+
readonly mcpEmail: "x-mcp-email";
|
|
7
|
+
/** CMS ⇄ サイトのプレビューと下書きの署名(鍵は PREVIEW_TOKEN) */
|
|
8
|
+
readonly preview: "x-preview-signature";
|
|
9
|
+
/** CMS → 外の送信先の Webhook・外の関数の署名(鍵は登録ごと、受け手は verifyParts と同じ手順で照合) */
|
|
10
|
+
readonly outbound: "x-cms-signature";
|
|
11
|
+
};
|
|
12
|
+
/** 署名の対象(body は本文、なしは空) */
|
|
13
|
+
export type SignedParts = Readonly<{
|
|
14
|
+
method: string;
|
|
15
|
+
url: string;
|
|
16
|
+
body?: string | ArrayBuffer | Uint8Array | null;
|
|
17
|
+
/** 主体(MCP のログインのメールアドレスなど、なしは空) */
|
|
18
|
+
subject?: string | null;
|
|
19
|
+
}>;
|
|
20
|
+
/** 署名のヘッダーの値(t=<秒>,v1=<署名>) */
|
|
21
|
+
export declare function signParts(secret: string, parts: SignedParts, now?: number): Promise<string>;
|
|
22
|
+
/** 署名の照合(形の誤り・時刻のずれの超過・不一致は false) */
|
|
23
|
+
export declare function verifyParts(secret: string, header: string | null | undefined, parts: SignedParts, { now, maxSkewSec }?: {
|
|
24
|
+
now?: number;
|
|
25
|
+
maxSkewSec?: number;
|
|
26
|
+
}): Promise<boolean>;
|
|
27
|
+
/** 署名を付けたヘッダー(init のヘッダーに署名と主体を追加した新しい Headers) */
|
|
28
|
+
export declare function signedHeaders(secret: string, { method, url, body, subject }: SignedParts, { header, subjectHeader, init, now }: Readonly<{
|
|
29
|
+
header: string;
|
|
30
|
+
subjectHeader?: string;
|
|
31
|
+
init?: HeadersInit;
|
|
32
|
+
now?: number;
|
|
33
|
+
}>): Promise<Headers>;
|
|
34
|
+
/** 署名つきのリクエストを照合する設定 */
|
|
35
|
+
export type VerifyRequestOptions = Readonly<{
|
|
36
|
+
/** 署名のヘッダーの名前 */
|
|
37
|
+
header: string;
|
|
38
|
+
/** 主体のヘッダーの名前(なしは主体なしで照合) */
|
|
39
|
+
subjectHeader?: string;
|
|
40
|
+
now?: number;
|
|
41
|
+
}>;
|
|
42
|
+
/** リクエストの署名の照合(本文は複製から読み込み、元のリクエストの本文は残す) */
|
|
43
|
+
export declare function verifyRequest(secret: string | undefined, request: Request, { header, subjectHeader, now }: VerifyRequestOptions): Promise<boolean>;
|
|
44
|
+
/** 期限と署名を付けた値(JSON にできる値、ttlSec は有効な秒数) */
|
|
45
|
+
export declare function sealValue(secret: string, value: unknown, ttlSec: number, now?: number): Promise<string>;
|
|
46
|
+
/** 封印した値(署名の不一致・期限切れ・形の誤りは null) */
|
|
47
|
+
export declare function openValue<T = unknown>(secret: string, token: string | null | undefined, now?: number): Promise<T | null>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type TraceContext } from './traceparent.js';
|
|
2
|
+
/** 区間の結果(ok・error、logTracer と同じ値) */
|
|
3
|
+
export type EdgeSpanStatus = 'ok' | 'error';
|
|
4
|
+
/** エントリポイントの区間(context は転送先へ受け渡す自身の区間、traceparent はその値) */
|
|
5
|
+
export type EdgeSpan = Readonly<{
|
|
6
|
+
context: TraceContext;
|
|
7
|
+
traceparent: string;
|
|
8
|
+
/** 応答のステータスの数か例外で終了(500以上・例外は error、2回目以降は無視) */
|
|
9
|
+
end: (result: number | 'error') => void;
|
|
10
|
+
}>;
|
|
11
|
+
/** エントリポイントの区間の引数(write は1行の出力先、既定は console.info) */
|
|
12
|
+
export type EdgeSpanOptions = Readonly<{
|
|
13
|
+
name: string;
|
|
14
|
+
request: Request;
|
|
15
|
+
sampleRate?: number;
|
|
16
|
+
write?: (line: string) => void;
|
|
17
|
+
}>;
|
|
18
|
+
/** 記録の割合(0〜1、なし・範囲の外・数でない値は1) */
|
|
19
|
+
export declare const sampleRateOf: (value: string | undefined) => number;
|
|
20
|
+
/** エントリポイントの区間の開始(受け取った traceparent の子か、新しいトレースの根) */
|
|
21
|
+
export declare function startEdgeSpan({ name, request, sampleRate, write }: EdgeSpanOptions): EdgeSpan;
|
|
22
|
+
/** 処理をエントリポイントの区間で包む(応答のステータスか例外で終了、例外は再送出) */
|
|
23
|
+
export declare function withEdgeSpan(options: EdgeSpanOptions, handle: (span: EdgeSpan) => Promise<Response>): Promise<Response>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** traceparent のヘッダーの名前 */
|
|
2
|
+
export declare const TRACEPARENT_HEADER = "traceparent";
|
|
3
|
+
/** トレースの文脈(traceId は32桁、spanId は呼び出し元の区間の16桁、sampled は記録の対象か) */
|
|
4
|
+
export type TraceContext = Readonly<{
|
|
5
|
+
traceId: string;
|
|
6
|
+
spanId: string;
|
|
7
|
+
sampled: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
/** 受け取った traceparent の解釈(なし・形の違反は null) */
|
|
10
|
+
export declare function parseTraceparent(value: string | null | undefined): TraceContext | null;
|
|
11
|
+
/** traceparent の値(版は00) */
|
|
12
|
+
export declare const formatTraceparent: (ctx: TraceContext) => string;
|
|
13
|
+
/** 新しいトレースの ID(32桁) */
|
|
14
|
+
export declare const newTraceId: () => string;
|
|
15
|
+
/** 新しい区間の ID(16桁) */
|
|
16
|
+
export declare const newSpanId: () => string;
|
|
17
|
+
/**
|
|
18
|
+
* 呼び出し元の区間の文脈(受け取った traceparent があれば同じトレースの子、なければ新しいトレース)
|
|
19
|
+
* - sampled: 受け取った値のフラグ、新しいトレースは引数(既定は記録の対象)
|
|
20
|
+
*/
|
|
21
|
+
export declare function childTraceContext(incoming: string | null | undefined, sampled?: boolean): TraceContext;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { recordView, type Classify, type Dataset } from './record.js';
|
|
2
|
+
export { syncCron } from './schedule.js';
|
|
3
|
+
export { analyticsEngine, type DailyRow, fixedSource, type Source, tenantDaily, type TenantSource } from './source.js';
|
|
4
|
+
export { readDaily, syncDaily } from './store.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Analytics Engine のデータセットの binding(書き込みだけ) */
|
|
2
|
+
export type Dataset = {
|
|
3
|
+
writeDataPoint(point: {
|
|
4
|
+
blobs?: string[];
|
|
5
|
+
doubles?: number[];
|
|
6
|
+
indexes?: string[];
|
|
7
|
+
}): void;
|
|
8
|
+
};
|
|
9
|
+
/** パスから種類と ID への対応(利用側が定義、例: /articles/<id> は記事) */
|
|
10
|
+
export type Classify = (path: string) => {
|
|
11
|
+
kind: string;
|
|
12
|
+
id: string;
|
|
13
|
+
};
|
|
14
|
+
/** 記録の設定(tenant は媒体の識別子) */
|
|
15
|
+
type RecordOptions = {
|
|
16
|
+
classify: Classify;
|
|
17
|
+
timeZone: string;
|
|
18
|
+
tenant?: string;
|
|
19
|
+
};
|
|
20
|
+
/** 閲覧を1件記録(対象: GET で応答が200の HTML・ボット以外、記録の失敗は無視) */
|
|
21
|
+
export declare function recordView(dataset: Dataset | undefined, request: Request, response: Response, { classify, timeZone, tenant }: RecordOptions): void;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** 日ごと・種類・ID の閲覧数 */
|
|
2
|
+
export type DailyRow = {
|
|
3
|
+
date: string;
|
|
4
|
+
kind: string;
|
|
5
|
+
id: string;
|
|
6
|
+
views: number;
|
|
7
|
+
};
|
|
8
|
+
/** 読み込み元の接続の型(since 以降の日付の閲覧数) */
|
|
9
|
+
export type Source = {
|
|
10
|
+
daily(since: string): Promise<DailyRow[]>;
|
|
11
|
+
};
|
|
12
|
+
/** Analytics Engine の接続の設定(token は Account Analytics の読み取りの API トークン、tenant は絞り込む媒体の識別子) */
|
|
13
|
+
type AnalyticsEngineOptions = {
|
|
14
|
+
accountId: string;
|
|
15
|
+
token: string;
|
|
16
|
+
dataset: string;
|
|
17
|
+
tenant?: string;
|
|
18
|
+
};
|
|
19
|
+
/** 1回の問い合わせの行数 */
|
|
20
|
+
export declare const PAGE_ROWS = 10000;
|
|
21
|
+
/** 問い合わせの回数の上限(PAGE_ROWS との積が1回で集計できる行数) */
|
|
22
|
+
export declare const MAX_PAGES = 20;
|
|
23
|
+
/** Analytics Engine の SQL API の読み込み元 */
|
|
24
|
+
export declare function analyticsEngine({ accountId, token, dataset, tenant }: AnalyticsEngineOptions): Source;
|
|
25
|
+
/** 全媒体の読み込み元の型(since 以降の日付の閲覧数を媒体の識別子ごとに) */
|
|
26
|
+
export type TenantSource = {
|
|
27
|
+
dailyByTenant(since: string): Promise<ReadonlyMap<string, DailyRow[]>>;
|
|
28
|
+
};
|
|
29
|
+
/** 全媒体を1回の問い合わせで集計する Analytics Engine の読み込み元(媒体の列が空の記録は除外) */
|
|
30
|
+
export declare function tenantDaily(options: Omit<AnalyticsEngineOptions, 'tenant'>): TenantSource;
|
|
31
|
+
/** 集計済みの行の読み込み元(定期の集計で全媒体の問い合わせ結果を媒体ごとに受け渡し、daily は since 以降の行) */
|
|
32
|
+
export declare const fixedSource: (rows: readonly DailyRow[]) => Source;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DailyRow, Source } from './source.js';
|
|
2
|
+
type Statement = {
|
|
3
|
+
bind(...values: unknown[]): Statement;
|
|
4
|
+
all<T = Record<string, unknown>>(): Promise<{
|
|
5
|
+
results: T[];
|
|
6
|
+
}>;
|
|
7
|
+
};
|
|
8
|
+
/** D1 の接続の型(D1Database と互換) */
|
|
9
|
+
export type Db = {
|
|
10
|
+
prepare(sql: string): Statement;
|
|
11
|
+
batch(statements: Statement[]): Promise<unknown>;
|
|
12
|
+
};
|
|
13
|
+
/** 読み込み元の since 以降の閲覧数を表へ(戻り値は行の数) */
|
|
14
|
+
export declare function syncDaily(source: Source, db: Db, since: string): Promise<number>;
|
|
15
|
+
/** 日付が after より後の閲覧数(live は今日の行を読み込み元から、失敗は表の値のままで failure に例外) */
|
|
16
|
+
export declare function readDaily(db: Db, after: string, live?: {
|
|
17
|
+
source: Source;
|
|
18
|
+
today: string;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
rows: DailyRow[];
|
|
21
|
+
live: boolean;
|
|
22
|
+
failure?: unknown;
|
|
23
|
+
}>;
|
|
24
|
+
export {};
|