@atcute/lexicons 1.2.9 → 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/interfaces/blob.d.ts +25 -2
- package/dist/interfaces/blob.d.ts.map +1 -1
- package/dist/interfaces/blob.js +54 -0
- package/dist/interfaces/blob.js.map +1 -1
- package/dist/interfaces/bytes.d.ts.map +1 -1
- package/dist/interfaces/bytes.js.map +1 -1
- package/dist/interfaces/cid-link.d.ts.map +1 -1
- package/dist/interfaces/cid-link.js.map +1 -1
- package/dist/interfaces/index.d.ts +1 -1
- package/dist/interfaces/index.d.ts.map +1 -1
- package/dist/interfaces/index.js +1 -1
- package/dist/interfaces/index.js.map +1 -1
- package/dist/syntax/at-identifier.d.ts.map +1 -1
- package/dist/syntax/at-identifier.js.map +1 -1
- package/dist/syntax/at-uri.d.ts +1 -1
- package/dist/syntax/at-uri.d.ts.map +1 -1
- package/dist/syntax/at-uri.js.map +1 -1
- package/dist/syntax/cid.d.ts +1 -1
- package/dist/syntax/cid.d.ts.map +1 -1
- package/dist/syntax/cid.js.map +1 -1
- package/dist/syntax/datetime.d.ts +1 -1
- package/dist/syntax/datetime.d.ts.map +1 -1
- package/dist/syntax/datetime.js.map +1 -1
- package/dist/syntax/did.d.ts +1 -1
- package/dist/syntax/did.d.ts.map +1 -1
- package/dist/syntax/did.js.map +1 -1
- package/dist/syntax/handle.d.ts +1 -1
- package/dist/syntax/handle.d.ts.map +1 -1
- package/dist/syntax/handle.js.map +1 -1
- package/dist/syntax/language.d.ts +1 -1
- package/dist/syntax/language.d.ts.map +1 -1
- package/dist/syntax/language.js.map +1 -1
- package/dist/syntax/nsid.d.ts +1 -1
- package/dist/syntax/nsid.d.ts.map +1 -1
- package/dist/syntax/nsid.js.map +1 -1
- package/dist/syntax/record-key.d.ts +1 -1
- package/dist/syntax/record-key.d.ts.map +1 -1
- package/dist/syntax/record-key.js.map +1 -1
- package/dist/syntax/tid.d.ts +1 -1
- package/dist/syntax/tid.d.ts.map +1 -1
- package/dist/syntax/tid.js.map +1 -1
- package/dist/syntax/uri.d.ts +1 -1
- package/dist/syntax/uri.d.ts.map +1 -1
- package/dist/syntax/uri.js.map +1 -1
- package/dist/syntax/utils/ascii.d.ts.map +1 -1
- package/dist/syntax/utils/ascii.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/validations/index.d.ts +41 -11
- package/dist/validations/index.d.ts.map +1 -1
- package/dist/validations/index.js +89 -9
- package/dist/validations/index.js.map +1 -1
- package/dist/validations/utils.d.ts.map +1 -1
- package/dist/validations/utils.js.map +1 -1
- package/lib/interfaces/blob.ts +78 -0
- package/lib/interfaces/index.ts +9 -1
- package/lib/validations/index.ts +125 -9
- package/package.json +5 -5
package/lib/validations/index.ts
CHANGED
|
@@ -68,6 +68,8 @@ export type IssueLeaf = { ok: false; msg: IssueFormatter } & (
|
|
|
68
68
|
| { code: 'invalid_string_length'; minLength: number; maxLength: number }
|
|
69
69
|
| { code: 'invalid_array_length'; minLength: number; maxLength: number }
|
|
70
70
|
| { code: 'invalid_bytes_size'; minSize: number; maxSize: number }
|
|
71
|
+
| { code: 'invalid_blob_size'; maxSize: number }
|
|
72
|
+
| { code: 'invalid_blob_mime_type'; accept: readonly string[] }
|
|
71
73
|
);
|
|
72
74
|
|
|
73
75
|
export type IssueTree =
|
|
@@ -85,7 +87,9 @@ export type Issue =
|
|
|
85
87
|
| { code: 'invalid_string_graphemes'; path: Key[]; minGraphemes: number; maxGraphemes: number }
|
|
86
88
|
| { code: 'invalid_string_length'; path: Key[]; minLength: number; maxLength: number }
|
|
87
89
|
| { code: 'invalid_array_length'; path: Key[]; minLength: number; maxLength: number }
|
|
88
|
-
| { code: 'invalid_bytes_size'; path: Key[]; minSize: number; maxSize: number }
|
|
90
|
+
| { code: 'invalid_bytes_size'; path: Key[]; minSize: number; maxSize: number }
|
|
91
|
+
| { code: 'invalid_blob_size'; path: Key[]; maxSize: number }
|
|
92
|
+
| { code: 'invalid_blob_mime_type'; path: Key[]; accept: readonly string[] };
|
|
89
93
|
|
|
90
94
|
// #__NO_SIDE_EFFECTS__
|
|
91
95
|
const joinIssues = (left: IssueTree | undefined, right: IssueTree): IssueTree => {
|
|
@@ -127,6 +131,8 @@ type kType = typeof kType;
|
|
|
127
131
|
export const FLAG_EMPTY = 0;
|
|
128
132
|
// Don't continue validation if an error is encountered
|
|
129
133
|
export const FLAG_ABORT_EARLY = 1 << 0;
|
|
134
|
+
// Enable strict blob validation (size, MIME type constraints, reject legacy blobs)
|
|
135
|
+
export const FLAG_STRICT = 1 << 1;
|
|
130
136
|
|
|
131
137
|
type MatcherResult = undefined | Ok<unknown> | IssueTree;
|
|
132
138
|
type Matcher = (input: unknown, flags: number) => MatcherResult;
|
|
@@ -313,12 +319,22 @@ class ErrImpl implements Err {
|
|
|
313
319
|
}
|
|
314
320
|
}
|
|
315
321
|
|
|
322
|
+
export interface ValidationOptions {
|
|
323
|
+
/** enable strict blob validation (size, MIME type constraints, reject legacy blobs) */
|
|
324
|
+
strict?: boolean;
|
|
325
|
+
}
|
|
326
|
+
|
|
316
327
|
// #__NO_SIDE_EFFECTS__
|
|
317
328
|
export const is = <const TSchema extends BaseSchema>(
|
|
318
329
|
schema: TSchema,
|
|
319
330
|
input: unknown,
|
|
331
|
+
options?: ValidationOptions,
|
|
320
332
|
): input is InferInput<TSchema> => {
|
|
321
|
-
|
|
333
|
+
let flags = FLAG_ABORT_EARLY;
|
|
334
|
+
if (options?.strict) {
|
|
335
|
+
flags |= FLAG_STRICT;
|
|
336
|
+
}
|
|
337
|
+
const r = schema['~run'](input, flags);
|
|
322
338
|
return r === undefined || r.ok;
|
|
323
339
|
};
|
|
324
340
|
|
|
@@ -326,8 +342,13 @@ export const is = <const TSchema extends BaseSchema>(
|
|
|
326
342
|
export const safeParse = <const TSchema extends BaseSchema>(
|
|
327
343
|
schema: TSchema,
|
|
328
344
|
input: unknown,
|
|
345
|
+
options?: ValidationOptions,
|
|
329
346
|
): ValidationResult<InferOutput<TSchema>> => {
|
|
330
|
-
|
|
347
|
+
let flags = FLAG_EMPTY;
|
|
348
|
+
if (options?.strict) {
|
|
349
|
+
flags |= FLAG_STRICT;
|
|
350
|
+
}
|
|
351
|
+
const r = schema['~run'](input, flags);
|
|
331
352
|
|
|
332
353
|
if (r === undefined) {
|
|
333
354
|
return ok(input as InferOutput<TSchema>);
|
|
@@ -343,8 +364,13 @@ export const safeParse = <const TSchema extends BaseSchema>(
|
|
|
343
364
|
export const parse = <const TSchema extends BaseSchema>(
|
|
344
365
|
schema: TSchema,
|
|
345
366
|
input: unknown,
|
|
367
|
+
options?: ValidationOptions,
|
|
346
368
|
): InferOutput<TSchema> => {
|
|
347
|
-
|
|
369
|
+
let flags = FLAG_EMPTY;
|
|
370
|
+
if (options?.strict) {
|
|
371
|
+
flags |= FLAG_STRICT;
|
|
372
|
+
}
|
|
373
|
+
const r = schema['~run'](input, flags);
|
|
348
374
|
|
|
349
375
|
if (r === undefined) {
|
|
350
376
|
return input as InferOutput<TSchema>;
|
|
@@ -886,7 +912,7 @@ const ISSUE_EXPECTED_BLOB: IssueLeaf = {
|
|
|
886
912
|
const BLOB_SCHEMA: BlobSchema = {
|
|
887
913
|
kind: 'schema',
|
|
888
914
|
type: 'blob',
|
|
889
|
-
'~run'(input,
|
|
915
|
+
'~run'(input, flags) {
|
|
890
916
|
if (typeof input !== 'object' || input === null) {
|
|
891
917
|
return ISSUE_EXPECTED_BLOB;
|
|
892
918
|
}
|
|
@@ -895,7 +921,7 @@ const BLOB_SCHEMA: BlobSchema = {
|
|
|
895
921
|
return undefined;
|
|
896
922
|
}
|
|
897
923
|
|
|
898
|
-
if (interfaces.isLegacyBlob(input)) {
|
|
924
|
+
if (!(flags & FLAG_STRICT) && interfaces.isLegacyBlob(input)) {
|
|
899
925
|
const blob: interfaces.Blob = {
|
|
900
926
|
$type: 'blob',
|
|
901
927
|
mimeType: input.mimeType,
|
|
@@ -918,6 +944,94 @@ export const blob = (): BlobSchema => {
|
|
|
918
944
|
return BLOB_SCHEMA;
|
|
919
945
|
};
|
|
920
946
|
|
|
947
|
+
// #region Blob constraints
|
|
948
|
+
|
|
949
|
+
export interface BlobSizeConstraint<
|
|
950
|
+
TMaxSize extends number = number,
|
|
951
|
+
> extends BaseConstraint<interfaces.Blob> {
|
|
952
|
+
readonly type: 'blob_size';
|
|
953
|
+
readonly maxSize: TMaxSize;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// #__NO_SIDE_EFFECTS__
|
|
957
|
+
export const blobSize = <const TMaxSize extends number>(maxSize: TMaxSize): BlobSizeConstraint<TMaxSize> => {
|
|
958
|
+
const issue: IssueLeaf = {
|
|
959
|
+
ok: false,
|
|
960
|
+
code: 'invalid_blob_size',
|
|
961
|
+
maxSize: maxSize,
|
|
962
|
+
msg() {
|
|
963
|
+
return `blob size must not exceed ${maxSize} bytes`;
|
|
964
|
+
},
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
return {
|
|
968
|
+
kind: 'constraint',
|
|
969
|
+
type: 'blob_size',
|
|
970
|
+
maxSize: maxSize,
|
|
971
|
+
'~run'(input, flags) {
|
|
972
|
+
if (!(flags & FLAG_STRICT)) {
|
|
973
|
+
return undefined;
|
|
974
|
+
}
|
|
975
|
+
if ((input as interfaces.Blob).size > maxSize) {
|
|
976
|
+
return issue;
|
|
977
|
+
}
|
|
978
|
+
return undefined;
|
|
979
|
+
},
|
|
980
|
+
};
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
export interface BlobAcceptConstraint extends BaseConstraint<interfaces.Blob> {
|
|
984
|
+
readonly type: 'blob_accept';
|
|
985
|
+
readonly accept: readonly string[];
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// #__NO_SIDE_EFFECTS__
|
|
989
|
+
export const blobAccept = (accept: readonly string[]): BlobAcceptConstraint => {
|
|
990
|
+
const normalized = accept.map((p) => p.toLowerCase());
|
|
991
|
+
|
|
992
|
+
const issue: IssueLeaf = {
|
|
993
|
+
ok: false,
|
|
994
|
+
code: 'invalid_blob_mime_type',
|
|
995
|
+
accept: accept,
|
|
996
|
+
msg() {
|
|
997
|
+
return `blob MIME type must match: ${accept.join(', ')}`;
|
|
998
|
+
},
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
return {
|
|
1002
|
+
kind: 'constraint',
|
|
1003
|
+
type: 'blob_accept',
|
|
1004
|
+
accept: accept,
|
|
1005
|
+
'~run'(input, flags) {
|
|
1006
|
+
if (!(flags & FLAG_STRICT)) {
|
|
1007
|
+
return undefined;
|
|
1008
|
+
}
|
|
1009
|
+
const mimeType = (input as interfaces.Blob).mimeType.toLowerCase();
|
|
1010
|
+
|
|
1011
|
+
for (let idx = 0, len = normalized.length; idx < len; idx++) {
|
|
1012
|
+
const pattern = normalized[idx];
|
|
1013
|
+
|
|
1014
|
+
if (pattern === '*/*') {
|
|
1015
|
+
return undefined;
|
|
1016
|
+
}
|
|
1017
|
+
if (pattern.endsWith('/*')) {
|
|
1018
|
+
if (mimeType.startsWith(pattern.slice(0, -1))) {
|
|
1019
|
+
return undefined;
|
|
1020
|
+
}
|
|
1021
|
+
} else {
|
|
1022
|
+
if (mimeType === pattern) {
|
|
1023
|
+
return undefined;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
return issue;
|
|
1029
|
+
},
|
|
1030
|
+
};
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
// #endregion
|
|
1034
|
+
|
|
921
1035
|
// #region IPLD bytes schema
|
|
922
1036
|
|
|
923
1037
|
export interface BytesSchema extends BaseSchema<interfaces.Bytes, interfaces.Bytes> {
|
|
@@ -1601,7 +1715,8 @@ export const record = <TKey extends RecordKeySchema, TObject extends ObjectSchem
|
|
|
1601
1715
|
|
|
1602
1716
|
// #region Variant schema
|
|
1603
1717
|
|
|
1604
|
-
type
|
|
1718
|
+
type VariantMember = ObjectSchema<any> | RecordSchema<ObjectSchema<any>, RecordKeySchema>;
|
|
1719
|
+
type VariantTuple = readonly VariantMember[];
|
|
1605
1720
|
|
|
1606
1721
|
type InferVariantInput<TMembers extends VariantTuple> = $type.enforce<InferInput<TMembers[number]>>;
|
|
1607
1722
|
|
|
@@ -1629,7 +1744,7 @@ export const variant: {
|
|
|
1629
1744
|
members: TMembers,
|
|
1630
1745
|
closed: TClosed,
|
|
1631
1746
|
): VariantSchema<TMembers, TClosed>;
|
|
1632
|
-
} = (members:
|
|
1747
|
+
} = (members: VariantMember[], closed: boolean = false): VariantSchema<any, any> => {
|
|
1633
1748
|
return {
|
|
1634
1749
|
kind: 'schema',
|
|
1635
1750
|
type: 'variant',
|
|
@@ -1640,7 +1755,8 @@ export const variant: {
|
|
|
1640
1755
|
const schemas: ObjectSchema[] = [];
|
|
1641
1756
|
|
|
1642
1757
|
for (let idx = 0, len = members.length; idx < len; idx++) {
|
|
1643
|
-
const
|
|
1758
|
+
const raw = members[idx]!;
|
|
1759
|
+
const member = raw.type === 'record' ? raw.object : raw;
|
|
1644
1760
|
const shape = member.shape;
|
|
1645
1761
|
|
|
1646
1762
|
let t = shape.$type as MaybeOptional<LiteralSchema<syntax.Nsid>> | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atcute/lexicons",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "AT Protocol core lexicon types and schema validations",
|
|
5
5
|
"license": "0BSD",
|
|
6
6
|
"repository": {
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
"@standard-schema/spec": "^1.1.0",
|
|
30
30
|
"esm-env": "^1.2.2",
|
|
31
31
|
"@atcute/uint8array": "^1.1.1",
|
|
32
|
-
"@atcute/util-text": "^1.
|
|
32
|
+
"@atcute/util-text": "^1.2.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@vitest/coverage-v8": "^4.
|
|
36
|
-
"vitest": "^4.
|
|
35
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
36
|
+
"vitest": "^4.1.2",
|
|
37
37
|
"@atcute/cbor": "^2.3.2",
|
|
38
|
-
"@atcute/multibase": "^1.
|
|
38
|
+
"@atcute/multibase": "^1.2.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsgo --project tsconfig.build.json",
|