@tiangong-lca/cli 0.1.0 → 0.1.2
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 +58 -6
- package/dist/src/batch.d.ts +6 -0
- package/dist/src/batch.js +6 -0
- package/dist/src/batch.js.map +1 -0
- package/dist/src/cli.d.ts +161 -0
- package/dist/src/cli.js +130 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/command-spec.d.ts +90 -0
- package/dist/src/command-spec.js +400 -0
- package/dist/src/command-spec.js.map +1 -0
- package/dist/src/lib/artifacts.d.ts +9 -0
- package/dist/src/lib/auth-identity-receipt.d.ts +160 -0
- package/dist/src/lib/auth-identity-receipt.js +634 -0
- package/dist/src/lib/auth-identity-receipt.js.map +1 -0
- package/dist/src/lib/batch/attempt-recovery.d.ts +12 -0
- package/dist/src/lib/batch/attempt-recovery.js +183 -0
- package/dist/src/lib/batch/attempt-recovery.js.map +1 -0
- package/dist/src/lib/batch/canonical-contracts.d.ts +21 -0
- package/dist/src/lib/batch/canonical-contracts.js +180 -0
- package/dist/src/lib/batch/canonical-contracts.js.map +1 -0
- package/dist/src/lib/batch/engine.d.ts +2 -0
- package/dist/src/lib/batch/engine.js +294 -0
- package/dist/src/lib/batch/engine.js.map +1 -0
- package/dist/src/lib/batch/errors.d.ts +51 -0
- package/dist/src/lib/batch/errors.js +81 -0
- package/dist/src/lib/batch/errors.js.map +1 -0
- package/dist/src/lib/batch/item-projection.d.ts +10 -0
- package/dist/src/lib/batch/item-projection.js +118 -0
- package/dist/src/lib/batch/item-projection.js.map +1 -0
- package/dist/src/lib/batch/run-lock.d.ts +4 -0
- package/dist/src/lib/batch/run-lock.js +155 -0
- package/dist/src/lib/batch/run-lock.js.map +1 -0
- package/dist/src/lib/batch/scheduler-runtime.d.ts +29 -0
- package/dist/src/lib/batch/scheduler-runtime.js +114 -0
- package/dist/src/lib/batch/scheduler-runtime.js.map +1 -0
- package/dist/src/lib/batch/types.d.ts +183 -0
- package/dist/src/lib/batch/types.js +2 -0
- package/dist/src/lib/batch/types.js.map +1 -0
- package/dist/src/lib/dataset-author.d.ts +39 -0
- package/dist/src/lib/dataset-bilingual.d.ts +165 -0
- package/dist/src/lib/dataset-classification.d.ts +279 -0
- package/dist/src/lib/dataset-command.d.ts +53 -0
- package/dist/src/lib/dataset-contract.d.ts +82 -0
- package/dist/src/lib/dataset-curation-queue.d.ts +219 -0
- package/dist/src/lib/dataset-evidence-search.d.ts +134 -0
- package/dist/src/lib/dataset-import-lca.d.ts +136 -0
- package/dist/src/lib/dataset-local.d.ts +23 -0
- package/dist/src/lib/dataset-maintenance-alias-request.d.ts +12 -0
- package/dist/src/lib/dataset-maintenance-alias-rewrite.d.ts +111 -0
- package/dist/src/lib/dataset-maintenance-apply.d.ts +458 -0
- package/dist/src/lib/dataset-maintenance-clear-account.d.ts +161 -0
- package/dist/src/lib/dataset-maintenance-contract.d.ts +317 -0
- package/dist/src/lib/dataset-maintenance-derivatives.d.ts +48 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.d.ts +50 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-capture.d.ts +110 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-command.d.ts +15 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-contract.d.ts +387 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.d.ts +456 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-freeze.d.ts +99 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-plan.d.ts +85 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-recovery.d.ts +268 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-run.d.ts +123 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-seal.d.ts +50 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-verify.d.ts +85 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-wire.d.ts +32 -0
- package/dist/src/lib/dataset-maintenance-pagination.d.ts +58 -0
- package/dist/src/lib/dataset-maintenance-plan.d.ts +40 -0
- package/dist/src/lib/dataset-maintenance-protected-artifacts.d.ts +16 -0
- package/dist/src/lib/dataset-maintenance-protected-before.d.ts +71 -0
- package/dist/src/lib/dataset-maintenance-protected-contract.d.ts +385 -0
- package/dist/src/lib/dataset-maintenance-protected-freeze.d.ts +148 -0
- package/dist/src/lib/dataset-maintenance-protected-preparation.d.ts +76 -0
- package/dist/src/lib/dataset-maintenance-protected-run.d.ts +252 -0
- package/dist/src/lib/dataset-maintenance-protected-seal.d.ts +93 -0
- package/dist/src/lib/dataset-maintenance-protected-toolchain.d.ts +31 -0
- package/dist/src/lib/dataset-maintenance-protected-verify.d.ts +99 -0
- package/dist/src/lib/dataset-maintenance-remote.d.ts +212 -0
- package/dist/src/lib/dataset-maintenance-support-validation.d.ts +18 -0
- package/dist/src/lib/dataset-maintenance-verify.d.ts +102 -0
- package/dist/src/lib/dataset-patch.d.ts +193 -0
- package/dist/src/lib/dataset-references-rewrite.d.ts +77 -0
- package/dist/src/lib/dataset-remote-refresh.d.ts +64 -0
- package/dist/src/lib/dataset-remote-verify.d.ts +168 -0
- package/dist/src/lib/dataset-save-draft-run.d.ts +283 -0
- package/dist/src/lib/dataset-save-draft-run.js +54 -21
- package/dist/src/lib/dataset-save-draft-run.js.map +1 -1
- package/dist/src/lib/dataset-source-upload-attachments.d.ts +109 -0
- package/dist/src/lib/dataset-validate.d.ts +62 -0
- package/dist/src/lib/dotenv.d.ts +6 -0
- package/dist/src/lib/env.d.ts +59 -0
- package/dist/src/lib/errors.d.ts +18 -0
- package/dist/src/lib/flow-build-alias-map.d.ts +80 -0
- package/dist/src/lib/flow-fetch-rows.d.ts +70 -0
- package/dist/src/lib/flow-get.d.ts +35 -0
- package/dist/src/lib/flow-governance.d.ts +22 -0
- package/dist/src/lib/flow-list.d.ts +57 -0
- package/dist/src/lib/flow-materialize-decisions.d.ts +67 -0
- package/dist/src/lib/flow-payload-validation.d.ts +32 -0
- package/dist/src/lib/flow-publish-reviewed-data.d.ts +262 -0
- package/dist/src/lib/flow-publish-version.d.ts +193 -0
- package/dist/src/lib/flow-qa.d.ts +289 -0
- package/dist/src/lib/flow-read.d.ts +70 -0
- package/dist/src/lib/flow-regen-product.d.ts +431 -0
- package/dist/src/lib/flow-remediate.d.ts +133 -0
- package/dist/src/lib/http.d.ts +25 -0
- package/dist/src/lib/http.js.map +1 -1
- package/dist/src/lib/identity-preflight.d.ts +235 -0
- package/dist/src/lib/io.d.ts +2 -0
- package/dist/src/lib/kb-search.d.ts +23 -0
- package/dist/src/lib/lca-release.d.ts +105 -0
- package/dist/src/lib/lifecyclemodel-auto-build.d.ts +223 -0
- package/dist/src/lib/lifecyclemodel-bundle-save.d.ts +71 -0
- package/dist/src/lib/lifecyclemodel-graph.d.ts +91 -0
- package/dist/src/lib/lifecyclemodel-orchestrate.d.ts +349 -0
- package/dist/src/lib/lifecyclemodel-publish-build.d.ts +77 -0
- package/dist/src/lib/lifecyclemodel-publish-resulting-process.d.ts +27 -0
- package/dist/src/lib/lifecyclemodel-qa.d.ts +206 -0
- package/dist/src/lib/lifecyclemodel-resulting-process.d.ts +204 -0
- package/dist/src/lib/lifecyclemodel-save-draft-run.d.ts +96 -0
- package/dist/src/lib/lifecyclemodel-validate-build.d.ts +78 -0
- package/dist/src/lib/llm.d.ts +51 -0
- package/dist/src/lib/package-version.d.ts +3 -0
- package/dist/src/lib/process-auto-build.d.ts +168 -0
- package/dist/src/lib/process-batch-build.d.ts +97 -0
- package/dist/src/lib/process-dedup-review.d.ts +253 -0
- package/dist/src/lib/process-flow-build-plan.d.ts +108 -0
- package/dist/src/lib/process-get.d.ts +26 -0
- package/dist/src/lib/process-list.d.ts +87 -0
- package/dist/src/lib/process-payload-validation.d.ts +27 -0
- package/dist/src/lib/process-publish-build.d.ts +148 -0
- package/dist/src/lib/process-qa.d.ts +243 -0
- package/dist/src/lib/process-refresh-references.d.ts +179 -0
- package/dist/src/lib/process-required-fields.d.ts +142 -0
- package/dist/src/lib/process-resume-build.d.ts +77 -0
- package/dist/src/lib/process-save-draft-run.d.ts +72 -0
- package/dist/src/lib/process-save-draft.d.ts +53 -0
- package/dist/src/lib/process-scope-statistics.d.ts +210 -0
- package/dist/src/lib/process-verify-rows.d.ts +71 -0
- package/dist/src/lib/publish.d.ts +219 -0
- package/dist/src/lib/remote.d.ts +19 -0
- package/dist/src/lib/run.d.ts +60 -0
- package/dist/src/lib/runtime-rulesets.d.ts +29 -0
- package/dist/src/lib/state-lock.d.ts +25 -0
- package/dist/src/lib/state-lock.js +23 -1
- package/dist/src/lib/state-lock.js.map +1 -1
- package/dist/src/lib/supabase-client.d.ts +47 -0
- package/dist/src/lib/supabase-data-api-contract.d.ts +304 -0
- package/dist/src/lib/supabase-data-api-replay.d.ts +74 -0
- package/dist/src/lib/supabase-json-ordered-write.d.ts +65 -0
- package/dist/src/lib/supabase-rest.d.ts +26 -0
- package/dist/src/lib/supabase-session.d.ts +115 -0
- package/dist/src/lib/tidas-languages.d.ts +3 -0
- package/dist/src/lib/tidas-sdk-package-validator.d.ts +111 -0
- package/dist/src/lib/tidas-sdk-validation.d.ts +42 -0
- package/dist/src/lib/unstructured.d.ts +32 -0
- package/dist/src/lib/user-api-key.d.ts +16 -0
- package/dist/src/lib/validation.d.ts +82 -0
- package/dist/src/main.d.ts +4 -0
- package/package.json +17 -3
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { CliError } from './errors.js';
|
|
4
|
+
import { buildSupabaseAuthHeaders, deriveSupabaseProjectBaseUrl, requireSupabaseRestRuntime, } from './supabase-client.js';
|
|
5
|
+
import { resolveSupabaseUserSession, } from './supabase-session.js';
|
|
6
|
+
import { redactEmail, requireUserApiKeyCredentials } from './user-api-key.js';
|
|
7
|
+
export const AUTH_IDENTITY_RECEIPT_SCHEMA = 'tiangong-lca.auth-identity-receipt.v1';
|
|
8
|
+
export const AUTH_IDENTITY_MAX_TIMEOUT_MS = 2_147_483_647;
|
|
9
|
+
const CLI_PACKAGE_NAME = '@tiangong-lca/cli';
|
|
10
|
+
const CURRENT_USER_PATH = '/auth/v1/user';
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
12
|
+
const MAX_RESPONSE_BYTES = 64 * 1024;
|
|
13
|
+
const SESSION_REFRESH_WINDOW_SECONDS = 300;
|
|
14
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
|
|
15
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
16
|
+
const EMAIL_PATTERN = /^[^@\s]+@[^@\s]+\.[^@\s]+$/u;
|
|
17
|
+
const SESSION_SOURCES = ['memory', 'cache', 'refresh', 'signin'];
|
|
18
|
+
const CACHE_MODES = ['disabled', 'custom-file', 'platform-default'];
|
|
19
|
+
class ResponseBodyTooLargeError extends Error {
|
|
20
|
+
constructor() {
|
|
21
|
+
super('Response body exceeded the identity receipt byte limit.');
|
|
22
|
+
this.name = 'ResponseBodyTooLargeError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function stableJsonValue(value) {
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
return value.map(stableJsonValue);
|
|
31
|
+
}
|
|
32
|
+
if (isRecord(value)) {
|
|
33
|
+
return Object.fromEntries(Object.keys(value)
|
|
34
|
+
.sort()
|
|
35
|
+
.map((key) => [key, stableJsonValue(value[key])]));
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function sha256Json(value) {
|
|
40
|
+
return createHash('sha256')
|
|
41
|
+
.update(JSON.stringify(stableJsonValue(value)))
|
|
42
|
+
.digest('hex');
|
|
43
|
+
}
|
|
44
|
+
function token(value) {
|
|
45
|
+
if (typeof value !== 'string') {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const normalized = value.trim();
|
|
49
|
+
return normalized || null;
|
|
50
|
+
}
|
|
51
|
+
function normalizeEmail(value) {
|
|
52
|
+
const normalized = token(value)?.toLowerCase() ?? null;
|
|
53
|
+
if (!normalized || normalized.length > 254 || !EMAIL_PATTERN.test(normalized)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return normalized;
|
|
57
|
+
}
|
|
58
|
+
function isCanonicalUuid(value) {
|
|
59
|
+
return typeof value === 'string' && UUID_PATTERN.test(value);
|
|
60
|
+
}
|
|
61
|
+
function identityError(message, code, options = {}) {
|
|
62
|
+
throw new CliError(message, {
|
|
63
|
+
code,
|
|
64
|
+
exitCode: options.exitCode ?? 1,
|
|
65
|
+
...(options.details === undefined ? {} : { details: options.details }),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function receiptInvalid(message) {
|
|
69
|
+
return identityError(message, 'AUTH_IDENTITY_RECEIPT_INVALID');
|
|
70
|
+
}
|
|
71
|
+
function normalizeExpectation(value, label) {
|
|
72
|
+
if (value === null || value === undefined) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const normalized = token(value);
|
|
76
|
+
if (!normalized) {
|
|
77
|
+
return identityError(`${label} must be a non-empty string when provided.`, 'AUTH_IDENTITY_EXPECTATION_INVALID', {
|
|
78
|
+
exitCode: 2,
|
|
79
|
+
details: { option: label },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return normalized;
|
|
83
|
+
}
|
|
84
|
+
function normalizeExpectedUserId(value) {
|
|
85
|
+
const normalized = normalizeExpectation(value, '--expected-user-id');
|
|
86
|
+
if (normalized !== null && !isCanonicalUuid(normalized)) {
|
|
87
|
+
return identityError('--expected-user-id must be a canonical lowercase UUID.', 'AUTH_IDENTITY_EXPECTATION_INVALID', { exitCode: 2, details: { option: '--expected-user-id' } });
|
|
88
|
+
}
|
|
89
|
+
return normalized;
|
|
90
|
+
}
|
|
91
|
+
function normalizeTimeoutMs(value) {
|
|
92
|
+
const normalized = value ?? DEFAULT_TIMEOUT_MS;
|
|
93
|
+
if (!Number.isInteger(normalized) ||
|
|
94
|
+
normalized <= 0 ||
|
|
95
|
+
normalized > AUTH_IDENTITY_MAX_TIMEOUT_MS) {
|
|
96
|
+
return identityError(`Auth identity timeout must be an integer between 1 and ${AUTH_IDENTITY_MAX_TIMEOUT_MS}.`, 'AUTH_IDENTITY_TIMEOUT_INVALID', { exitCode: 2 });
|
|
97
|
+
}
|
|
98
|
+
return normalized;
|
|
99
|
+
}
|
|
100
|
+
function canonicalTimestamp(value) {
|
|
101
|
+
if (typeof value !== 'string') {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const timestamp = new Date(value);
|
|
105
|
+
return !Number.isNaN(timestamp.getTime()) && timestamp.toISOString() === value;
|
|
106
|
+
}
|
|
107
|
+
function canonicalProjectIdentity(projectBaseUrl) {
|
|
108
|
+
const normalized = token(projectBaseUrl);
|
|
109
|
+
if (!normalized || normalized !== projectBaseUrl) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const url = new URL(normalized);
|
|
114
|
+
const suffix = '.supabase.co';
|
|
115
|
+
const hostname = url.hostname.toLowerCase();
|
|
116
|
+
const projectRef = hostname.endsWith(suffix) ? hostname.slice(0, -suffix.length) : '';
|
|
117
|
+
if (url.protocol !== 'https:' ||
|
|
118
|
+
url.origin !== normalized ||
|
|
119
|
+
url.username ||
|
|
120
|
+
url.password ||
|
|
121
|
+
url.port ||
|
|
122
|
+
!projectRef ||
|
|
123
|
+
projectRef.includes('.') ||
|
|
124
|
+
!/^[a-z0-9-]+$/u.test(projectRef)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return { projectBaseUrl: url.origin, projectRef };
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function projectRefFromBaseUrl(projectBaseUrl) {
|
|
134
|
+
const identity = canonicalProjectIdentity(projectBaseUrl);
|
|
135
|
+
if (!identity) {
|
|
136
|
+
return identityError('The Supabase project URL did not contain a project identity.', 'AUTH_IDENTITY_PROJECT_INVALID', { exitCode: 2 });
|
|
137
|
+
}
|
|
138
|
+
return identity.projectRef;
|
|
139
|
+
}
|
|
140
|
+
function resolveProjectIdentity(apiBaseUrl) {
|
|
141
|
+
let projectBaseUrl;
|
|
142
|
+
try {
|
|
143
|
+
projectBaseUrl = deriveSupabaseProjectBaseUrl(apiBaseUrl);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return identityError('TIANGONG_LCA_API_BASE_URL must identify a canonical Supabase project.', 'AUTH_IDENTITY_PROJECT_INVALID', { exitCode: 2 });
|
|
147
|
+
}
|
|
148
|
+
const projectRef = projectRefFromBaseUrl(projectBaseUrl);
|
|
149
|
+
return { projectBaseUrl, projectRef };
|
|
150
|
+
}
|
|
151
|
+
function cacheMode(runtime) {
|
|
152
|
+
if (runtime.disableSessionCache) {
|
|
153
|
+
return 'disabled';
|
|
154
|
+
}
|
|
155
|
+
return runtime.sessionFile ? 'custom-file' : 'platform-default';
|
|
156
|
+
}
|
|
157
|
+
function sessionExpiresAtUtc(expiresAt) {
|
|
158
|
+
if (expiresAt === null) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (!Number.isFinite(expiresAt)) {
|
|
162
|
+
return identityError('Resolved auth session contained an invalid expiry.', 'AUTH_IDENTITY_SESSION_MISMATCH');
|
|
163
|
+
}
|
|
164
|
+
const timestamp = new Date(Math.floor(expiresAt) * 1000);
|
|
165
|
+
if (Number.isNaN(timestamp.getTime())) {
|
|
166
|
+
return identityError('Resolved auth session contained an invalid expiry.', 'AUTH_IDENTITY_SESSION_MISMATCH');
|
|
167
|
+
}
|
|
168
|
+
return timestamp.toISOString();
|
|
169
|
+
}
|
|
170
|
+
function assertSessionBinding(options) {
|
|
171
|
+
const { runtime, session } = options;
|
|
172
|
+
const sessionEmail = normalizeEmail(session.userEmail);
|
|
173
|
+
const apiKeyEmail = normalizeEmail(options.apiKeyEmail);
|
|
174
|
+
const accessToken = token(session.accessToken);
|
|
175
|
+
const source = session.source;
|
|
176
|
+
const expectedMode = cacheMode(runtime);
|
|
177
|
+
const customSessionPath = runtime.sessionFile ? path.resolve(runtime.sessionFile) : null;
|
|
178
|
+
const sessionPath = session.sessionFile ? path.resolve(session.sessionFile) : null;
|
|
179
|
+
const cachePathMatches = expectedMode === 'disabled'
|
|
180
|
+
? sessionPath === null
|
|
181
|
+
: expectedMode === 'custom-file'
|
|
182
|
+
? sessionPath === customSessionPath
|
|
183
|
+
: sessionPath !== null;
|
|
184
|
+
const cacheSourceFresh = !['memory', 'cache'].includes(source) ||
|
|
185
|
+
(typeof session.expiresAt === 'number' &&
|
|
186
|
+
Number.isFinite(session.expiresAt) &&
|
|
187
|
+
session.expiresAt >
|
|
188
|
+
Math.floor(options.now.getTime() / 1000) + SESSION_REFRESH_WINDOW_SECONDS);
|
|
189
|
+
if (!accessToken ||
|
|
190
|
+
session.projectBaseUrl !== options.projectBaseUrl ||
|
|
191
|
+
!sessionEmail ||
|
|
192
|
+
!apiKeyEmail ||
|
|
193
|
+
sessionEmail !== apiKeyEmail ||
|
|
194
|
+
!SESSION_SOURCES.includes(session.source) ||
|
|
195
|
+
!cachePathMatches ||
|
|
196
|
+
!cacheSourceFresh ||
|
|
197
|
+
(runtime.forceReauth && ['memory', 'cache'].includes(source))) {
|
|
198
|
+
identityError('Resolved auth session does not match the requested credential, project, or cache boundary.', 'AUTH_IDENTITY_SESSION_MISMATCH');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async function resolveBoundSession(options) {
|
|
202
|
+
try {
|
|
203
|
+
return await options.resolveSessionImpl({
|
|
204
|
+
runtime: options.runtime,
|
|
205
|
+
fetchImpl: options.fetchImpl,
|
|
206
|
+
timeoutMs: options.timeoutMs,
|
|
207
|
+
now: options.now,
|
|
208
|
+
forceRefresh: options.forceRefresh,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
const causeCode = error instanceof CliError
|
|
213
|
+
? error.code
|
|
214
|
+
: isRecord(error) && typeof error.code === 'string'
|
|
215
|
+
? error.code
|
|
216
|
+
: 'UNEXPECTED_SESSION_FAILURE';
|
|
217
|
+
return identityError('Failed to resolve the authenticated user session.', 'AUTH_IDENTITY_SESSION_FAILED', { details: { cause_code: causeCode } });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function currentUserFailureStatus(error) {
|
|
221
|
+
if (!(error instanceof CliError) ||
|
|
222
|
+
error.code !== 'AUTH_IDENTITY_REMOTE_REQUEST_FAILED' ||
|
|
223
|
+
!isRecord(error.details) ||
|
|
224
|
+
typeof error.details.status !== 'number' ||
|
|
225
|
+
!Number.isInteger(error.details.status)) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
return error.details.status;
|
|
229
|
+
}
|
|
230
|
+
function declaredResponseBytes(response) {
|
|
231
|
+
const value = response.headers.get('content-length')?.trim() ?? '';
|
|
232
|
+
if (!/^\d+$/u.test(value)) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return Number(value);
|
|
236
|
+
}
|
|
237
|
+
async function readBoundedResponseText(response) {
|
|
238
|
+
const declaredBytes = declaredResponseBytes(response);
|
|
239
|
+
if (declaredBytes !== null && declaredBytes > MAX_RESPONSE_BYTES) {
|
|
240
|
+
throw new ResponseBodyTooLargeError();
|
|
241
|
+
}
|
|
242
|
+
if (!response.body) {
|
|
243
|
+
const text = await response.text();
|
|
244
|
+
if (Buffer.byteLength(text, 'utf8') > MAX_RESPONSE_BYTES) {
|
|
245
|
+
throw new ResponseBodyTooLargeError();
|
|
246
|
+
}
|
|
247
|
+
return text;
|
|
248
|
+
}
|
|
249
|
+
const reader = response.body.getReader();
|
|
250
|
+
const decoder = new TextDecoder();
|
|
251
|
+
let observedBytes = 0;
|
|
252
|
+
let text = '';
|
|
253
|
+
try {
|
|
254
|
+
while (true) {
|
|
255
|
+
const chunk = await reader.read();
|
|
256
|
+
if (chunk.done) {
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
observedBytes += chunk.value.byteLength;
|
|
260
|
+
if (observedBytes > MAX_RESPONSE_BYTES) {
|
|
261
|
+
await Promise.allSettled([reader.cancel()]);
|
|
262
|
+
throw new ResponseBodyTooLargeError();
|
|
263
|
+
}
|
|
264
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
265
|
+
}
|
|
266
|
+
text += decoder.decode();
|
|
267
|
+
return text;
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
reader.releaseLock();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async function fetchCurrentUser(options) {
|
|
274
|
+
const url = `${options.projectBaseUrl}${CURRENT_USER_PATH}`;
|
|
275
|
+
let response;
|
|
276
|
+
try {
|
|
277
|
+
response = await options.fetchImpl(url, {
|
|
278
|
+
method: 'GET',
|
|
279
|
+
headers: buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),
|
|
280
|
+
redirect: 'error',
|
|
281
|
+
signal: AbortSignal.timeout(options.timeoutMs),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
return identityError('Authenticated current-user lookup failed before a response was received.', 'AUTH_IDENTITY_REMOTE_REQUEST_FAILED', {
|
|
286
|
+
details: {
|
|
287
|
+
endpoint: CURRENT_USER_PATH,
|
|
288
|
+
cause: error instanceof Error ? error.name : typeof error,
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (!response.ok) {
|
|
293
|
+
return identityError(`Authenticated current-user lookup returned HTTP ${response.status}.`, 'AUTH_IDENTITY_REMOTE_REQUEST_FAILED', { details: { endpoint: CURRENT_USER_PATH, status: response.status } });
|
|
294
|
+
}
|
|
295
|
+
const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
|
|
296
|
+
if (!contentType.includes('application/json')) {
|
|
297
|
+
return identityError('Authenticated current-user response did not declare JSON content.', 'AUTH_IDENTITY_REMOTE_INVALID_JSON', { details: { endpoint: CURRENT_USER_PATH, status: response.status } });
|
|
298
|
+
}
|
|
299
|
+
let text;
|
|
300
|
+
try {
|
|
301
|
+
text = await readBoundedResponseText(response);
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
if (error instanceof ResponseBodyTooLargeError) {
|
|
305
|
+
return identityError('Authenticated current-user response exceeded the bounded receipt limit.', 'AUTH_IDENTITY_RESPONSE_TOO_LARGE', { details: { endpoint: CURRENT_USER_PATH, status: response.status } });
|
|
306
|
+
}
|
|
307
|
+
return identityError('Authenticated current-user response could not be read.', 'AUTH_IDENTITY_REMOTE_REQUEST_FAILED', {
|
|
308
|
+
details: {
|
|
309
|
+
endpoint: CURRENT_USER_PATH,
|
|
310
|
+
status: response.status,
|
|
311
|
+
cause: error instanceof Error ? error.name : typeof error,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
let payload;
|
|
316
|
+
try {
|
|
317
|
+
payload = JSON.parse(text);
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
return identityError('Authenticated current-user response was not valid JSON.', 'AUTH_IDENTITY_REMOTE_INVALID_JSON', { details: { endpoint: CURRENT_USER_PATH, status: response.status } });
|
|
321
|
+
}
|
|
322
|
+
if (isRecord(payload) && payload.ok === false) {
|
|
323
|
+
return identityError('Authenticated current-user response explicitly reported ok:false.', 'AUTH_IDENTITY_REMOTE_REJECTED', { details: { endpoint: CURRENT_USER_PATH, status: response.status } });
|
|
324
|
+
}
|
|
325
|
+
const userId = isRecord(payload) && isCanonicalUuid(payload.id) ? payload.id : null;
|
|
326
|
+
const email = isRecord(payload) ? normalizeEmail(payload.email) : null;
|
|
327
|
+
if (!userId || !email) {
|
|
328
|
+
return identityError('Authenticated current-user response did not contain a valid id and email.', 'AUTH_IDENTITY_RESPONSE_INVALID', { details: { endpoint: CURRENT_USER_PATH, status: response.status } });
|
|
329
|
+
}
|
|
330
|
+
return { userId, email };
|
|
331
|
+
}
|
|
332
|
+
function requestFingerprint(projectRef) {
|
|
333
|
+
return sha256Json({
|
|
334
|
+
method: 'GET',
|
|
335
|
+
path: CURRENT_USER_PATH,
|
|
336
|
+
project_ref: projectRef,
|
|
337
|
+
redirect: 'error',
|
|
338
|
+
header_names: ['accept', 'apikey', 'authorization'],
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function responseFingerprint(options) {
|
|
342
|
+
return sha256Json({
|
|
343
|
+
project_ref: options.projectRef,
|
|
344
|
+
user_id: options.userId,
|
|
345
|
+
display_email: options.displayEmail,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function hasExactKeys(value, expected) {
|
|
349
|
+
const actual = Object.keys(value).sort();
|
|
350
|
+
const sortedExpected = [...expected].sort();
|
|
351
|
+
return (actual.length === sortedExpected.length &&
|
|
352
|
+
actual.every((key, index) => key === sortedExpected[index]));
|
|
353
|
+
}
|
|
354
|
+
function isOneOf(value, values) {
|
|
355
|
+
return typeof value === 'string' && values.includes(value);
|
|
356
|
+
}
|
|
357
|
+
function isCanonicalToken(value) {
|
|
358
|
+
return typeof value === 'string' && token(value) === value;
|
|
359
|
+
}
|
|
360
|
+
function isMaskedEmail(value) {
|
|
361
|
+
return isCanonicalToken(value) && /^(?:[^*@\s]{2})?\*{4}@[^@\s]+\.[^@\s]+$/u.test(value);
|
|
362
|
+
}
|
|
363
|
+
function receiptScope(receipt) {
|
|
364
|
+
const { receipt_scope_sha256: _receiptScopeSha256, ...scope } = receipt;
|
|
365
|
+
return scope;
|
|
366
|
+
}
|
|
367
|
+
export function parseAuthIdentityReceipt(value) {
|
|
368
|
+
if (!isRecord(value) ||
|
|
369
|
+
!hasExactKeys(value, [
|
|
370
|
+
'assertions',
|
|
371
|
+
'bindings',
|
|
372
|
+
'captured_at_utc',
|
|
373
|
+
'cli',
|
|
374
|
+
'identity',
|
|
375
|
+
'operation',
|
|
376
|
+
'project',
|
|
377
|
+
'receipt_scope_sha256',
|
|
378
|
+
'remote_write_mode',
|
|
379
|
+
'schema',
|
|
380
|
+
'session',
|
|
381
|
+
'status',
|
|
382
|
+
])) {
|
|
383
|
+
return receiptInvalid('Auth identity receipt must be an exact object.');
|
|
384
|
+
}
|
|
385
|
+
const cli = value.cli;
|
|
386
|
+
const project = value.project;
|
|
387
|
+
const identity = value.identity;
|
|
388
|
+
const session = value.session;
|
|
389
|
+
const bindings = value.bindings;
|
|
390
|
+
const assertions = value.assertions;
|
|
391
|
+
const parsedProject = isRecord(project) && typeof project.project_base_url === 'string'
|
|
392
|
+
? canonicalProjectIdentity(project.project_base_url)
|
|
393
|
+
: null;
|
|
394
|
+
if (value.schema !== AUTH_IDENTITY_RECEIPT_SCHEMA ||
|
|
395
|
+
value.status !== 'passed' ||
|
|
396
|
+
value.operation !== 'current-user-read' ||
|
|
397
|
+
value.remote_write_mode !== 'read-only' ||
|
|
398
|
+
!canonicalTimestamp(value.captured_at_utc) ||
|
|
399
|
+
!isRecord(cli) ||
|
|
400
|
+
!hasExactKeys(cli, ['package_name', 'package_version']) ||
|
|
401
|
+
cli.package_name !== CLI_PACKAGE_NAME ||
|
|
402
|
+
!isCanonicalToken(cli.package_version) ||
|
|
403
|
+
!isRecord(project) ||
|
|
404
|
+
!hasExactKeys(project, ['project_base_url', 'project_ref']) ||
|
|
405
|
+
!isCanonicalToken(project.project_ref) ||
|
|
406
|
+
!parsedProject ||
|
|
407
|
+
parsedProject.projectRef !== project.project_ref ||
|
|
408
|
+
!isRecord(identity) ||
|
|
409
|
+
!hasExactKeys(identity, ['display_email', 'user_id']) ||
|
|
410
|
+
!isCanonicalUuid(identity.user_id) ||
|
|
411
|
+
!isMaskedEmail(identity.display_email) ||
|
|
412
|
+
!isRecord(session) ||
|
|
413
|
+
!hasExactKeys(session, ['cache_mode', 'expires_at_utc', 'force_reauth', 'source']) ||
|
|
414
|
+
!isOneOf(session.source, SESSION_SOURCES) ||
|
|
415
|
+
!isOneOf(session.cache_mode, CACHE_MODES) ||
|
|
416
|
+
typeof session.force_reauth !== 'boolean' ||
|
|
417
|
+
!(session.expires_at_utc === null || canonicalTimestamp(session.expires_at_utc)) ||
|
|
418
|
+
!isRecord(bindings) ||
|
|
419
|
+
!hasExactKeys(bindings, ['request_sha256', 'response_sha256']) ||
|
|
420
|
+
typeof bindings.request_sha256 !== 'string' ||
|
|
421
|
+
!SHA256_PATTERN.test(bindings.request_sha256) ||
|
|
422
|
+
typeof bindings.response_sha256 !== 'string' ||
|
|
423
|
+
!SHA256_PATTERN.test(bindings.response_sha256) ||
|
|
424
|
+
bindings.request_sha256 !== requestFingerprint(project.project_ref) ||
|
|
425
|
+
bindings.response_sha256 !==
|
|
426
|
+
responseFingerprint({
|
|
427
|
+
projectRef: project.project_ref,
|
|
428
|
+
userId: identity.user_id,
|
|
429
|
+
displayEmail: identity.display_email,
|
|
430
|
+
}) ||
|
|
431
|
+
!isRecord(assertions) ||
|
|
432
|
+
!hasExactKeys(assertions, [
|
|
433
|
+
'expected_project_ref',
|
|
434
|
+
'expected_user_id',
|
|
435
|
+
'mode',
|
|
436
|
+
'passed',
|
|
437
|
+
'project_ref_passed',
|
|
438
|
+
'requested_count',
|
|
439
|
+
'user_id_passed',
|
|
440
|
+
]) ||
|
|
441
|
+
!isOneOf(assertions.mode, ['observed', 'partial', 'intent-bound']) ||
|
|
442
|
+
![0, 1, 2].includes(assertions.requested_count) ||
|
|
443
|
+
!(assertions.expected_project_ref === null || isCanonicalToken(assertions.expected_project_ref)) ||
|
|
444
|
+
!(assertions.expected_user_id === null || isCanonicalUuid(assertions.expected_user_id)) ||
|
|
445
|
+
!(assertions.project_ref_passed === true || assertions.project_ref_passed === null) ||
|
|
446
|
+
!(assertions.user_id_passed === true || assertions.user_id_passed === null) ||
|
|
447
|
+
assertions.passed !== true ||
|
|
448
|
+
(assertions.expected_project_ref === null) !== (assertions.project_ref_passed === null) ||
|
|
449
|
+
(assertions.expected_user_id === null) !== (assertions.user_id_passed === null) ||
|
|
450
|
+
assertions.requested_count !==
|
|
451
|
+
Number(assertions.expected_project_ref !== null) +
|
|
452
|
+
Number(assertions.expected_user_id !== null) ||
|
|
453
|
+
assertions.mode !==
|
|
454
|
+
(assertions.requested_count === 0
|
|
455
|
+
? 'observed'
|
|
456
|
+
: assertions.requested_count === 2
|
|
457
|
+
? 'intent-bound'
|
|
458
|
+
: 'partial') ||
|
|
459
|
+
(assertions.expected_project_ref !== null &&
|
|
460
|
+
assertions.expected_project_ref !== project.project_ref) ||
|
|
461
|
+
(assertions.expected_user_id !== null && assertions.expected_user_id !== identity.user_id) ||
|
|
462
|
+
typeof value.receipt_scope_sha256 !== 'string' ||
|
|
463
|
+
!SHA256_PATTERN.test(value.receipt_scope_sha256)) {
|
|
464
|
+
return receiptInvalid('Auth identity receipt failed schema validation.');
|
|
465
|
+
}
|
|
466
|
+
const receipt = value;
|
|
467
|
+
if (receipt.receipt_scope_sha256 !== sha256Json(receiptScope(receipt))) {
|
|
468
|
+
return receiptInvalid('Auth identity receipt canonical scope hash does not match.');
|
|
469
|
+
}
|
|
470
|
+
return receipt;
|
|
471
|
+
}
|
|
472
|
+
export async function runAuthIdentityReceipt(options) {
|
|
473
|
+
const timeoutMs = normalizeTimeoutMs(options.timeoutMs);
|
|
474
|
+
const expectedProjectRef = normalizeExpectation(options.expectedProjectRef, '--expected-project-ref');
|
|
475
|
+
const expectedUserId = normalizeExpectedUserId(options.expectedUserId);
|
|
476
|
+
const cliVersion = token(options.cliVersion);
|
|
477
|
+
if (!cliVersion) {
|
|
478
|
+
return identityError('CLI package version is required for an identity receipt.', 'AUTH_IDENTITY_VERSION_INVALID', { exitCode: 2 });
|
|
479
|
+
}
|
|
480
|
+
const now = options.now ?? new Date();
|
|
481
|
+
if (Number.isNaN(now.getTime())) {
|
|
482
|
+
return identityError('Identity receipt capture time is invalid.', 'AUTH_IDENTITY_CAPTURE_TIME_INVALID', { exitCode: 2 });
|
|
483
|
+
}
|
|
484
|
+
const runtime = requireSupabaseRestRuntime(options.env);
|
|
485
|
+
const { projectBaseUrl, projectRef } = resolveProjectIdentity(runtime.apiBaseUrl);
|
|
486
|
+
if (expectedProjectRef !== null && expectedProjectRef !== projectRef) {
|
|
487
|
+
return identityError('Authenticated project does not match --expected-project-ref.', 'AUTH_IDENTITY_PROJECT_MISMATCH', { details: { expected_project_ref: expectedProjectRef, observed_project_ref: projectRef } });
|
|
488
|
+
}
|
|
489
|
+
const credentials = requireUserApiKeyCredentials(runtime.userApiKey);
|
|
490
|
+
let session = await resolveBoundSession({
|
|
491
|
+
runtime,
|
|
492
|
+
fetchImpl: options.fetchImpl,
|
|
493
|
+
timeoutMs,
|
|
494
|
+
now,
|
|
495
|
+
resolveSessionImpl: options.resolveSessionImpl ?? resolveSupabaseUserSession,
|
|
496
|
+
});
|
|
497
|
+
assertSessionBinding({
|
|
498
|
+
runtime,
|
|
499
|
+
session,
|
|
500
|
+
projectBaseUrl,
|
|
501
|
+
apiKeyEmail: credentials.email,
|
|
502
|
+
now,
|
|
503
|
+
});
|
|
504
|
+
let currentUser;
|
|
505
|
+
try {
|
|
506
|
+
currentUser = await fetchCurrentUser({
|
|
507
|
+
projectBaseUrl,
|
|
508
|
+
publishableKey: runtime.publishableKey,
|
|
509
|
+
accessToken: session.accessToken,
|
|
510
|
+
timeoutMs,
|
|
511
|
+
fetchImpl: options.fetchImpl,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
const status = currentUserFailureStatus(error);
|
|
516
|
+
if (status === null || ![401, 403].includes(status)) {
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
session = await resolveBoundSession({
|
|
520
|
+
runtime,
|
|
521
|
+
fetchImpl: options.fetchImpl,
|
|
522
|
+
timeoutMs,
|
|
523
|
+
now,
|
|
524
|
+
forceRefresh: true,
|
|
525
|
+
resolveSessionImpl: options.resolveSessionImpl ?? resolveSupabaseUserSession,
|
|
526
|
+
});
|
|
527
|
+
assertSessionBinding({
|
|
528
|
+
runtime,
|
|
529
|
+
session,
|
|
530
|
+
projectBaseUrl,
|
|
531
|
+
apiKeyEmail: credentials.email,
|
|
532
|
+
now,
|
|
533
|
+
});
|
|
534
|
+
currentUser = await fetchCurrentUser({
|
|
535
|
+
projectBaseUrl,
|
|
536
|
+
publishableKey: runtime.publishableKey,
|
|
537
|
+
accessToken: session.accessToken,
|
|
538
|
+
timeoutMs,
|
|
539
|
+
fetchImpl: options.fetchImpl,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
const sessionEmail = normalizeEmail(session.userEmail);
|
|
543
|
+
const apiKeyEmail = normalizeEmail(credentials.email);
|
|
544
|
+
if (!sessionEmail ||
|
|
545
|
+
!apiKeyEmail ||
|
|
546
|
+
currentUser.email !== sessionEmail ||
|
|
547
|
+
currentUser.email !== apiKeyEmail) {
|
|
548
|
+
return identityError('Server-verified current user does not match the API-key and session identity.', 'AUTH_IDENTITY_SESSION_MISMATCH');
|
|
549
|
+
}
|
|
550
|
+
if (expectedUserId !== null && expectedUserId !== currentUser.userId) {
|
|
551
|
+
return identityError('Authenticated user does not match --expected-user-id.', 'AUTH_IDENTITY_USER_MISMATCH', { details: { expected_user_id: expectedUserId, observed_user_id: currentUser.userId } });
|
|
552
|
+
}
|
|
553
|
+
const displayEmail = redactEmail(currentUser.email);
|
|
554
|
+
const requestedAssertionCount = Number(expectedProjectRef !== null) + Number(expectedUserId !== null);
|
|
555
|
+
const scope = {
|
|
556
|
+
schema: AUTH_IDENTITY_RECEIPT_SCHEMA,
|
|
557
|
+
status: 'passed',
|
|
558
|
+
operation: 'current-user-read',
|
|
559
|
+
remote_write_mode: 'read-only',
|
|
560
|
+
captured_at_utc: now.toISOString(),
|
|
561
|
+
cli: {
|
|
562
|
+
package_name: CLI_PACKAGE_NAME,
|
|
563
|
+
package_version: cliVersion,
|
|
564
|
+
},
|
|
565
|
+
project: {
|
|
566
|
+
project_ref: projectRef,
|
|
567
|
+
project_base_url: projectBaseUrl,
|
|
568
|
+
},
|
|
569
|
+
identity: {
|
|
570
|
+
user_id: currentUser.userId,
|
|
571
|
+
display_email: displayEmail,
|
|
572
|
+
},
|
|
573
|
+
session: {
|
|
574
|
+
source: session.source,
|
|
575
|
+
cache_mode: cacheMode(runtime),
|
|
576
|
+
force_reauth: runtime.forceReauth,
|
|
577
|
+
expires_at_utc: sessionExpiresAtUtc(session.expiresAt),
|
|
578
|
+
},
|
|
579
|
+
bindings: {
|
|
580
|
+
request_sha256: requestFingerprint(projectRef),
|
|
581
|
+
response_sha256: responseFingerprint({
|
|
582
|
+
projectRef,
|
|
583
|
+
userId: currentUser.userId,
|
|
584
|
+
displayEmail,
|
|
585
|
+
}),
|
|
586
|
+
},
|
|
587
|
+
assertions: {
|
|
588
|
+
mode: requestedAssertionCount === 0
|
|
589
|
+
? 'observed'
|
|
590
|
+
: requestedAssertionCount === 2
|
|
591
|
+
? 'intent-bound'
|
|
592
|
+
: 'partial',
|
|
593
|
+
requested_count: requestedAssertionCount,
|
|
594
|
+
expected_project_ref: expectedProjectRef,
|
|
595
|
+
expected_user_id: expectedUserId,
|
|
596
|
+
project_ref_passed: expectedProjectRef === null ? null : true,
|
|
597
|
+
user_id_passed: expectedUserId === null ? null : true,
|
|
598
|
+
passed: true,
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
return parseAuthIdentityReceipt({
|
|
602
|
+
...scope,
|
|
603
|
+
receipt_scope_sha256: sha256Json(scope),
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
export const __testInternals = {
|
|
607
|
+
MAX_RESPONSE_BYTES,
|
|
608
|
+
assertSessionBinding,
|
|
609
|
+
cacheMode,
|
|
610
|
+
canonicalTimestamp,
|
|
611
|
+
canonicalProjectIdentity,
|
|
612
|
+
currentUserFailureStatus,
|
|
613
|
+
declaredResponseBytes,
|
|
614
|
+
fetchCurrentUser,
|
|
615
|
+
hasExactKeys,
|
|
616
|
+
isCanonicalToken,
|
|
617
|
+
isCanonicalUuid,
|
|
618
|
+
isMaskedEmail,
|
|
619
|
+
isOneOf,
|
|
620
|
+
normalizeEmail,
|
|
621
|
+
normalizeExpectation,
|
|
622
|
+
normalizeExpectedUserId,
|
|
623
|
+
normalizeTimeoutMs,
|
|
624
|
+
projectRefFromBaseUrl,
|
|
625
|
+
readBoundedResponseText,
|
|
626
|
+
requestFingerprint,
|
|
627
|
+
responseFingerprint,
|
|
628
|
+
resolveProjectIdentity,
|
|
629
|
+
sessionExpiresAtUtc,
|
|
630
|
+
sha256Json,
|
|
631
|
+
stableJsonValue,
|
|
632
|
+
token,
|
|
633
|
+
};
|
|
634
|
+
//# sourceMappingURL=auth-identity-receipt.js.map
|