@oxy.so/contracts 1.1.1 → 1.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/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/identity.js +3 -2
- package/dist/cjs/identityMove.js +156 -0
- package/dist/cjs/identityProof.js +164 -0
- package/dist/cjs/identityRecovery.js +51 -0
- package/dist/cjs/index.js +75 -19
- package/dist/cjs/inference/catalogue.js +7 -6
- package/dist/cjs/inference/identifiers.js +3 -1
- package/dist/cjs/inference/providerConnection.js +1 -1
- package/dist/cjs/inference/request.js +15 -1
- package/dist/cjs/inference/streamEvents.js +24 -2
- package/dist/cjs/inference/version.js +1 -1
- package/dist/cjs/userResponse.js +82 -1
- package/dist/cjs/username.js +70 -2
- package/dist/cjs/webIdentityCarrier.js +181 -0
- package/dist/cjs/webauthn.js +14 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/identity.js +3 -2
- package/dist/esm/identityMove.js +148 -0
- package/dist/esm/identityProof.js +159 -0
- package/dist/esm/identityRecovery.js +48 -0
- package/dist/esm/index.js +16 -7
- package/dist/esm/inference/catalogue.js +7 -6
- package/dist/esm/inference/identifiers.js +2 -0
- package/dist/esm/inference/providerConnection.js +2 -2
- package/dist/esm/inference/request.js +14 -0
- package/dist/esm/inference/streamEvents.js +24 -2
- package/dist/esm/inference/version.js +1 -1
- package/dist/esm/userResponse.js +81 -0
- package/dist/esm/username.js +69 -1
- package/dist/esm/webIdentityCarrier.js +178 -0
- package/dist/esm/webauthn.js +14 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/accountGraph.d.ts +7 -7
- package/dist/types/agency.d.ts +24 -24
- package/dist/types/browserHub.d.ts +16 -16
- package/dist/types/deviceDirectory.d.ts +28 -28
- package/dist/types/externalIdentity.d.ts +21 -7
- package/dist/types/identity.d.ts +3 -2
- package/dist/types/identityMove.d.ts +185 -0
- package/dist/types/identityProof.d.ts +156 -0
- package/dist/types/identityRecovery.d.ts +246 -0
- package/dist/types/index.d.ts +14 -8
- package/dist/types/inference/catalogue.d.ts +21 -20
- package/dist/types/inference/embeddings.d.ts +12 -12
- package/dist/types/inference/errors.d.ts +4 -4
- package/dist/types/inference/identifiers.d.ts +2 -0
- package/dist/types/inference/inbox.d.ts +6 -6
- package/dist/types/inference/providerConnection.d.ts +40 -40
- package/dist/types/inference/request.d.ts +84 -36
- package/dist/types/inference/streamEvents.d.ts +69 -13
- package/dist/types/inference/usage.d.ts +8 -8
- package/dist/types/inference/version.d.ts +1 -1
- package/dist/types/keyRotation.d.ts +2 -2
- package/dist/types/oauth.d.ts +30 -30
- package/dist/types/sessionStatus.d.ts +6 -6
- package/dist/types/transparency.d.ts +10 -10
- package/dist/types/userResponse.d.ts +386 -18
- package/dist/types/username.d.ts +25 -2
- package/dist/types/webIdentityCarrier.d.ts +1130 -0
- package/dist/types/webauthn.d.ts +208 -0
- package/package.json +1 -1
- package/dist/cjs/devicePairing.js +0 -138
- package/dist/esm/devicePairing.js +0 -135
- package/dist/types/devicePairing.d.ts +0 -130
|
@@ -137,12 +137,13 @@ exports.inferenceDataPolicySchema = zod_1.z
|
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
139
|
/**
|
|
140
|
-
* Who a route may be served to. Availability
|
|
141
|
-
* permission to resell the same provider/model publicly, which is why
|
|
142
|
-
* an explicit scope on the route rather than a boolean derived from
|
|
140
|
+
* Who a route may be served to. Availability to an official Oxy product never
|
|
141
|
+
* implies permission to resell the same provider/model publicly, which is why
|
|
142
|
+
* this is an explicit scope on the route rather than a boolean derived from
|
|
143
|
+
* "it works".
|
|
143
144
|
*/
|
|
144
145
|
exports.availabilityScopeSchema = zod_1.z.enum([
|
|
145
|
-
'
|
|
146
|
+
'platform_internal',
|
|
146
147
|
'public_payg',
|
|
147
148
|
'enterprise',
|
|
148
149
|
'byok_only',
|
|
@@ -335,7 +336,7 @@ exports.inferenceProviderSchema = zod_1.z.object({
|
|
|
335
336
|
exports.modelDeploymentSchema = zod_1.z
|
|
336
337
|
.object({
|
|
337
338
|
/** See `version.ts`: exchanged with the data plane on its own. */
|
|
338
|
-
schemaVersion: zod_1.z.literal(
|
|
339
|
+
schemaVersion: zod_1.z.literal(2),
|
|
339
340
|
deploymentId: identifiers_1.deploymentIdSchema,
|
|
340
341
|
provider: identifiers_1.inferenceProviderSlugSchema,
|
|
341
342
|
/** Always revision-pinned: a deployment serves specific weights. */
|
|
@@ -458,7 +459,7 @@ exports.catalogueServingProviderSummarySchema = zod_1.z
|
|
|
458
459
|
*/
|
|
459
460
|
exports.modelCatalogueEntrySchema = zod_1.z.object({
|
|
460
461
|
/** See `version.ts`: this is the public catalogue response shape. */
|
|
461
|
-
schemaVersion: zod_1.z.literal(
|
|
462
|
+
schemaVersion: zod_1.z.literal(3),
|
|
462
463
|
modelId: identifiers_1.modelIdSchema,
|
|
463
464
|
publisher: exports.cataloguePublisherSummarySchema,
|
|
464
465
|
displayName: zod_1.z.string().min(1).max(200),
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* Decided in: docs/adr/0007-canonical-request-attribution.md, docs/adr/0008-catalogue-concept-separation.md.
|
|
20
20
|
*/
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.RESERVED_ALIA_PUBLISHER = exports.inferenceRegionSchema = exports.deploymentIdSchema = exports.inferenceProviderSlugSchema = exports.routingProfileIdSchema = exports.routingProfileSlugSchema = exports.modelReferenceSchema = exports.modelRevisionLabelSchema = exports.modelIdSchema = exports.modelSlugSchema = exports.publisherSlugSchema = exports.sha256DigestSchema = exports.inferenceHttpsUrlSchema = exports.inferenceDateSchema = exports.inferenceTimestampSchema = exports.inferenceEnvironmentSchema = exports.idempotencyKeySchema = exports.generationIdSchema = exports.requestIdSchema = exports.oxyCredentialIdSchema = exports.oxyApplicationIdSchema = exports.delegatedUserIdSchema = exports.oxyAccountIdSchema = void 0;
|
|
22
|
+
exports.RESERVED_ALIA_PUBLISHER = exports.inferenceRegionSchema = exports.deploymentIdSchema = exports.inferenceProviderSlugSchema = exports.routingProfileIdSchema = exports.routingProfileSlugSchema = exports.modelReferenceSchema = exports.modelRevisionLabelSchema = exports.modelIdSchema = exports.modelSlugSchema = exports.publisherSlugSchema = exports.PADDED_BASE64_PATTERN = exports.sha256DigestSchema = exports.inferenceHttpsUrlSchema = exports.inferenceDateSchema = exports.inferenceTimestampSchema = exports.inferenceEnvironmentSchema = exports.idempotencyKeySchema = exports.generationIdSchema = exports.requestIdSchema = exports.oxyCredentialIdSchema = exports.oxyApplicationIdSchema = exports.delegatedUserIdSchema = exports.oxyAccountIdSchema = void 0;
|
|
23
23
|
const zod_1 = require("zod");
|
|
24
24
|
/* -------------------------------------------------------------------------- */
|
|
25
25
|
/* Principal identifiers */
|
|
@@ -129,6 +129,8 @@ exports.inferenceHttpsUrlSchema = zod_1.z
|
|
|
129
129
|
exports.sha256DigestSchema = zod_1.z
|
|
130
130
|
.string()
|
|
131
131
|
.regex(/^sha256:[a-f0-9]{64}$/, 'digest must be sha256:<64 lowercase hex>');
|
|
132
|
+
/** Standard base64 in whole, padded 4-character groups. */
|
|
133
|
+
exports.PADDED_BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
132
134
|
/* -------------------------------------------------------------------------- */
|
|
133
135
|
/* Catalogue references */
|
|
134
136
|
/* -------------------------------------------------------------------------- */
|
|
@@ -114,7 +114,7 @@ const kaanaCredentialSecretBase64Schema = zod_1.z
|
|
|
114
114
|
.string()
|
|
115
115
|
.min(1)
|
|
116
116
|
.max(8192)
|
|
117
|
-
.regex(
|
|
117
|
+
.regex(identifiers_1.PADDED_BASE64_PATTERN)
|
|
118
118
|
.refine(isVisibleASCIIProviderCredential, {
|
|
119
119
|
message: 'a decoded provider credential is 1-4096 visible ASCII bytes',
|
|
120
120
|
});
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* docs/adr/0017-authorized-routes-in-the-envelope.md.
|
|
26
26
|
*/
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.inferenceRequestSchema = exports.clientRequestMetadataSchema = exports.responseFormatSchema = exports.toolChoiceSchema = exports.toolDefinitionSchema = exports.samplingParametersSchema = exports.inferenceInputSchema = exports.inferenceMessageSchema = exports.inferenceMessageRoleSchema = exports.inferenceToolCallSchema = exports.inferenceContentPartSchema = exports.inferenceContentSourceSchema = void 0;
|
|
28
|
+
exports.inferenceRequestSchema = exports.inferenceSpeechParametersSchema = exports.clientRequestMetadataSchema = exports.responseFormatSchema = exports.toolChoiceSchema = exports.toolDefinitionSchema = exports.samplingParametersSchema = exports.inferenceInputSchema = exports.inferenceMessageSchema = exports.inferenceMessageRoleSchema = exports.inferenceToolCallSchema = exports.inferenceContentPartSchema = exports.inferenceContentSourceSchema = void 0;
|
|
29
29
|
const zod_1 = require("zod");
|
|
30
30
|
const attribution_1 = require("./attribution");
|
|
31
31
|
const catalogue_1 = require("./catalogue");
|
|
@@ -314,6 +314,12 @@ const modelLineOf = (reference) => {
|
|
|
314
314
|
const at = reference.indexOf("@");
|
|
315
315
|
return at === -1 ? reference : reference.slice(0, at);
|
|
316
316
|
};
|
|
317
|
+
/** Parameters for text-to-speech, preserved in the signed request. */
|
|
318
|
+
exports.inferenceSpeechParametersSchema = zod_1.z.object({
|
|
319
|
+
voice: zod_1.z.string().min(1).max(64),
|
|
320
|
+
responseFormat: zod_1.z.enum(["mp3", "opus", "aac", "flac", "wav", "pcm"]),
|
|
321
|
+
speed: zod_1.z.number().min(0.25).max(4).optional(),
|
|
322
|
+
}).strict();
|
|
317
323
|
/**
|
|
318
324
|
* The canonical internal request Oxy forwards to the data plane.
|
|
319
325
|
*
|
|
@@ -333,6 +339,7 @@ exports.inferenceRequestSchema = zod_1.z
|
|
|
333
339
|
stream: zod_1.z.boolean(),
|
|
334
340
|
maxOutputTokens: zod_1.z.number().int().positive().safe().optional(),
|
|
335
341
|
sampling: exports.samplingParametersSchema,
|
|
342
|
+
speech: exports.inferenceSpeechParametersSchema.optional(),
|
|
336
343
|
tools: zod_1.z.array(exports.toolDefinitionSchema).default([]),
|
|
337
344
|
toolChoice: exports.toolChoiceSchema.optional(),
|
|
338
345
|
responseFormat: exports.responseFormatSchema.optional(),
|
|
@@ -375,6 +382,13 @@ exports.inferenceRequestSchema = zod_1.z
|
|
|
375
382
|
authorizedRoutes: zod_1.z.array(routingPolicy_1.authorizedRouteSchema).min(1).optional(),
|
|
376
383
|
})
|
|
377
384
|
.superRefine((request, ctx) => {
|
|
385
|
+
const isSpeech = request.client.apiFormat === "audio_speech";
|
|
386
|
+
if (isSpeech && request.speech !== undefined && (request.modality !== "audio" || request.input.format !== "text" || request.stream)) {
|
|
387
|
+
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ["speech"], message: "speech requires audio modality, text input, parameters and non-streaming output" });
|
|
388
|
+
}
|
|
389
|
+
if (!isSpeech && request.speech !== undefined) {
|
|
390
|
+
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ["speech"], message: "speech parameters require the audio_speech API format" });
|
|
391
|
+
}
|
|
378
392
|
if (request.toolChoice !== undefined && request.tools.length === 0) {
|
|
379
393
|
ctx.addIssue({
|
|
380
394
|
code: zod_1.z.ZodIssueCode.custom,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Normalized stream events — what the data plane emits and the Oxy edge
|
|
4
4
|
* forwards as SSE.
|
|
5
5
|
*
|
|
6
|
-
* One discriminated union,
|
|
6
|
+
* One discriminated union, eight shapes, all carrying `requestId` and a
|
|
7
7
|
* monotonic `sequence`. `requestId` is on EVERY event rather than only the
|
|
8
8
|
* first because a proxy that re-frames or a client that reconnects would
|
|
9
9
|
* otherwise be holding events it cannot attribute; `sequence` is what makes a
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* Decided in: docs/adr/0010-public-api-compatibility.md, docs/adr/0008-catalogue-concept-separation.md.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.inferenceStreamEventSchema = exports.inferenceStreamDoneEventSchema = exports.inferenceFinishReasonSchema = exports.inferenceStreamErrorEventSchema = exports.inferenceStreamRouteSwitchEventSchema = exports.inferenceRouteSwitchReasonSchema = exports.inferenceRouteSwitchDetailSchema = exports.inferenceStreamUsageEventSchema = exports.inferenceStreamToolCallEventSchema = exports.inferenceStreamDeltaEventSchema = exports.inferenceStreamStartEventSchema = void 0;
|
|
25
|
+
exports.inferenceStreamEventSchema = exports.inferenceStreamDoneEventSchema = exports.inferenceFinishReasonSchema = exports.inferenceStreamErrorEventSchema = exports.inferenceStreamRouteSwitchEventSchema = exports.inferenceRouteSwitchReasonSchema = exports.inferenceRouteSwitchDetailSchema = exports.inferenceStreamUsageEventSchema = exports.inferenceStreamToolCallEventSchema = exports.inferenceStreamAudioEventSchema = exports.MAX_INFERENCE_AUDIO_BYTES = exports.inferenceAudioMediaTypeSchema = exports.inferenceStreamDeltaEventSchema = exports.inferenceStreamStartEventSchema = void 0;
|
|
26
26
|
const zod_1 = require("zod");
|
|
27
27
|
const identifiers_1 = require("./identifiers");
|
|
28
28
|
const errors_1 = require("./errors");
|
|
@@ -65,6 +65,27 @@ exports.inferenceStreamDeltaEventSchema = zod_1.z.object({
|
|
|
65
65
|
channel: zod_1.z.enum(['output_text', 'reasoning', 'refusal']),
|
|
66
66
|
text: zod_1.z.string(),
|
|
67
67
|
});
|
|
68
|
+
/** The encodings an audio output may carry. */
|
|
69
|
+
exports.inferenceAudioMediaTypeSchema = zod_1.z.enum([
|
|
70
|
+
'audio/mpeg',
|
|
71
|
+
'audio/wav',
|
|
72
|
+
'audio/ogg',
|
|
73
|
+
'audio/aac',
|
|
74
|
+
'audio/flac',
|
|
75
|
+
'audio/pcm',
|
|
76
|
+
]);
|
|
77
|
+
/** The most bytes one folded audio output may hold, at the edge and in the SDK. */
|
|
78
|
+
exports.MAX_INFERENCE_AUDIO_BYTES = 20 * 1024 * 1024;
|
|
79
|
+
/** A bounded, independently base64-encoded chunk of one audio output. */
|
|
80
|
+
exports.inferenceStreamAudioEventSchema = zod_1.z.object({
|
|
81
|
+
schemaVersion: zod_1.z.literal(1),
|
|
82
|
+
type: zod_1.z.literal('audio'),
|
|
83
|
+
requestId: identifiers_1.requestIdSchema,
|
|
84
|
+
sequence: zod_1.z.number().int().nonnegative().safe(),
|
|
85
|
+
outputIndex: zod_1.z.number().int().nonnegative().safe(),
|
|
86
|
+
mediaType: exports.inferenceAudioMediaTypeSchema,
|
|
87
|
+
data: zod_1.z.string().min(4).max(65536).regex(identifiers_1.PADDED_BASE64_PATTERN),
|
|
88
|
+
});
|
|
68
89
|
/**
|
|
69
90
|
* A tool call being streamed.
|
|
70
91
|
*
|
|
@@ -250,6 +271,7 @@ exports.inferenceStreamDoneEventSchema = zod_1.z.object({
|
|
|
250
271
|
exports.inferenceStreamEventSchema = zod_1.z.discriminatedUnion('type', [
|
|
251
272
|
exports.inferenceStreamStartEventSchema,
|
|
252
273
|
exports.inferenceStreamDeltaEventSchema,
|
|
274
|
+
exports.inferenceStreamAudioEventSchema,
|
|
253
275
|
exports.inferenceStreamToolCallEventSchema,
|
|
254
276
|
exports.inferenceStreamUsageEventSchema,
|
|
255
277
|
exports.inferenceStreamRouteSwitchEventSchema,
|
|
@@ -102,4 +102,4 @@ exports.INFERENCE_CONTRACT_VERSION = void 0;
|
|
|
102
102
|
* change to, say, the catalogue reject every in-flight inference request; the
|
|
103
103
|
* per-shape `schemaVersion` is what a message is validated against.
|
|
104
104
|
*/
|
|
105
|
-
exports.INFERENCE_CONTRACT_VERSION = '
|
|
105
|
+
exports.INFERENCE_CONTRACT_VERSION = '3.0.0';
|
package/dist/cjs/userResponse.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* `require()`).
|
|
31
31
|
*/
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.deviceLinkedSessionsResponseSchema = exports.deviceLinkedSessionSchema = exports.currentUserResponseSchema = exports.userProfileUpdateSchema = exports.userResponseSchema = exports.themePreferenceSchema = exports.userRelationshipSchema = exports.userNameSchema = void 0;
|
|
33
|
+
exports.deviceLinkedSessionsResponseSchema = exports.deviceLinkedSessionSchema = exports.currentUserResponseSchema = exports.userProfileUpdateSchema = exports.userResponseSchema = exports.dateOfBirthSchema = exports.themePreferenceSchema = exports.userRelationshipSchema = exports.userNameSchema = void 0;
|
|
34
34
|
exports.resolveUserId = resolveUserId;
|
|
35
35
|
exports.safeParseContract = safeParseContract;
|
|
36
36
|
const zod_1 = require("zod");
|
|
@@ -52,6 +52,60 @@ exports.themePreferenceSchema = zod_1.z.object({
|
|
|
52
52
|
mode: zod_1.z.enum(['light', 'dark', 'system']),
|
|
53
53
|
colorPreset: zod_1.z.string(),
|
|
54
54
|
});
|
|
55
|
+
/**
|
|
56
|
+
* The earliest calendar year `dateOfBirthSchema` accepts.
|
|
57
|
+
*
|
|
58
|
+
* Not a real biological bound — it exists to catch an obviously-transposed
|
|
59
|
+
* year (`1027` for `2027`, a stray OCR/typo digit) with a clear message
|
|
60
|
+
* instead of the value quietly becoming a 150-year-old account. 1900 is
|
|
61
|
+
* generous enough that no living person's real birthdate is rejected by it.
|
|
62
|
+
*/
|
|
63
|
+
const MIN_BIRTH_YEAR = 1900;
|
|
64
|
+
/**
|
|
65
|
+
* `true` when `year`/`month`/`day` name a date that actually exists on the
|
|
66
|
+
* Gregorian calendar — the check `z.string().regex(...)` alone cannot make,
|
|
67
|
+
* since the regex only constrains digit COUNT and would pass `2024-02-30`.
|
|
68
|
+
*/
|
|
69
|
+
function isRealCalendarDate(year, month, day) {
|
|
70
|
+
const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
71
|
+
const daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
72
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth[month - 1];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A date of birth, `YYYY-MM-DD`, the sole structured representation this
|
|
76
|
+
* platform stores going forward (`users.date_of_birth` — see
|
|
77
|
+
* `packages/api/src/db/schema/users.ts`). `birthday` (below) stays as the
|
|
78
|
+
* legacy free-text field for backward compatibility with existing readers;
|
|
79
|
+
* this schema is what both the write path (`user.service.ts`) and the read
|
|
80
|
+
* path (nothing — a date of birth is owner-only, never echoed to another
|
|
81
|
+
* viewer) validate against.
|
|
82
|
+
*
|
|
83
|
+
* Three checks, in order, because a regex alone would accept a string-shaped
|
|
84
|
+
* lie:
|
|
85
|
+
* 1. Exactly `YYYY-MM-DD` — the wire format, nothing looser.
|
|
86
|
+
* 2. A real Gregorian date — rejects `2024-02-30`, a date the regex cannot see
|
|
87
|
+
* is impossible.
|
|
88
|
+
* 3. Bounded to a plausible human lifetime — not before {@link MIN_BIRTH_YEAR}
|
|
89
|
+
* and not after today (comparing the zero-padded ISO strings directly is
|
|
90
|
+
* a valid, simpler stand-in for a numeric comparison here, since two
|
|
91
|
+
* `YYYY-MM-DD` strings of equal length sort exactly the way their dates
|
|
92
|
+
* do). "Today" is UTC — see `computeIsAdult` in `user.service.ts` for why
|
|
93
|
+
* a date with no timezone of its own is evaluated in UTC rather than any
|
|
94
|
+
* particular caller's local zone.
|
|
95
|
+
*/
|
|
96
|
+
exports.dateOfBirthSchema = zod_1.z
|
|
97
|
+
.string()
|
|
98
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, 'dateOfBirth must be an ISO 8601 calendar date (YYYY-MM-DD)')
|
|
99
|
+
.refine((value) => {
|
|
100
|
+
const [year, month, day] = value.split('-').map(Number);
|
|
101
|
+
return isRealCalendarDate(year, month, day);
|
|
102
|
+
}, { message: 'dateOfBirth is not a real calendar date' })
|
|
103
|
+
.refine((value) => Number(value.slice(0, 4)) >= MIN_BIRTH_YEAR, {
|
|
104
|
+
message: `dateOfBirth must not be before ${MIN_BIRTH_YEAR}`,
|
|
105
|
+
})
|
|
106
|
+
.refine((value) => value <= new Date().toISOString().slice(0, 10), {
|
|
107
|
+
message: 'dateOfBirth must not be in the future',
|
|
108
|
+
});
|
|
55
109
|
/**
|
|
56
110
|
* The canonical user object emitted by `formatUserResponse`.
|
|
57
111
|
*
|
|
@@ -81,6 +135,26 @@ exports.userResponseSchema = zod_1.z
|
|
|
81
135
|
phone: zod_1.z.string().optional(),
|
|
82
136
|
address: zod_1.z.string().optional(),
|
|
83
137
|
birthday: zod_1.z.string().optional(),
|
|
138
|
+
/**
|
|
139
|
+
* Structured date of birth, `YYYY-MM-DD`. Present only on the
|
|
140
|
+
* account's OWN profile response (`GET /users/me`, `PUT /users/me`
|
|
141
|
+
* with `includePrivateFields`) — never on another account's profile,
|
|
142
|
+
* the same visibility `phone`/`address`/`birthday` already have. See
|
|
143
|
+
* {@link dateOfBirthSchema}.
|
|
144
|
+
*/
|
|
145
|
+
dateOfBirth: exports.dateOfBirthSchema.optional(),
|
|
146
|
+
/**
|
|
147
|
+
* Derived, non-PII signal: whether the account holder is at least 18
|
|
148
|
+
* (see `computeIsAdult` in `user.service.ts` for the exact threshold
|
|
149
|
+
* and the UTC-"today" choice). Computed fresh on every read — age
|
|
150
|
+
* changes daily, so this is never stored. `undefined` when
|
|
151
|
+
* `dateOfBirth` is unset ("unknown"), distinct from `false` ("known,
|
|
152
|
+
* not yet 18"). Rides the same owner-only visibility as
|
|
153
|
+
* `dateOfBirth`; a future pass may widen this specific field to
|
|
154
|
+
* other viewers without exposing the birthdate itself, but that is
|
|
155
|
+
* not decided here.
|
|
156
|
+
*/
|
|
157
|
+
isAdult: zod_1.z.boolean().optional(),
|
|
84
158
|
/** Avatar file id (string) or null. */
|
|
85
159
|
avatar: zod_1.z.string().nullable().optional(),
|
|
86
160
|
/** Named Bloom color preset (e.g. `"blue"`) or null. */
|
|
@@ -176,6 +250,13 @@ exports.userProfileUpdateSchema = zod_1.z
|
|
|
176
250
|
phone: zod_1.z.string().optional(),
|
|
177
251
|
address: zod_1.z.string().optional(),
|
|
178
252
|
birthday: zod_1.z.string().optional(),
|
|
253
|
+
/**
|
|
254
|
+
* Structured date of birth. `null` (or `''`, at the service layer)
|
|
255
|
+
* clears it. Independently settable from `birthday` — see
|
|
256
|
+
* `user.service.ts`'s `updateUserProfile` for why the two legacy and
|
|
257
|
+
* structured fields are not kept in sync with each other.
|
|
258
|
+
*/
|
|
259
|
+
dateOfBirth: exports.dateOfBirthSchema.nullable().optional(),
|
|
179
260
|
locations: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
180
261
|
links: zod_1.z.array(zod_1.z.string()).optional(),
|
|
181
262
|
linksMetadata: zod_1.z
|
package/dist/cjs/username.js
CHANGED
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
* knows what is taken, and it must not pretend to.
|
|
101
101
|
*/
|
|
102
102
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
103
|
-
exports.botUsernameSchema = exports.BOT_USERNAME_INVALID_MESSAGE = exports.BOT_USERNAME_SUFFIX = exports.usernameSchema = exports.USERNAME_PATTERN_SOURCE = exports.USERNAME_INVALID_MESSAGE = exports.USERNAME_MAX_LENGTH = exports.USERNAME_MIN_LENGTH = void 0;
|
|
103
|
+
exports.botUsernameSchema = exports.BOT_USERNAME_INVALID_MESSAGE = exports.BOT_USERNAME_SUFFIX = exports.usernameSchema = exports.USERNAME_PATTERN_SOURCE = exports.NUMERIC_USERNAME_MESSAGE = exports.RESERVED_USERNAME_MESSAGE = exports.RESERVED_USERNAMES = exports.USERNAME_INVALID_MESSAGE = exports.USERNAME_MAX_LENGTH = exports.USERNAME_MIN_LENGTH = void 0;
|
|
104
104
|
exports.isValidUsername = isValidUsername;
|
|
105
105
|
exports.stripDisallowedUsernameCharacters = stripDisallowedUsernameCharacters;
|
|
106
106
|
exports.usernameSchemaForAccountKind = usernameSchemaForAccountKind;
|
|
@@ -113,6 +113,71 @@ exports.USERNAME_MAX_LENGTH = 30;
|
|
|
113
113
|
/** The 400 / inline-validation copy for every path that rejects a handle. */
|
|
114
114
|
exports.USERNAME_INVALID_MESSAGE = 'Username must be 3-30 characters, use only letters, numbers, hyphens and underscores, ' +
|
|
115
115
|
'start and end with a letter or number, and never repeat a separator';
|
|
116
|
+
/**
|
|
117
|
+
* Exact-match handles nobody may newly claim, regardless of account kind.
|
|
118
|
+
*
|
|
119
|
+
* This is NOT a namespace tightening like the bot suffix — it is a list of
|
|
120
|
+
* names withheld from `users_lower_username_key` before anybody asks for
|
|
121
|
+
* them. Compared against the same normalization the unique index applies
|
|
122
|
+
* (`lower(btrim(username))`), so `Admin`, ` ADMIN ` and `admin` are the one
|
|
123
|
+
* name this list means.
|
|
124
|
+
*
|
|
125
|
+
* `oxy`, `mention`, `homiio`, `clarity`, `faircoin`, `astro` and `mercaria`
|
|
126
|
+
* are deliberately NOT here: each already has an `organization`/`project`
|
|
127
|
+
* row, so the unique index already refuses a second one, and `oxy` is the
|
|
128
|
+
* name `USERNAME_MIN_LENGTH` is pinned against in
|
|
129
|
+
* `__tests__/username.test.ts` — listing it would make
|
|
130
|
+
* `usernameSchema.safeParse('oxy')` fail and falsify that comment. A brand
|
|
131
|
+
* with no account of its own yet (measured 2026-09-18: Alia, Allo, TNP,
|
|
132
|
+
* Kaana, Bloom) has no row to protect it, so it is listed until one exists.
|
|
133
|
+
*/
|
|
134
|
+
exports.RESERVED_USERNAMES = new Set([
|
|
135
|
+
// Oxy product lines with no account row of their own yet.
|
|
136
|
+
'alia',
|
|
137
|
+
'allo',
|
|
138
|
+
'tnp',
|
|
139
|
+
'kaana',
|
|
140
|
+
'bloom',
|
|
141
|
+
// System / role words a signup impersonating staff or Oxy itself would reach for.
|
|
142
|
+
'admin',
|
|
143
|
+
'administrator',
|
|
144
|
+
'root',
|
|
145
|
+
'superadmin',
|
|
146
|
+
'super',
|
|
147
|
+
'superuser',
|
|
148
|
+
'support',
|
|
149
|
+
'staff',
|
|
150
|
+
'moderator',
|
|
151
|
+
'mod',
|
|
152
|
+
'security',
|
|
153
|
+
'system',
|
|
154
|
+
'official',
|
|
155
|
+
'help',
|
|
156
|
+
'noreply',
|
|
157
|
+
'anonymous',
|
|
158
|
+
'everyone',
|
|
159
|
+
'owner',
|
|
160
|
+
]);
|
|
161
|
+
/** The 400 / inline-validation copy for a handle on {@link RESERVED_USERNAMES}. */
|
|
162
|
+
exports.RESERVED_USERNAME_MESSAGE = 'This username is reserved and cannot be registered';
|
|
163
|
+
/**
|
|
164
|
+
* {@link RESERVED_USERNAMES}, minus `alia`, checked against each `-`/`_`
|
|
165
|
+
* separated SEGMENT of a candidate rather than the whole string — so
|
|
166
|
+
* `official-oxy`, `super-admin` and `team-kaana` are refused the same as the
|
|
167
|
+
* bare words, without banning every word that merely CONTAINS one as a
|
|
168
|
+
* substring (`superman`, `modern`, `grassroot`, `homeowner` all stay legal:
|
|
169
|
+
* none of them separates the reserved word from the rest with `-` or `_`).
|
|
170
|
+
*
|
|
171
|
+
* `alia` is excluded because `alia-` is the live internal-cost-centre
|
|
172
|
+
* namespace: `alia-production-chat` is a minted account and
|
|
173
|
+
* `alia-research` / `alia-voice` / `alia-evaluations` are pinned as legal
|
|
174
|
+
* slugs in `__tests__/username.test.ts` and `internalCostCenterSpecs.test.ts`.
|
|
175
|
+
* Segment-matching `alia` would refuse all four. The bare word `alia` is
|
|
176
|
+
* still refused — {@link RESERVED_USERNAMES} above catches it exactly.
|
|
177
|
+
*/
|
|
178
|
+
const RESERVED_USERNAME_SEGMENTS = new Set([...exports.RESERVED_USERNAMES].filter((word) => word !== 'alia'));
|
|
179
|
+
/** The 400 / inline-validation copy for a handle that is only digits. */
|
|
180
|
+
exports.NUMERIC_USERNAME_MESSAGE = 'Username cannot be only numbers';
|
|
116
181
|
/**
|
|
117
182
|
* Alphanumeric runs joined by single separators, as a SOURCE string.
|
|
118
183
|
*
|
|
@@ -149,7 +214,10 @@ exports.usernameSchema = zod_1.z
|
|
|
149
214
|
.trim()
|
|
150
215
|
.min(exports.USERNAME_MIN_LENGTH, exports.USERNAME_INVALID_MESSAGE)
|
|
151
216
|
.max(exports.USERNAME_MAX_LENGTH, exports.USERNAME_INVALID_MESSAGE)
|
|
152
|
-
.regex(USERNAME_PATTERN, exports.USERNAME_INVALID_MESSAGE)
|
|
217
|
+
.regex(USERNAME_PATTERN, exports.USERNAME_INVALID_MESSAGE)
|
|
218
|
+
.refine((username) => !/^[0-9]+$/.test(username), exports.NUMERIC_USERNAME_MESSAGE)
|
|
219
|
+
.refine((username) => !exports.RESERVED_USERNAMES.has(username.toLowerCase()), exports.RESERVED_USERNAME_MESSAGE)
|
|
220
|
+
.refine((username) => !username.split(/[-_]/).some((segment) => RESERVED_USERNAME_SEGMENTS.has(segment.toLowerCase())), exports.RESERVED_USERNAME_MESSAGE);
|
|
153
221
|
/**
|
|
154
222
|
* Whether a candidate handle is storable — the boolean form, for input surfaces
|
|
155
223
|
* that show a message as somebody types rather than throwing.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.webIdentityEnvelopeEstablishSchema = exports.webauthnAssertionResponseSchema = exports.webIdentityEnvelopePutSchema = exports.webIdentityEnvelopeActionSchema = exports.webIdentityEnvelopeProofFieldsSchema = exports.webIdentityEnvelopeResponseSchema = exports.webIdentityHolderSchema = exports.webIdentityEnvelopeUploadSchema = exports.webIdentityEnvelopeSchema = exports.webIdentityWrapSchema = exports.webauthnRpIdSchema = exports.webauthnCredentialIdSchema = exports.webIdentityPublicKeySchema = exports.WEB_IDENTITY_SECRET_KINDS = exports.WEB_IDENTITY_ENVELOPE_VERSION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Web identity holder contract — the sealed envelope that lets a browser hold an
|
|
6
|
+
* account's self-custody root without Oxy ever being able to use it (ADR 0024).
|
|
7
|
+
*
|
|
8
|
+
* A root is a BIP-39 phrase (12–24 words) whose seed's first 32 bytes are the
|
|
9
|
+
* secp256k1 key — exactly the Commons derivation — or, for a few imported
|
|
10
|
+
* identities, a raw private key that never had a phrase. On the web it travels as:
|
|
11
|
+
*
|
|
12
|
+
* secret ── XChaCha20-Poly1305 under a random DEK ──▶ sealedSecret
|
|
13
|
+
* DEK ── XChaCha20-Poly1305 under KEK_i ──▶ wraps[i]
|
|
14
|
+
* KEK_i = HKDF(PRF output of passkey i)
|
|
15
|
+
*
|
|
16
|
+
* The server stores the envelope and can open NONE of it: the PRF output never
|
|
17
|
+
* leaves the user's authenticator, and the secret is never uploaded. The AEAD
|
|
18
|
+
* associated data binds the secret to the root's public key and kind, and each
|
|
19
|
+
* wrap to its credential and RP ID, so a re-labelled or transplanted envelope
|
|
20
|
+
* fails to open instead of decrypting into the wrong identity.
|
|
21
|
+
*
|
|
22
|
+
* Platform-agnostic — zod only, ESM-safe (no `require()`).
|
|
23
|
+
*/
|
|
24
|
+
const zod_1 = require("zod");
|
|
25
|
+
const identityProof_1 = require("./identityProof");
|
|
26
|
+
/** The envelope scheme. A scheme change is a new literal, never a mutation. */
|
|
27
|
+
exports.WEB_IDENTITY_ENVELOPE_VERSION = 2;
|
|
28
|
+
/**
|
|
29
|
+
* What an envelope seals. A raw-key identity stays a raw-key identity:
|
|
30
|
+
* nothing ever derives or displays a phrase for it.
|
|
31
|
+
*/
|
|
32
|
+
exports.WEB_IDENTITY_SECRET_KINDS = ['mnemonic-entropy', 'raw-private-key'];
|
|
33
|
+
const hex = (bytes, label) => zod_1.z
|
|
34
|
+
.string()
|
|
35
|
+
.trim()
|
|
36
|
+
.regex(new RegExp(`^[0-9a-fA-F]{${bytes * 2}}$`), `${label} must be ${bytes * 2} hex characters`);
|
|
37
|
+
/**
|
|
38
|
+
* The identity's secp256k1 public key in Oxy's canonical form: uncompressed SEC1
|
|
39
|
+
* (`04` + 64 bytes), lowercase hex — what `KeyManager.derivePublicKey` produces
|
|
40
|
+
* and `users.public_key` stores.
|
|
41
|
+
*/
|
|
42
|
+
exports.webIdentityPublicKeySchema = zod_1.z
|
|
43
|
+
.string()
|
|
44
|
+
.trim()
|
|
45
|
+
.regex(/^04[0-9a-f]{128}$/, 'publicKey must be an uncompressed, lowercase secp256k1 key (130 hex characters)');
|
|
46
|
+
/** A WebAuthn credential id, base64url as the browser reports it. */
|
|
47
|
+
exports.webauthnCredentialIdSchema = zod_1.z
|
|
48
|
+
.string()
|
|
49
|
+
.trim()
|
|
50
|
+
.min(16)
|
|
51
|
+
.max(1024)
|
|
52
|
+
.regex(/^[A-Za-z0-9_-]+$/, 'credentialId must be base64url');
|
|
53
|
+
/** A WebAuthn RP ID: a bare registrable host name, lowercase. */
|
|
54
|
+
exports.webauthnRpIdSchema = zod_1.z
|
|
55
|
+
.string()
|
|
56
|
+
.trim()
|
|
57
|
+
.min(1)
|
|
58
|
+
.max(253)
|
|
59
|
+
.regex(/^(localhost|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)$/, 'rpId must be a lowercase host name');
|
|
60
|
+
/** One passkey's wrap of the envelope's data key. */
|
|
61
|
+
exports.webIdentityWrapSchema = zod_1.z.object({
|
|
62
|
+
credentialId: exports.webauthnCredentialIdSchema,
|
|
63
|
+
/** 24-byte XChaCha20-Poly1305 nonce. */
|
|
64
|
+
nonce: hex(24, 'nonce'),
|
|
65
|
+
/** The 32-byte DEK sealed under this passkey's KEK, with the 16-byte tag appended (48 bytes). */
|
|
66
|
+
wrappedKey: hex(48, 'wrappedKey'),
|
|
67
|
+
createdAt: zod_1.z.string().datetime(),
|
|
68
|
+
/** The RP ID the passkey was created under, asserted explicitly by every later ceremony (ADR 0024 D2). */
|
|
69
|
+
rpId: exports.webauthnRpIdSchema,
|
|
70
|
+
/**
|
|
71
|
+
* When this passkey's PRF output was shown to open the envelope. A wrap is a
|
|
72
|
+
* root HOLDER only once this is set; a login passkey never is by default.
|
|
73
|
+
*/
|
|
74
|
+
verifiedAt: zod_1.z.string().datetime().optional(),
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* The sealed identity as it is stored (server copy and local copy alike).
|
|
78
|
+
*
|
|
79
|
+
* `wraps` holds one entry per passkey able to open it; at least one, and a
|
|
80
|
+
* bounded number so an envelope cannot grow without limit.
|
|
81
|
+
*/
|
|
82
|
+
exports.webIdentityEnvelopeSchema = zod_1.z
|
|
83
|
+
.object({
|
|
84
|
+
version: zod_1.z.literal(exports.WEB_IDENTITY_ENVELOPE_VERSION),
|
|
85
|
+
algorithm: zod_1.z.literal('xchacha20poly1305'),
|
|
86
|
+
publicKey: exports.webIdentityPublicKeySchema,
|
|
87
|
+
secretKind: zod_1.z.enum(exports.WEB_IDENTITY_SECRET_KINDS),
|
|
88
|
+
/** 24-byte nonce of the secret seal. */
|
|
89
|
+
secretNonce: hex(24, 'secretNonce'),
|
|
90
|
+
/**
|
|
91
|
+
* The sealed secret, tag appended: 16/20/24/28/32 bytes of BIP-39 entropy
|
|
92
|
+
* (12–24 words) or a 32-byte private key, plus 16.
|
|
93
|
+
*/
|
|
94
|
+
sealedSecret: zod_1.z
|
|
95
|
+
.string()
|
|
96
|
+
.trim()
|
|
97
|
+
.regex(/^(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{72}|[0-9a-fA-F]{80}|[0-9a-fA-F]{88}|[0-9a-fA-F]{96})$/, 'sealedSecret has an unsupported length'),
|
|
98
|
+
wraps: zod_1.z.array(exports.webIdentityWrapSchema).min(1).max(10),
|
|
99
|
+
})
|
|
100
|
+
.refine((envelope) => envelope.secretKind === 'mnemonic-entropy' || envelope.sealedSecret.length === 96, {
|
|
101
|
+
message: 'a raw private key seals to 48 bytes',
|
|
102
|
+
path: ['sealedSecret'],
|
|
103
|
+
});
|
|
104
|
+
/**
|
|
105
|
+
* `PUT /identity/web-envelope` — store or replace the caller's envelope.
|
|
106
|
+
*
|
|
107
|
+
* Refused unless `envelope.publicKey` is the identity key already linked to the
|
|
108
|
+
* account: an envelope can only ever carry the account's own identity.
|
|
109
|
+
*/
|
|
110
|
+
exports.webIdentityEnvelopeUploadSchema = zod_1.z.object({
|
|
111
|
+
envelope: exports.webIdentityEnvelopeSchema,
|
|
112
|
+
});
|
|
113
|
+
/** A root holder as the status read reports it — metadata only, nothing that opens anything. */
|
|
114
|
+
exports.webIdentityHolderSchema = zod_1.z.object({
|
|
115
|
+
credentialId: exports.webauthnCredentialIdSchema,
|
|
116
|
+
rpId: exports.webauthnRpIdSchema,
|
|
117
|
+
verifiedAt: zod_1.z.string().datetime().nullable(),
|
|
118
|
+
createdAt: zod_1.z.string().datetime(),
|
|
119
|
+
});
|
|
120
|
+
/**
|
|
121
|
+
* `GET /identity/web-envelope` — the caller's envelope and the readiness facts
|
|
122
|
+
* ADR 0024 D5 keeps separate. A client decides what to show from these fields
|
|
123
|
+
* WITHOUT decrypting anything.
|
|
124
|
+
*/
|
|
125
|
+
exports.webIdentityEnvelopeResponseSchema = zod_1.z.object({
|
|
126
|
+
envelope: exports.webIdentityEnvelopeSchema.nullable(),
|
|
127
|
+
/** The revision a write must name as `expectedRevision`; `0` when there is no envelope. */
|
|
128
|
+
revision: zod_1.z.number().int().nonnegative(),
|
|
129
|
+
/** Whether the account has a linked root at all (it may live only in Commons). */
|
|
130
|
+
rootLinked: zod_1.z.boolean(),
|
|
131
|
+
/** The web wraps, as metadata. */
|
|
132
|
+
holders: zod_1.z.array(exports.webIdentityHolderSchema),
|
|
133
|
+
/** When the owner confirmed the recovery material is written down, or `null`. */
|
|
134
|
+
phraseConfirmedAt: zod_1.z.string().datetime().nullable(),
|
|
135
|
+
/** When the recovery material was shown to re-derive this root, or `null`. */
|
|
136
|
+
recoveryVerifiedAt: zod_1.z.string().datetime().nullable(),
|
|
137
|
+
updatedAt: zod_1.z.string().datetime().nullable(),
|
|
138
|
+
});
|
|
139
|
+
/** A root proof, plus the envelope revision the write expects to replace. */
|
|
140
|
+
exports.webIdentityEnvelopeProofFieldsSchema = zod_1.z.object({
|
|
141
|
+
proof: identityProof_1.identityProofSchema,
|
|
142
|
+
expectedRevision: zod_1.z.number().int().nonnegative(),
|
|
143
|
+
});
|
|
144
|
+
/**
|
|
145
|
+
* `POST /identity/web-envelope/phrase-confirmed`, `/recovery-verified` and
|
|
146
|
+
* `DELETE /identity/web-envelope` prove control of the root, not just a bearer.
|
|
147
|
+
*/
|
|
148
|
+
exports.webIdentityEnvelopeActionSchema = exports.webIdentityEnvelopeProofFieldsSchema.strict();
|
|
149
|
+
/** `PUT /identity/web-envelope` body. */
|
|
150
|
+
exports.webIdentityEnvelopePutSchema = exports.webIdentityEnvelopeUploadSchema.extend(exports.webIdentityEnvelopeProofFieldsSchema.shape).strict();
|
|
151
|
+
/**
|
|
152
|
+
* A WebAuthn assertion by one of the account's EXISTING passkeys whose
|
|
153
|
+
* `clientDataJSON.challenge` is the proof challenge — the fresh use of the
|
|
154
|
+
* existing factor a keyless account needs before its first root is linked.
|
|
155
|
+
*/
|
|
156
|
+
exports.webauthnAssertionResponseSchema = zod_1.z
|
|
157
|
+
.object({
|
|
158
|
+
id: exports.webauthnCredentialIdSchema,
|
|
159
|
+
rawId: zod_1.z.string().min(1).max(2048),
|
|
160
|
+
type: zod_1.z.literal('public-key'),
|
|
161
|
+
response: zod_1.z
|
|
162
|
+
.object({
|
|
163
|
+
clientDataJSON: zod_1.z.string().min(1).max(8192),
|
|
164
|
+
authenticatorData: zod_1.z.string().min(1).max(8192),
|
|
165
|
+
signature: zod_1.z.string().min(1).max(2048),
|
|
166
|
+
userHandle: zod_1.z.string().max(2048).optional(),
|
|
167
|
+
})
|
|
168
|
+
.passthrough(),
|
|
169
|
+
clientExtensionResults: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
170
|
+
authenticatorAttachment: zod_1.z.string().optional(),
|
|
171
|
+
})
|
|
172
|
+
.passthrough();
|
|
173
|
+
/**
|
|
174
|
+
* `POST /identity/web-envelope/establish` body — an account's FIRST root, linked
|
|
175
|
+
* and stored with its envelope in ONE transaction: one root proof
|
|
176
|
+
* (`web_envelope_establish`, digest of the envelope) plus a fresh `assertion` by
|
|
177
|
+
* an existing passkey over the same challenge.
|
|
178
|
+
*/
|
|
179
|
+
exports.webIdentityEnvelopeEstablishSchema = exports.webIdentityEnvelopeUploadSchema
|
|
180
|
+
.extend({ proof: identityProof_1.identityProofSchema, assertion: exports.webauthnAssertionResponseSchema })
|
|
181
|
+
.strict();
|
package/dist/cjs/webauthn.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.webauthnLoginVerifyRequestSchema = exports.webauthnRegisterVerifyRequestSchema = exports.webauthnLoginOptionsRequestSchema = exports.webauthnRegisterOptionsRequestSchema = void 0;
|
|
16
16
|
const zod_1 = require("zod");
|
|
17
|
+
const identityProof_1 = require("./identityProof");
|
|
18
|
+
const webIdentityCarrier_1 = require("./webIdentityCarrier");
|
|
17
19
|
/**
|
|
18
20
|
* Device-session options shared by every first-party sign-in body
|
|
19
21
|
* (`deviceName`/`deviceFingerprint`). Mirrors what
|
|
@@ -59,6 +61,18 @@ exports.webauthnLoginOptionsRequestSchema = zod_1.z.object({
|
|
|
59
61
|
*/
|
|
60
62
|
exports.webauthnRegisterVerifyRequestSchema = zod_1.z.object({
|
|
61
63
|
username: zod_1.z.string().trim().min(1).max(60).optional(),
|
|
64
|
+
/**
|
|
65
|
+
* Sign-up only (ADR 0024 D4): the account's root, created on the holder BEFORE
|
|
66
|
+
* this request — sealed under the passkey being registered — and a root proof
|
|
67
|
+
* (`enroll_identity`) whose challenge is the registration challenge. The
|
|
68
|
+
* account, passkey, root and envelope are then created in one transaction.
|
|
69
|
+
*/
|
|
70
|
+
identity: zod_1.z
|
|
71
|
+
.object({
|
|
72
|
+
envelope: webIdentityCarrier_1.webIdentityEnvelopeSchema,
|
|
73
|
+
proof: identityProof_1.identityProofSchema,
|
|
74
|
+
})
|
|
75
|
+
.optional(),
|
|
62
76
|
...deviceSessionEnvelope,
|
|
63
77
|
});
|
|
64
78
|
/**
|