@kravc/schema 2.7.6 → 2.8.0-alpha.1
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 +19 -14
- package/dist/CredentialFactory.d.ts +345 -0
- package/dist/CredentialFactory.d.ts.map +1 -0
- package/dist/CredentialFactory.js +381 -0
- package/dist/CredentialFactory.js.map +1 -0
- package/dist/Schema.d.ts +448 -0
- package/dist/Schema.d.ts.map +1 -0
- package/dist/Schema.js +506 -0
- package/dist/Schema.js.map +1 -0
- package/dist/ValidationError.d.ts +70 -0
- package/dist/ValidationError.d.ts.map +1 -0
- package/dist/ValidationError.js +78 -0
- package/dist/ValidationError.js.map +1 -0
- package/dist/Validator.d.ts +483 -0
- package/dist/Validator.d.ts.map +1 -0
- package/dist/Validator.js +570 -0
- package/dist/Validator.js.map +1 -0
- package/dist/helpers/JsonSchema.d.ts +99 -0
- package/dist/helpers/JsonSchema.d.ts.map +1 -0
- package/dist/helpers/JsonSchema.js +3 -0
- package/dist/helpers/JsonSchema.js.map +1 -0
- package/dist/helpers/cleanupAttributes.d.ts +34 -0
- package/dist/helpers/cleanupAttributes.d.ts.map +1 -0
- package/dist/helpers/cleanupAttributes.js +113 -0
- package/dist/helpers/cleanupAttributes.js.map +1 -0
- package/dist/helpers/cleanupNulls.d.ts +27 -0
- package/dist/helpers/cleanupNulls.d.ts.map +1 -0
- package/dist/helpers/cleanupNulls.js +96 -0
- package/dist/helpers/cleanupNulls.js.map +1 -0
- package/dist/helpers/createSchemasMap.d.ts +67 -0
- package/dist/helpers/createSchemasMap.d.ts.map +1 -0
- package/dist/helpers/createSchemasMap.js +200 -0
- package/dist/helpers/createSchemasMap.js.map +1 -0
- package/dist/helpers/getReferenceIds.d.ts +169 -0
- package/dist/helpers/getReferenceIds.d.ts.map +1 -0
- package/dist/helpers/getReferenceIds.js +241 -0
- package/dist/helpers/getReferenceIds.js.map +1 -0
- package/dist/helpers/got.d.ts +60 -0
- package/dist/helpers/got.d.ts.map +1 -0
- package/dist/helpers/got.js +72 -0
- package/dist/helpers/got.js.map +1 -0
- package/dist/helpers/mapObjectProperties.d.ts +150 -0
- package/dist/helpers/mapObjectProperties.d.ts.map +1 -0
- package/dist/helpers/mapObjectProperties.js +229 -0
- package/dist/helpers/mapObjectProperties.js.map +1 -0
- package/dist/helpers/normalizeAttributes.d.ts +213 -0
- package/dist/helpers/normalizeAttributes.d.ts.map +1 -0
- package/dist/helpers/normalizeAttributes.js +243 -0
- package/dist/helpers/normalizeAttributes.js.map +1 -0
- package/dist/helpers/normalizeProperties.d.ts +168 -0
- package/dist/helpers/normalizeProperties.d.ts.map +1 -0
- package/dist/helpers/normalizeProperties.js +223 -0
- package/dist/helpers/normalizeProperties.js.map +1 -0
- package/dist/helpers/normalizeRequired.d.ts +159 -0
- package/dist/helpers/normalizeRequired.d.ts.map +1 -0
- package/dist/helpers/normalizeRequired.js +206 -0
- package/dist/helpers/normalizeRequired.js.map +1 -0
- package/dist/helpers/normalizeType.d.ts +81 -0
- package/dist/helpers/normalizeType.d.ts.map +1 -0
- package/dist/helpers/normalizeType.js +210 -0
- package/dist/helpers/normalizeType.js.map +1 -0
- package/dist/helpers/nullifyEmptyValues.d.ts +139 -0
- package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -0
- package/dist/helpers/nullifyEmptyValues.js +191 -0
- package/dist/helpers/nullifyEmptyValues.js.map +1 -0
- package/dist/helpers/removeRequiredAndDefault.d.ts +106 -0
- package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -0
- package/dist/helpers/removeRequiredAndDefault.js +138 -0
- package/dist/helpers/removeRequiredAndDefault.js.map +1 -0
- package/dist/helpers/validateId.d.ts +39 -0
- package/dist/helpers/validateId.d.ts.map +1 -0
- package/dist/helpers/validateId.js +51 -0
- package/dist/helpers/validateId.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/ld/documentLoader.d.ts +8 -0
- package/dist/ld/documentLoader.d.ts.map +1 -0
- package/dist/ld/documentLoader.js +24 -0
- package/dist/ld/documentLoader.js.map +1 -0
- package/dist/ld/getLinkedDataAttributeType.d.ts +10 -0
- package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -0
- package/dist/ld/getLinkedDataAttributeType.js +32 -0
- package/dist/ld/getLinkedDataAttributeType.js.map +1 -0
- package/dist/ld/getLinkedDataContext.d.ts +19 -0
- package/dist/ld/getLinkedDataContext.d.ts.map +1 -0
- package/dist/ld/getLinkedDataContext.js +50 -0
- package/dist/ld/getLinkedDataContext.js.map +1 -0
- package/eslint.config.mjs +32 -52
- package/examples/credentials/createAccountCredential.ts +27 -0
- package/examples/credentials/createMineSweeperScoreCredential.ts +115 -0
- package/examples/index.ts +7 -0
- package/examples/schemas/FavoriteItemSchema.ts +27 -0
- package/examples/{Preferences.yaml → schemas/Preferences.yaml} +2 -0
- package/examples/schemas/PreferencesSchema.ts +29 -0
- package/examples/schemas/ProfileSchema.ts +91 -0
- package/examples/schemas/Status.yaml +3 -0
- package/examples/schemas/StatusSchema.ts +12 -0
- package/jest.config.mjs +5 -0
- package/package.json +27 -20
- package/src/CredentialFactory.ts +392 -0
- package/src/Schema.ts +583 -0
- package/src/ValidationError.ts +90 -0
- package/src/Validator.ts +603 -0
- package/src/__tests__/CredentialFactory.test.ts +588 -0
- package/src/__tests__/Schema.test.ts +371 -0
- package/src/__tests__/ValidationError.test.ts +235 -0
- package/src/__tests__/Validator.test.ts +787 -0
- package/src/helpers/JsonSchema.ts +119 -0
- package/src/helpers/__tests__/cleanupAttributes.test.ts +943 -0
- package/src/helpers/__tests__/cleanupNulls.test.ts +772 -0
- package/src/helpers/__tests__/createSchemasMap.test.ts +238 -0
- package/src/helpers/__tests__/getReferenceIds.test.ts +975 -0
- package/src/helpers/__tests__/got.test.ts +193 -0
- package/src/helpers/__tests__/mapObjectProperties.test.ts +1126 -0
- package/src/helpers/__tests__/normalizeAttributes.test.ts +1435 -0
- package/src/helpers/__tests__/normalizeProperties.test.ts +727 -0
- package/src/helpers/__tests__/normalizeRequired.test.ts +669 -0
- package/src/helpers/__tests__/normalizeType.test.ts +772 -0
- package/src/helpers/__tests__/nullifyEmptyValues.test.ts +735 -0
- package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +734 -0
- package/src/helpers/__tests__/validateId.test.ts +118 -0
- package/src/helpers/cleanupAttributes.ts +151 -0
- package/src/helpers/cleanupNulls.ts +106 -0
- package/src/helpers/createSchemasMap.ts +212 -0
- package/src/helpers/getReferenceIds.ts +273 -0
- package/src/helpers/got.ts +73 -0
- package/src/helpers/mapObjectProperties.ts +272 -0
- package/src/helpers/normalizeAttributes.ts +247 -0
- package/src/helpers/normalizeProperties.ts +249 -0
- package/src/helpers/normalizeRequired.ts +233 -0
- package/src/helpers/normalizeType.ts +235 -0
- package/src/helpers/nullifyEmptyValues.ts +207 -0
- package/src/helpers/removeRequiredAndDefault.ts +151 -0
- package/src/helpers/validateId.ts +53 -0
- package/src/index.ts +17 -0
- package/src/ld/__tests__/documentLoader.test.ts +57 -0
- package/src/ld/__tests__/getLinkedDataAttributeType.test.ts +212 -0
- package/src/ld/__tests__/getLinkedDataContext.test.ts +378 -0
- package/src/ld/documentLoader.ts +28 -0
- package/src/ld/getLinkedDataAttributeType.ts +46 -0
- package/src/ld/getLinkedDataContext.ts +80 -0
- package/tsconfig.json +27 -0
- package/types/credentials-context.d.ts +14 -0
- package/types/security-context.d.ts +6 -0
- package/examples/Status.yaml +0 -3
- package/examples/createAccountCredential.js +0 -27
- package/examples/createMineSweeperScoreCredential.js +0 -63
- package/examples/index.js +0 -9
- package/src/CredentialFactory.js +0 -67
- package/src/CredentialFactory.spec.js +0 -131
- package/src/Schema.js +0 -104
- package/src/Schema.spec.js +0 -172
- package/src/ValidationError.js +0 -31
- package/src/Validator.js +0 -128
- package/src/Validator.spec.js +0 -355
- package/src/helpers/cleanupAttributes.js +0 -71
- package/src/helpers/cleanupNulls.js +0 -42
- package/src/helpers/getReferenceIds.js +0 -71
- package/src/helpers/mapObject.js +0 -65
- package/src/helpers/normalizeAttributes.js +0 -28
- package/src/helpers/normalizeProperties.js +0 -61
- package/src/helpers/normalizeRequired.js +0 -37
- package/src/helpers/normalizeType.js +0 -41
- package/src/helpers/nullifyEmptyValues.js +0 -57
- package/src/helpers/removeRequiredAndDefault.js +0 -30
- package/src/helpers/validateId.js +0 -19
- package/src/index.d.ts +0 -25
- package/src/index.js +0 -8
- package/src/ld/documentLoader.js +0 -25
- package/src/ld/documentLoader.spec.js +0 -12
- package/src/ld/getLinkedDataContext.js +0 -63
- package/src/ld/getLinkedDataType.js +0 -38
- /package/examples/{FavoriteItem.yaml → schemas/FavoriteItem.yaml} +0 -0
- /package/examples/{Profile.yaml → schemas/Profile.yaml} +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EnumSchema,
|
|
3
|
+
PropertySchema,
|
|
4
|
+
ArrayPropertySchema,
|
|
5
|
+
ObjectPropertySchema,
|
|
6
|
+
} from './JsonSchema';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Recursively removes `required` and `default` attributes from a JSON schema.
|
|
10
|
+
*
|
|
11
|
+
* **Intent:**
|
|
12
|
+
* This function is designed to transform JSON schemas by stripping out validation
|
|
13
|
+
* constraints (`required`) and default values (`default`) from all properties,
|
|
14
|
+
* including nested objects and array items. This is useful for creating "pure"
|
|
15
|
+
* or "update-friendly" versions of schemas where all fields become optional and
|
|
16
|
+
* no defaults are applied.
|
|
17
|
+
*
|
|
18
|
+
* **Use Cases:**
|
|
19
|
+
* 1. **Update Schemas**: Create schemas for partial updates where all fields are optional
|
|
20
|
+
* 2. **Schema Transformation**: Prepare schemas for contexts where required/default
|
|
21
|
+
* constraints are not needed (e.g., credential generation, API transformations)
|
|
22
|
+
* 3. **Schema Normalization**: Remove validation metadata before further processing
|
|
23
|
+
*
|
|
24
|
+
* **Behavior:**
|
|
25
|
+
* - Mutates the input schema in-place by deleting `required` and `default` properties
|
|
26
|
+
* - Recursively processes nested object properties
|
|
27
|
+
* - Recursively processes array items (including nested objects within arrays)
|
|
28
|
+
* - Preserves all other schema properties (type, description, pattern, etc.)
|
|
29
|
+
* - Returns an object containing only the `properties` field
|
|
30
|
+
*
|
|
31
|
+
* **Examples:**
|
|
32
|
+
*
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Example 1: Simple object schema
|
|
35
|
+
* const schema = {
|
|
36
|
+
* type: 'object',
|
|
37
|
+
* properties: {
|
|
38
|
+
* name: { type: 'string', required: true, default: 'John' },
|
|
39
|
+
* age: { type: 'number', required: false, default: 0 }
|
|
40
|
+
* }
|
|
41
|
+
* };
|
|
42
|
+
*
|
|
43
|
+
* const result = removeRequiredAndDefault(schema);
|
|
44
|
+
* // result.properties.name: { type: 'string' } (required and default removed)
|
|
45
|
+
* // result.properties.age: { type: 'number' } (required and default removed)
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Example 2: Nested objects
|
|
50
|
+
* const schema = {
|
|
51
|
+
* type: 'object',
|
|
52
|
+
* properties: {
|
|
53
|
+
* user: {
|
|
54
|
+
* type: 'object',
|
|
55
|
+
* required: true,
|
|
56
|
+
* default: {},
|
|
57
|
+
* properties: {
|
|
58
|
+
* name: { type: 'string', required: true, default: 'John' },
|
|
59
|
+
* address: {
|
|
60
|
+
* type: 'object',
|
|
61
|
+
* properties: {
|
|
62
|
+
* street: { type: 'string', required: true, default: '' }
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
* }
|
|
68
|
+
* };
|
|
69
|
+
*
|
|
70
|
+
* const result = removeRequiredAndDefault(schema);
|
|
71
|
+
* // All required and default attributes are removed at all nesting levels
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* ```typescript
|
|
75
|
+
* // Example 3: Arrays with object items
|
|
76
|
+
* const schema = {
|
|
77
|
+
* type: 'object',
|
|
78
|
+
* properties: {
|
|
79
|
+
* users: {
|
|
80
|
+
* type: 'array',
|
|
81
|
+
* required: true,
|
|
82
|
+
* default: [],
|
|
83
|
+
* items: {
|
|
84
|
+
* type: 'object',
|
|
85
|
+
* properties: {
|
|
86
|
+
* name: { type: 'string', required: true, default: 'John' }
|
|
87
|
+
* }
|
|
88
|
+
* }
|
|
89
|
+
* }
|
|
90
|
+
* }
|
|
91
|
+
* };
|
|
92
|
+
*
|
|
93
|
+
* const result = removeRequiredAndDefault(schema);
|
|
94
|
+
* // Removes required/default from array property and nested object properties
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* **Limitations:**
|
|
98
|
+
* - Only processes schemas with a `properties` field (ObjectPropertySchema)
|
|
99
|
+
* - EnumSchema is accepted as a parameter but not processed (will return empty properties)
|
|
100
|
+
* - The function mutates the input schema object
|
|
101
|
+
* - Array items that are primitives (string, number, etc.) are processed but
|
|
102
|
+
* their return values are discarded (this is intentional)
|
|
103
|
+
*
|
|
104
|
+
* @param jsonSchema - The JSON schema to process (must have a `properties` field)
|
|
105
|
+
* @returns An object containing only the `properties` field with all `required`
|
|
106
|
+
* and `default` attributes removed recursively
|
|
107
|
+
*/
|
|
108
|
+
const removeRequiredAndDefault = (jsonSchema: PropertySchema | EnumSchema) => {
|
|
109
|
+
const { properties } = (jsonSchema as ObjectPropertySchema);
|
|
110
|
+
|
|
111
|
+
if (!properties) {
|
|
112
|
+
return { properties: {} };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (const fieldName in properties) {
|
|
116
|
+
const property = properties[fieldName];
|
|
117
|
+
|
|
118
|
+
// Remove required and default from the property itself
|
|
119
|
+
delete property.required;
|
|
120
|
+
delete property.default;
|
|
121
|
+
|
|
122
|
+
// Check if property has a type to determine how to process it
|
|
123
|
+
const { type } = (property as ObjectPropertySchema | ArrayPropertySchema);
|
|
124
|
+
|
|
125
|
+
const isObject = type === 'object';
|
|
126
|
+
const isArray = type === 'array';
|
|
127
|
+
|
|
128
|
+
if (isObject) {
|
|
129
|
+
// Recursively process nested object properties
|
|
130
|
+
removeRequiredAndDefault(property as ObjectPropertySchema);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (isArray) {
|
|
135
|
+
const { items } = (property as ArrayPropertySchema);
|
|
136
|
+
if (items) {
|
|
137
|
+
// Recursively process array items
|
|
138
|
+
// Note: For EnumSchema/ReferencePropertySchema items without properties,
|
|
139
|
+
// this will return { properties: {} } but the items themselves are not
|
|
140
|
+
// modified (only properties within objects are processed)
|
|
141
|
+
removeRequiredAndDefault(items);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// For other types (string, number, boolean, etc.) or properties without type,
|
|
145
|
+
// no further processing is needed
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { properties };
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export default removeRequiredAndDefault;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isURL } from 'validator';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validates that a value is a URL or a DID (Decentralized Identifier).
|
|
5
|
+
* Throws if the value is missing, null, undefined, or not a valid URL/DID.
|
|
6
|
+
*
|
|
7
|
+
* **Intent**
|
|
8
|
+
* Enforce that identifier-style parameters (schema IDs, credential IDs, subject
|
|
9
|
+
* references, etc.) are either resolvable URLs or W3C DIDs before they are used
|
|
10
|
+
* in Linked Data or verification flows. This prevents malformed or arbitrary
|
|
11
|
+
* strings from propagating into storage, APIs, or cryptographic operations.
|
|
12
|
+
*
|
|
13
|
+
* **Use cases**
|
|
14
|
+
* - Validating `schemaId`, `credentialId`, or similar parameters in credential
|
|
15
|
+
* and schema factories before creating or resolving documents.
|
|
16
|
+
* - Input validation for APIs that accept URI identifiers (e.g. fetch by ID).
|
|
17
|
+
* - Guarding helpers that resolve or dereference IDs (e.g. document loaders,
|
|
18
|
+
* schema registries) from invalid input.
|
|
19
|
+
*
|
|
20
|
+
* **Examples**
|
|
21
|
+
*
|
|
22
|
+
* Valid — URLs:
|
|
23
|
+
* validateId('schemaId', 'https://example.com/schemas/v1');
|
|
24
|
+
* validateId('id', 'http://example.com:8080/path?q=1');
|
|
25
|
+
*
|
|
26
|
+
* Valid — DIDs (case-insensitive `did:` prefix):
|
|
27
|
+
* validateId('subject', 'did:example:123456789');
|
|
28
|
+
* validateId('subject', 'did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK');
|
|
29
|
+
*
|
|
30
|
+
* Throws — missing or invalid:
|
|
31
|
+
* validateId('id', ''); // Error: Parameter "id" is required
|
|
32
|
+
* validateId('id', null); // Error: Parameter "id" is required
|
|
33
|
+
* validateId('id', 'not-a-uri'); // Error: Parameter "id" must be a URL, received: "not-a-uri"
|
|
34
|
+
*
|
|
35
|
+
* @param name - Parameter name used in error messages (e.g. `"schemaId"`, `"credentialId"`).
|
|
36
|
+
* @param value - The value to validate (`string`, `null`, or `undefined`).
|
|
37
|
+
* @throws {Error} When `value` is falsy, or when it is not a URL and does not start with `did:`.
|
|
38
|
+
*/
|
|
39
|
+
const validateId = (name: string, value: string | null | undefined) => {
|
|
40
|
+
if (!value) {
|
|
41
|
+
throw new Error(`Parameter "${name}" is required`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const isURI = value.toLowerCase().startsWith('did:') || isURL(value);
|
|
45
|
+
|
|
46
|
+
if (isURI) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
throw new Error(`Parameter "${name}" must be a URL, received: "${value}"`);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default validateId;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import got from './helpers/got';
|
|
2
|
+
import Schema from './Schema';
|
|
3
|
+
import Validator from './Validator';
|
|
4
|
+
import documentLoader from './ld/documentLoader';
|
|
5
|
+
import ValidationError from './ValidationError';
|
|
6
|
+
import createSchemasMap from './helpers/createSchemasMap';
|
|
7
|
+
import CredentialFactory from './CredentialFactory';
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
got,
|
|
11
|
+
Schema,
|
|
12
|
+
Validator,
|
|
13
|
+
documentLoader,
|
|
14
|
+
ValidationError,
|
|
15
|
+
createSchemasMap,
|
|
16
|
+
CredentialFactory
|
|
17
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import documentLoader from '../documentLoader';
|
|
2
|
+
import { constants } from 'credentials-context';
|
|
3
|
+
|
|
4
|
+
describe('documentLoader(documentUrl)', () => {
|
|
5
|
+
describe('successful loading', () => {
|
|
6
|
+
it('should return document for valid credentials context URL', () => {
|
|
7
|
+
const result = documentLoader(constants.CREDENTIALS_CONTEXT_V1_URL);
|
|
8
|
+
|
|
9
|
+
expect(result).toHaveProperty('document');
|
|
10
|
+
expect(result).toHaveProperty('contextUrl', null);
|
|
11
|
+
expect(result).toHaveProperty('documentUrl', constants.CREDENTIALS_CONTEXT_V1_URL);
|
|
12
|
+
expect(result.document).toBeDefined();
|
|
13
|
+
expect(typeof result.document).toBe('object');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should handle URL with fragment', () => {
|
|
17
|
+
const urlWithFragment = `${constants.CREDENTIALS_CONTEXT_V1_URL}#VerifiableCredential`;
|
|
18
|
+
const result = documentLoader(urlWithFragment);
|
|
19
|
+
|
|
20
|
+
expect(result).toHaveProperty('document');
|
|
21
|
+
expect(result).toHaveProperty('documentUrl', urlWithFragment);
|
|
22
|
+
expect(result.document).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return correct structure', () => {
|
|
26
|
+
const result = documentLoader(constants.CREDENTIALS_CONTEXT_V1_URL);
|
|
27
|
+
|
|
28
|
+
expect(result).toEqual({
|
|
29
|
+
document: expect.any(Object),
|
|
30
|
+
contextUrl: null,
|
|
31
|
+
documentUrl: constants.CREDENTIALS_CONTEXT_V1_URL
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('error handling', () => {
|
|
37
|
+
it('should throw error if context not found', () => {
|
|
38
|
+
expect(() => {
|
|
39
|
+
documentLoader('https://example.com/unknown-context');
|
|
40
|
+
}).toThrow('Custom context "https://example.com/unknown-context" is not supported');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should throw error with full URL including fragment when context not found', () => {
|
|
44
|
+
const urlWithFragment = 'https://example.com/unknown-context#fragment';
|
|
45
|
+
|
|
46
|
+
expect(() => {
|
|
47
|
+
documentLoader(urlWithFragment);
|
|
48
|
+
}).toThrow('Custom context "https://example.com/unknown-context#fragment" is not supported');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should throw error for empty string', () => {
|
|
52
|
+
expect(() => {
|
|
53
|
+
documentLoader('');
|
|
54
|
+
}).toThrow('Custom context "" is not supported');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import getLinkedDataAttributeType, { type PropertySchema } from '../getLinkedDataAttributeType';
|
|
2
|
+
|
|
3
|
+
describe('getLinkedDataAttributeType(propertySchema)', () => {
|
|
4
|
+
describe('when @type is defined', () => {
|
|
5
|
+
it('should return the @type value when it is a string', () => {
|
|
6
|
+
const propertySchema: PropertySchema = {
|
|
7
|
+
'@type': 'schema:Text',
|
|
8
|
+
type: 'string',
|
|
9
|
+
};
|
|
10
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Text');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should return the @type value even when type and format are present', () => {
|
|
14
|
+
const propertySchema: PropertySchema = {
|
|
15
|
+
'@type': 'schema:CustomType',
|
|
16
|
+
type: 'number',
|
|
17
|
+
format: 'date',
|
|
18
|
+
};
|
|
19
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:CustomType');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return the @type value for integer type', () => {
|
|
23
|
+
const propertySchema: PropertySchema = {
|
|
24
|
+
'@type': 'schema:CustomInteger',
|
|
25
|
+
type: 'integer',
|
|
26
|
+
};
|
|
27
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:CustomInteger');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('when type is integer', () => {
|
|
32
|
+
it('should return schema:Integer for integer type', () => {
|
|
33
|
+
const propertySchema: PropertySchema = {
|
|
34
|
+
type: 'integer',
|
|
35
|
+
};
|
|
36
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Integer');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return schema:Integer even when format is present', () => {
|
|
40
|
+
const propertySchema: PropertySchema = {
|
|
41
|
+
type: 'integer',
|
|
42
|
+
format: 'int32',
|
|
43
|
+
};
|
|
44
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Integer');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('when type is number', () => {
|
|
49
|
+
it('should return schema:Number for number type', () => {
|
|
50
|
+
const propertySchema: PropertySchema = {
|
|
51
|
+
type: 'number',
|
|
52
|
+
};
|
|
53
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Number');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should return schema:Number even when format is present', () => {
|
|
57
|
+
const propertySchema: PropertySchema = {
|
|
58
|
+
type: 'number',
|
|
59
|
+
format: 'float',
|
|
60
|
+
};
|
|
61
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Number');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('when format is date', () => {
|
|
66
|
+
it('should return schema:Date for date format', () => {
|
|
67
|
+
const propertySchema: PropertySchema = {
|
|
68
|
+
type: 'string',
|
|
69
|
+
format: 'date',
|
|
70
|
+
};
|
|
71
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Date');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should return schema:Date regardless of type when format is date', () => {
|
|
75
|
+
const propertySchema: PropertySchema = {
|
|
76
|
+
type: 'object',
|
|
77
|
+
format: 'date',
|
|
78
|
+
};
|
|
79
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Date');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('when format is date-time', () => {
|
|
84
|
+
it('should return schema:DateTime for date-time format', () => {
|
|
85
|
+
const propertySchema: PropertySchema = {
|
|
86
|
+
type: 'string',
|
|
87
|
+
format: 'date-time',
|
|
88
|
+
};
|
|
89
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:DateTime');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should return schema:DateTime when format is date-time and type is string', () => {
|
|
93
|
+
const propertySchema: PropertySchema = {
|
|
94
|
+
type: 'string',
|
|
95
|
+
format: 'date-time',
|
|
96
|
+
};
|
|
97
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:DateTime');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should prioritize type over format (number type wins over date-time format)', () => {
|
|
101
|
+
const propertySchema: PropertySchema = {
|
|
102
|
+
type: 'number',
|
|
103
|
+
format: 'date-time',
|
|
104
|
+
};
|
|
105
|
+
// The function checks type before format, so number type takes priority
|
|
106
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Number');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('when no match is found', () => {
|
|
111
|
+
it('should return undefined for string type without format', () => {
|
|
112
|
+
const propertySchema: PropertySchema = {
|
|
113
|
+
type: 'string',
|
|
114
|
+
};
|
|
115
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should return undefined for boolean type', () => {
|
|
119
|
+
const propertySchema: PropertySchema = {
|
|
120
|
+
type: 'boolean',
|
|
121
|
+
};
|
|
122
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBeUndefined();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should return undefined for array type', () => {
|
|
126
|
+
const propertySchema: PropertySchema = {
|
|
127
|
+
type: 'array',
|
|
128
|
+
};
|
|
129
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBeUndefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should return undefined for object type', () => {
|
|
133
|
+
const propertySchema: PropertySchema = {
|
|
134
|
+
type: 'object',
|
|
135
|
+
};
|
|
136
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should return undefined for other formats', () => {
|
|
140
|
+
const propertySchema: PropertySchema = {
|
|
141
|
+
type: 'string',
|
|
142
|
+
format: 'email',
|
|
143
|
+
};
|
|
144
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBeUndefined();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should return undefined for empty object', () => {
|
|
148
|
+
const propertySchema: PropertySchema = {} as PropertySchema;
|
|
149
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should return undefined when only $ref is present', () => {
|
|
153
|
+
const propertySchema: PropertySchema = {
|
|
154
|
+
$ref: '#/definitions/SomeType',
|
|
155
|
+
};
|
|
156
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBeUndefined();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('priority order', () => {
|
|
161
|
+
it('should prioritize @type over type integer', () => {
|
|
162
|
+
const propertySchema: PropertySchema = {
|
|
163
|
+
'@type': 'schema:Custom',
|
|
164
|
+
type: 'integer',
|
|
165
|
+
};
|
|
166
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Custom');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should prioritize @type over format date', () => {
|
|
170
|
+
const propertySchema: PropertySchema = {
|
|
171
|
+
'@type': 'schema:Custom',
|
|
172
|
+
format: 'date',
|
|
173
|
+
};
|
|
174
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Custom');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should prioritize integer type over number type', () => {
|
|
178
|
+
const propertySchema: PropertySchema = {
|
|
179
|
+
type: 'integer',
|
|
180
|
+
format: 'number',
|
|
181
|
+
};
|
|
182
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Integer');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should prioritize integer type over date format', () => {
|
|
186
|
+
const propertySchema: PropertySchema = {
|
|
187
|
+
type: 'integer',
|
|
188
|
+
format: 'date',
|
|
189
|
+
};
|
|
190
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Integer');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should prioritize number type over date format', () => {
|
|
194
|
+
const propertySchema: PropertySchema = {
|
|
195
|
+
type: 'number',
|
|
196
|
+
format: 'date',
|
|
197
|
+
};
|
|
198
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Number');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should prioritize date format over date-time format when both are present (date checked first)', () => {
|
|
202
|
+
// NOTE: This tests the actual implementation order.
|
|
203
|
+
// The function checks date before date-time, so date would win if both were somehow set.
|
|
204
|
+
// But in practice, format can only be one value, so this is more of an edge case test.
|
|
205
|
+
const propertySchema: PropertySchema = {
|
|
206
|
+
type: 'string',
|
|
207
|
+
format: 'date',
|
|
208
|
+
};
|
|
209
|
+
expect(getLinkedDataAttributeType(propertySchema)).toBe('schema:Date');
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
});
|