@kravc/schema 2.7.6 → 2.8.0-alpha.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.
Files changed (170) hide show
  1. package/README.md +19 -14
  2. package/dist/CredentialFactory.d.ts +345 -0
  3. package/dist/CredentialFactory.d.ts.map +1 -0
  4. package/dist/CredentialFactory.js +381 -0
  5. package/dist/CredentialFactory.js.map +1 -0
  6. package/dist/Schema.d.ts +448 -0
  7. package/dist/Schema.d.ts.map +1 -0
  8. package/dist/Schema.js +506 -0
  9. package/dist/Schema.js.map +1 -0
  10. package/dist/ValidationError.d.ts +70 -0
  11. package/dist/ValidationError.d.ts.map +1 -0
  12. package/dist/ValidationError.js +78 -0
  13. package/dist/ValidationError.js.map +1 -0
  14. package/dist/Validator.d.ts +483 -0
  15. package/dist/Validator.d.ts.map +1 -0
  16. package/dist/Validator.js +570 -0
  17. package/dist/Validator.js.map +1 -0
  18. package/dist/helpers/JsonSchema.d.ts +99 -0
  19. package/dist/helpers/JsonSchema.d.ts.map +1 -0
  20. package/dist/helpers/JsonSchema.js +3 -0
  21. package/dist/helpers/JsonSchema.js.map +1 -0
  22. package/dist/helpers/cleanupAttributes.d.ts +34 -0
  23. package/dist/helpers/cleanupAttributes.d.ts.map +1 -0
  24. package/dist/helpers/cleanupAttributes.js +113 -0
  25. package/dist/helpers/cleanupAttributes.js.map +1 -0
  26. package/dist/helpers/cleanupNulls.d.ts +27 -0
  27. package/dist/helpers/cleanupNulls.d.ts.map +1 -0
  28. package/dist/helpers/cleanupNulls.js +96 -0
  29. package/dist/helpers/cleanupNulls.js.map +1 -0
  30. package/dist/helpers/getReferenceIds.d.ts +169 -0
  31. package/dist/helpers/getReferenceIds.d.ts.map +1 -0
  32. package/dist/helpers/getReferenceIds.js +241 -0
  33. package/dist/helpers/getReferenceIds.js.map +1 -0
  34. package/dist/helpers/got.d.ts +60 -0
  35. package/dist/helpers/got.d.ts.map +1 -0
  36. package/dist/helpers/got.js +72 -0
  37. package/dist/helpers/got.js.map +1 -0
  38. package/dist/helpers/mapObjectProperties.d.ts +150 -0
  39. package/dist/helpers/mapObjectProperties.d.ts.map +1 -0
  40. package/dist/helpers/mapObjectProperties.js +229 -0
  41. package/dist/helpers/mapObjectProperties.js.map +1 -0
  42. package/dist/helpers/normalizeAttributes.d.ts +213 -0
  43. package/dist/helpers/normalizeAttributes.d.ts.map +1 -0
  44. package/dist/helpers/normalizeAttributes.js +243 -0
  45. package/dist/helpers/normalizeAttributes.js.map +1 -0
  46. package/dist/helpers/normalizeProperties.d.ts +168 -0
  47. package/dist/helpers/normalizeProperties.d.ts.map +1 -0
  48. package/dist/helpers/normalizeProperties.js +223 -0
  49. package/dist/helpers/normalizeProperties.js.map +1 -0
  50. package/dist/helpers/normalizeRequired.d.ts +159 -0
  51. package/dist/helpers/normalizeRequired.d.ts.map +1 -0
  52. package/dist/helpers/normalizeRequired.js +206 -0
  53. package/dist/helpers/normalizeRequired.js.map +1 -0
  54. package/dist/helpers/normalizeType.d.ts +81 -0
  55. package/dist/helpers/normalizeType.d.ts.map +1 -0
  56. package/dist/helpers/normalizeType.js +210 -0
  57. package/dist/helpers/normalizeType.js.map +1 -0
  58. package/dist/helpers/nullifyEmptyValues.d.ts +139 -0
  59. package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -0
  60. package/dist/helpers/nullifyEmptyValues.js +191 -0
  61. package/dist/helpers/nullifyEmptyValues.js.map +1 -0
  62. package/dist/helpers/removeRequiredAndDefault.d.ts +106 -0
  63. package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -0
  64. package/dist/helpers/removeRequiredAndDefault.js +138 -0
  65. package/dist/helpers/removeRequiredAndDefault.js.map +1 -0
  66. package/dist/helpers/validateId.d.ts +39 -0
  67. package/dist/helpers/validateId.d.ts.map +1 -0
  68. package/dist/helpers/validateId.js +51 -0
  69. package/dist/helpers/validateId.js.map +1 -0
  70. package/dist/index.d.ts +7 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +17 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/ld/documentLoader.d.ts +8 -0
  75. package/dist/ld/documentLoader.d.ts.map +1 -0
  76. package/dist/ld/documentLoader.js +24 -0
  77. package/dist/ld/documentLoader.js.map +1 -0
  78. package/dist/ld/getLinkedDataAttributeType.d.ts +10 -0
  79. package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -0
  80. package/dist/ld/getLinkedDataAttributeType.js +32 -0
  81. package/dist/ld/getLinkedDataAttributeType.js.map +1 -0
  82. package/dist/ld/getLinkedDataContext.d.ts +19 -0
  83. package/dist/ld/getLinkedDataContext.d.ts.map +1 -0
  84. package/dist/ld/getLinkedDataContext.js +50 -0
  85. package/dist/ld/getLinkedDataContext.js.map +1 -0
  86. package/eslint.config.mjs +32 -52
  87. package/examples/credentials/createAccountCredential.ts +27 -0
  88. package/examples/credentials/createMineSweeperScoreCredential.ts +115 -0
  89. package/examples/index.ts +7 -0
  90. package/examples/schemas/FavoriteItemSchema.ts +27 -0
  91. package/examples/{Preferences.yaml → schemas/Preferences.yaml} +2 -0
  92. package/examples/schemas/PreferencesSchema.ts +29 -0
  93. package/examples/schemas/ProfileSchema.ts +91 -0
  94. package/examples/schemas/Status.yaml +3 -0
  95. package/examples/schemas/StatusSchema.ts +12 -0
  96. package/jest.config.mjs +5 -0
  97. package/package.json +27 -20
  98. package/src/CredentialFactory.ts +392 -0
  99. package/src/Schema.ts +583 -0
  100. package/src/ValidationError.ts +90 -0
  101. package/src/Validator.ts +603 -0
  102. package/src/__tests__/CredentialFactory.test.ts +588 -0
  103. package/src/__tests__/Schema.test.ts +371 -0
  104. package/src/__tests__/ValidationError.test.ts +235 -0
  105. package/src/__tests__/Validator.test.ts +787 -0
  106. package/src/helpers/JsonSchema.ts +119 -0
  107. package/src/helpers/__tests__/cleanupAttributes.test.ts +943 -0
  108. package/src/helpers/__tests__/cleanupNulls.test.ts +772 -0
  109. package/src/helpers/__tests__/getReferenceIds.test.ts +975 -0
  110. package/src/helpers/__tests__/got.test.ts +193 -0
  111. package/src/helpers/__tests__/mapObjectProperties.test.ts +1126 -0
  112. package/src/helpers/__tests__/normalizeAttributes.test.ts +1435 -0
  113. package/src/helpers/__tests__/normalizeProperties.test.ts +727 -0
  114. package/src/helpers/__tests__/normalizeRequired.test.ts +669 -0
  115. package/src/helpers/__tests__/normalizeType.test.ts +772 -0
  116. package/src/helpers/__tests__/nullifyEmptyValues.test.ts +735 -0
  117. package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +734 -0
  118. package/src/helpers/__tests__/validateId.test.ts +118 -0
  119. package/src/helpers/cleanupAttributes.ts +151 -0
  120. package/src/helpers/cleanupNulls.ts +106 -0
  121. package/src/helpers/getReferenceIds.ts +273 -0
  122. package/src/helpers/got.ts +73 -0
  123. package/src/helpers/mapObjectProperties.ts +272 -0
  124. package/src/helpers/normalizeAttributes.ts +247 -0
  125. package/src/helpers/normalizeProperties.ts +249 -0
  126. package/src/helpers/normalizeRequired.ts +233 -0
  127. package/src/helpers/normalizeType.ts +235 -0
  128. package/src/helpers/nullifyEmptyValues.ts +207 -0
  129. package/src/helpers/removeRequiredAndDefault.ts +151 -0
  130. package/src/helpers/validateId.ts +53 -0
  131. package/src/index.ts +13 -0
  132. package/src/ld/__tests__/documentLoader.test.ts +57 -0
  133. package/src/ld/__tests__/getLinkedDataAttributeType.test.ts +212 -0
  134. package/src/ld/__tests__/getLinkedDataContext.test.ts +378 -0
  135. package/src/ld/documentLoader.ts +28 -0
  136. package/src/ld/getLinkedDataAttributeType.ts +46 -0
  137. package/src/ld/getLinkedDataContext.ts +80 -0
  138. package/tsconfig.json +27 -0
  139. package/types/credentials-context.d.ts +14 -0
  140. package/types/security-context.d.ts +6 -0
  141. package/examples/Status.yaml +0 -3
  142. package/examples/createAccountCredential.js +0 -27
  143. package/examples/createMineSweeperScoreCredential.js +0 -63
  144. package/examples/index.js +0 -9
  145. package/src/CredentialFactory.js +0 -67
  146. package/src/CredentialFactory.spec.js +0 -131
  147. package/src/Schema.js +0 -104
  148. package/src/Schema.spec.js +0 -172
  149. package/src/ValidationError.js +0 -31
  150. package/src/Validator.js +0 -128
  151. package/src/Validator.spec.js +0 -355
  152. package/src/helpers/cleanupAttributes.js +0 -71
  153. package/src/helpers/cleanupNulls.js +0 -42
  154. package/src/helpers/getReferenceIds.js +0 -71
  155. package/src/helpers/mapObject.js +0 -65
  156. package/src/helpers/normalizeAttributes.js +0 -28
  157. package/src/helpers/normalizeProperties.js +0 -61
  158. package/src/helpers/normalizeRequired.js +0 -37
  159. package/src/helpers/normalizeType.js +0 -41
  160. package/src/helpers/nullifyEmptyValues.js +0 -57
  161. package/src/helpers/removeRequiredAndDefault.js +0 -30
  162. package/src/helpers/validateId.js +0 -19
  163. package/src/index.d.ts +0 -25
  164. package/src/index.js +0 -8
  165. package/src/ld/documentLoader.js +0 -25
  166. package/src/ld/documentLoader.spec.js +0 -12
  167. package/src/ld/getLinkedDataContext.js +0 -63
  168. package/src/ld/getLinkedDataType.js +0 -38
  169. /package/examples/{FavoriteItem.yaml → schemas/FavoriteItem.yaml} +0 -0
  170. /package/examples/{Profile.yaml → schemas/Profile.yaml} +0 -0
@@ -0,0 +1,12 @@
1
+ import { Schema } from '../../src';
2
+
3
+ export enum Status {
4
+ ACTIVE = 'ACTIVE',
5
+ PENDING = 'PENDING'
6
+ }
7
+
8
+ const StatusSchema = new Schema({
9
+ enum: Object.values(Status),
10
+ }, 'Status');
11
+
12
+ export default StatusSchema;
@@ -0,0 +1,5 @@
1
+ export default {
2
+ preset: 'ts-jest',
3
+ testMatch: ['<rootDir>/src/**/*.test.ts'],
4
+ collectCoverageFrom: ['<rootDir>/src/**/*.ts'],
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kravc/schema",
3
- "version": "2.7.6",
3
+ "version": "2.8.0-alpha.0",
4
4
  "description": "Advanced JSON schema manipulation and validation library.",
5
5
  "keywords": [
6
6
  "JSON",
@@ -12,40 +12,47 @@
12
12
  "Semantic Web",
13
13
  "Verifiable Credential"
14
14
  ],
15
- "main": "src/index.js",
16
- "types": "src/index.d.ts",
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "http://github.com/alexkravets/schema.git"
19
+ "url": "git+ssh://git@github.com/alexkravets/schema.git"
20
20
  },
21
21
  "directories": {
22
22
  "src": "src"
23
23
  },
24
24
  "scripts": {
25
- "test": "eslint src/ examples/ && NODE_PATH=./ nyc mocha \"./src/**/*.spec.js\""
25
+ "test": "eslint --fix src/ examples/ && jest --coverage",
26
+ "prebuild": "rimraf dist",
27
+ "build": "tsc",
28
+ "prepare": "npm run build",
29
+ "prepublishOnly": "npm run build"
26
30
  },
27
31
  "author": "Alexander Kravets <a@kra.vc>",
28
32
  "license": "ISC",
29
33
  "dependencies": {
30
34
  "credentials-context": "^2.0.0",
31
- "lodash": "^4.17.21",
35
+ "lodash": "^4.17.23",
32
36
  "security-context": "^4.0.0",
33
- "validator": "^13.15.15",
37
+ "validator": "^13.15.26",
34
38
  "z-schema": "^6.0.2"
35
39
  },
36
40
  "devDependencies": {
37
- "@eslint/eslintrc": "^3.3.1",
38
- "@eslint/js": "^9.38.0",
39
- "chai": "^6.2.0",
40
- "eslint": "^9.38.0",
41
- "globals": "^16.4.0",
42
- "js-yaml": "^4.1.0",
43
- "jsonld": "^8.3.3",
44
- "mocha": "^11.7.4",
45
- "nyc": "^17.1.0"
46
- },
47
- "nyc": {
48
- "include": "src",
49
- "exclude": "**/*.spec.js"
41
+ "@eslint/js": "^9.39.2",
42
+ "@types/jest": "^30.0.0",
43
+ "@types/js-yaml": "^4.0.9",
44
+ "@types/jsonld": "^1.5.15",
45
+ "@types/lodash": "^4.17.23",
46
+ "@types/validator": "^13.15.10",
47
+ "eslint": "^9.39.2",
48
+ "eslint-plugin-jsdoc": "^62.4.1",
49
+ "globals": "^17.1.0",
50
+ "jest": "^30.2.0",
51
+ "js-yaml": "^4.1.1",
52
+ "jsonld": "^9.0.0",
53
+ "rimraf": "^6.1.2",
54
+ "ts-jest": "^29.4.6",
55
+ "typescript": "^5.9.3",
56
+ "typescript-eslint": "^8.54.0"
50
57
  }
51
58
  }
@@ -0,0 +1,392 @@
1
+ import { constants } from 'credentials-context';
2
+
3
+ import Validator from './Validator';
4
+ import validateId from './helpers/validateId';
5
+ import type { TargetObject } from './helpers/JsonSchema';
6
+ import Schema, { type LinkedDataType } from './Schema';
7
+
8
+ const { CREDENTIALS_CONTEXT_V1_URL } = constants;
9
+
10
+ /**
11
+ * Factory class for creating Verifiable Credentials with embedded JSON-LD linked data context.
12
+ *
13
+ * **Intent:** Provide a standardized way to generate W3C Verifiable Credentials that are
14
+ * compatible with JSON-LD and semantic web standards. The factory automatically validates
15
+ * credential subjects against provided schemas, generates appropriate JSON-LD contexts, and
16
+ * structures credentials according to the Verifiable Credentials data model.
17
+ *
18
+ * **Use Cases:**
19
+ * - Generate Verifiable Credentials for decentralized identity systems
20
+ * - Create credentials with automatic schema validation and type checking
21
+ * - Build credentials with embedded semantic context (schema.org, custom vocabularies)
22
+ * - Support multi-schema credentials with nested object references
23
+ * - Enable credential interoperability through standardized JSON-LD contexts
24
+ * - Integrate with credential issuance systems (e.g., DID-based identity providers)
25
+ *
26
+ * **Key Features:**
27
+ * - Automatic validation of credential subjects against schema definitions
28
+ * - JSON-LD context generation for semantic web compatibility
29
+ * - Support for multiple schemas with cross-references (`$ref`)
30
+ * - Type-safe credential generation with TypeScript support
31
+ * - Schema.org type mapping for common data formats (date-time, date, etc.)
32
+ *
33
+ * **Example - Simple Credential:**
34
+ * ```typescript
35
+ * import { Schema, CredentialFactory } from '@kravc/schema';
36
+ *
37
+ * // Define credential subject schema
38
+ * const accountSchema = new Schema({
39
+ * id: {},
40
+ * username: { required: true },
41
+ * createdAt: { format: 'date-time', required: true },
42
+ * dateOfBirth: { format: 'date' }
43
+ * }, 'Account');
44
+ *
45
+ * // Create factory with credential URI and schemas
46
+ * const factory = new CredentialFactory(
47
+ * 'https://example.com/schema/AccountV1',
48
+ * [accountSchema]
49
+ * );
50
+ *
51
+ * // Generate credential
52
+ * const credential = factory.createCredential(
53
+ * 'https://example.com/credentials/123',
54
+ * 'did:holder:123',
55
+ * {
56
+ * id: 'did:holder:123',
57
+ * username: 'alice',
58
+ * createdAt: new Date().toISOString()
59
+ * }
60
+ * );
61
+ * ```
62
+ *
63
+ * **Example - Multi-Schema Credential with References:**
64
+ * ```typescript
65
+ * import { Schema, CredentialFactory } from '@kravc/schema';
66
+ *
67
+ * // Define multiple related schemas
68
+ * const playerSchema = new Schema({
69
+ * id: {},
70
+ * hasVideoGameScore: { $ref: 'VideoGameScore', required: true }
71
+ * }, 'Player');
72
+ *
73
+ * const videoGameSchema = new Schema({
74
+ * id: {},
75
+ * name: { type: 'string', required: true },
76
+ * version: { type: 'string', required: true }
77
+ * }, 'VideoGame', 'https://schema.org/');
78
+ *
79
+ * const scoreSchema = new Schema({
80
+ * game: { $ref: 'VideoGame', required: true },
81
+ * wins: { type: 'integer', required: true },
82
+ * bestScore: { type: 'integer', required: true }
83
+ * }, 'VideoGameScore');
84
+ *
85
+ * // Create factory with multiple schemas
86
+ * const factory = new CredentialFactory(
87
+ * 'https://example.com/schema/GameScoreV1',
88
+ * [playerSchema, videoGameSchema, scoreSchema]
89
+ * );
90
+ *
91
+ * // Generate credential with nested data
92
+ * const credential = factory.createCredential(
93
+ * 'https://example.com/credentials/score-456',
94
+ * 'did:player:789',
95
+ * {
96
+ * id: 'did:player:789',
97
+ * hasVideoGameScore: {
98
+ * game: { id: 'did:game:001', name: 'MineSweeper', version: '1.0' },
99
+ * wins: 10,
100
+ * bestScore: 5000
101
+ * }
102
+ * }
103
+ * );
104
+ * ```
105
+ *
106
+ * **Example - Accessing Credential Context:**
107
+ * ```typescript
108
+ * const factory = new CredentialFactory(uri, schemas);
109
+ *
110
+ * // Get credential type (extracted from URI)
111
+ * const type = factory.credentialType; // e.g., 'AccountV1'
112
+ *
113
+ * // Get JSON-LD context for embedding
114
+ * const context = factory.context;
115
+ * // {
116
+ * // AccountV1: { '@id': 'https://example.com/schema/AccountV1' },
117
+ * // Account: { '@id': '...', '@context': {...} }
118
+ * // }
119
+ * ```
120
+ */
121
+ class CredentialFactory {
122
+ private _uri: string;
123
+ private _types: Schema[];
124
+ private _context: Record<string, LinkedDataType>;
125
+ private _validator: Validator;
126
+
127
+ /**
128
+ * Creates a new CredentialFactory instance.
129
+ *
130
+ * **Intent:** Initialize a factory with credential schemas and URI, enabling credential
131
+ * generation with automatic validation and JSON-LD context generation.
132
+ *
133
+ * **Use Cases:**
134
+ * - Set up credential factories for different credential types
135
+ * - Configure factories with single or multiple related schemas
136
+ * - Prepare factories for batch credential generation
137
+ *
138
+ * **Behavior:**
139
+ * - Validates that the provided URI is a valid URL
140
+ * - Processes schemas: if a schema doesn't have a URL, creates a new Schema instance
141
+ * with the factory URI as the base URL
142
+ * - Validates all schemas using the Validator
143
+ * - Builds JSON-LD context from schema linked data types
144
+ *
145
+ * @param uri - The base URI for the credential type (e.g., 'https://example.com/schema/AccountV1')
146
+ * Must be a valid URL. The last segment will be used as the credential type name.
147
+ * @param schemas - Array of Schema instances defining the credential subject structure.
148
+ * The first schema is considered the root schema for validation.
149
+ * Schemas can reference each other using `$ref`.
150
+ *
151
+ * @throws Error if the URI is not a valid URL
152
+ * @throws Error if schema validation fails (e.g., invalid JSON Schema structure)
153
+ *
154
+ * **Example:**
155
+ * ```typescript
156
+ * const accountSchema = new Schema({
157
+ * username: { required: true },
158
+ * email: { format: 'email', required: true }
159
+ * }, 'Account');
160
+ *
161
+ * const factory = new CredentialFactory(
162
+ * 'https://example.com/schema/AccountV1',
163
+ * [accountSchema]
164
+ * );
165
+ * ```
166
+ */
167
+ constructor(uri: string, schemas: Schema[]) {
168
+ validateId('uri', uri);
169
+
170
+ this._types = schemas
171
+ .map(schema => {
172
+ if (schema.url) {
173
+ return schema;
174
+ }
175
+
176
+ return new Schema(schema, schema.id, uri);
177
+ });
178
+
179
+ this._uri = uri;
180
+ this._context = {};
181
+ this._validator = new Validator(this._types);
182
+
183
+ for (const { id, linkedDataType } of this._types) {
184
+ this._context[id] = linkedDataType!;
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Returns the credential type name extracted from the factory URI.
190
+ *
191
+ * **Intent:** Provide a convenient way to access the credential type identifier
192
+ * without manually parsing the URI.
193
+ *
194
+ * **Use Cases:**
195
+ * - Access credential type for logging or debugging
196
+ * - Use type name in credential processing logic
197
+ * - Generate type-specific identifiers or filenames
198
+ *
199
+ * **Behavior:**
200
+ * - Extracts the last segment from the URI path
201
+ * - Example: 'https://example.com/schema/AccountV1' → 'AccountV1'
202
+ *
203
+ * @returns The credential type name (last segment of the URI path)
204
+ *
205
+ * **Example:**
206
+ * ```typescript
207
+ * const factory = new CredentialFactory(
208
+ * 'https://example.com/schema/AccountV1',
209
+ * [schema]
210
+ * );
211
+ *
212
+ * console.log(factory.credentialType); // 'AccountV1'
213
+ * ```
214
+ */
215
+ get credentialType() {
216
+ const [ credentialType ] = this._uri.split('/').reverse();
217
+
218
+ return credentialType;
219
+ }
220
+
221
+ /**
222
+ * Returns the JSON-LD context object for the credential.
223
+ *
224
+ * **Intent:** Provide the complete JSON-LD context mapping that should be included
225
+ * in the credential's `@context` array for semantic web compatibility.
226
+ *
227
+ * **Use Cases:**
228
+ * - Access context for credential serialization
229
+ * - Inspect context mappings for debugging
230
+ * - Use context in custom credential processing
231
+ * - Embed context in other JSON-LD documents
232
+ *
233
+ * **Structure:**
234
+ * - Contains the credential type mapping (`{ credentialType: { '@id': uri } }`)
235
+ * - Includes all schema-linked data types from the factory's schemas
236
+ * - Each schema's `linkedDataType` is included if the schema was created with a URL
237
+ *
238
+ * @returns Object containing credential type and schema type mappings for JSON-LD context
239
+ *
240
+ * **Example:**
241
+ * ```typescript
242
+ * const factory = new CredentialFactory(uri, schemas);
243
+ * const context = factory.context;
244
+ *
245
+ * // Result structure:
246
+ * // {
247
+ * // AccountV1: { '@id': 'https://example.com/schema/AccountV1' },
248
+ * // Account: {
249
+ * // '@id': 'https://example.com/schema/AccountV1#Account',
250
+ * // '@context': { ... }
251
+ * // }
252
+ * // }
253
+ * ```
254
+ */
255
+ get context() {
256
+ return {
257
+ [this.credentialType]: { '@id': this._uri },
258
+ ...this._context
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Creates a Verifiable Credential for the specified subject.
264
+ *
265
+ * **Intent:** Generate a W3C-compliant Verifiable Credential with validated subject data,
266
+ * proper JSON-LD context, and standardized structure ready for issuance and verification.
267
+ *
268
+ * **Use Cases:**
269
+ * - Issue credentials for user accounts, achievements, certifications
270
+ * - Generate credentials with automatic schema validation
271
+ * - Create credentials with embedded semantic context for interoperability
272
+ * - Build credentials for decentralized identity systems
273
+ * - Generate credentials that can be verified using JSON-LD processors
274
+ *
275
+ * **Behavior:**
276
+ * - Validates that `id` and `holder` are valid URLs/URIs
277
+ * - Validates the subject against the root schema (first schema in the factory)
278
+ * - Applies schema defaults and normalizes data according to schema rules
279
+ * - Generates credential with proper `@context`, `type`, `id`, `holder`, and `credentialSubject`
280
+ * - Returns credential ready for signing (issuer, issuanceDate, proof to be added separately)
281
+ *
282
+ * **Credential Structure:**
283
+ * ```typescript
284
+ * {
285
+ * '@context': [
286
+ * 'https://www.w3.org/2018/credentials/v1',
287
+ * factory.context // JSON-LD context from factory
288
+ * ],
289
+ * id: string, // Credential identifier
290
+ * type: ['VerifiableCredential', credentialType],
291
+ * holder: string, // DID or identifier of credential holder
292
+ * credentialSubject: { // Validated and normalized subject data
293
+ * id: string,
294
+ * type: string, // Root schema ID
295
+ * // ... additional subject properties
296
+ * }
297
+ * }
298
+ * ```
299
+ *
300
+ * @param id - Unique identifier for the credential (must be a valid URL/URI)
301
+ * @param holder - DID or identifier of the credential holder (must be a valid URL/URI)
302
+ * @param subject - Object containing the credential subject data to be validated against
303
+ * the root schema. Properties are validated, defaults are applied,
304
+ * and nested objects are validated against referenced schemas.
305
+ *
306
+ * @returns Verifiable Credential object with validated subject and JSON-LD context
307
+ *
308
+ * @throws Error if `id` is not a valid URL/URI
309
+ * @throws Error if `holder` is not a valid URL/URI
310
+ * @throws ValidationError if subject data doesn't match the schema requirements
311
+ *
312
+ * **Example - Basic Credential:**
313
+ * ```typescript
314
+ * const factory = new CredentialFactory(uri, [accountSchema]);
315
+ *
316
+ * const credential = factory.createCredential(
317
+ * 'https://example.com/credentials/123',
318
+ * 'did:holder:456',
319
+ * {
320
+ * id: 'did:holder:456',
321
+ * username: 'alice',
322
+ * createdAt: '2024-01-01T00:00:00Z'
323
+ * }
324
+ * );
325
+ *
326
+ * // Result:
327
+ * // {
328
+ * // '@context': ['https://www.w3.org/2018/credentials/v1', {...}],
329
+ * // id: 'https://example.com/credentials/123',
330
+ * // type: ['VerifiableCredential', 'AccountV1'],
331
+ * // holder: 'did:holder:456',
332
+ * // credentialSubject: {
333
+ * // id: 'did:holder:456',
334
+ * // username: 'alice',
335
+ * // createdAt: '2024-01-01T00:00:00Z',
336
+ * // type: 'Account'
337
+ * // }
338
+ * // }
339
+ * ```
340
+ *
341
+ * **Example - Credential with Nested Objects:**
342
+ * ```typescript
343
+ * const factory = new CredentialFactory(uri, [playerSchema, gameSchema, scoreSchema]);
344
+ *
345
+ * const credential = factory.createCredential(
346
+ * 'https://example.com/credentials/score-789',
347
+ * 'did:player:123',
348
+ * {
349
+ * id: 'did:player:123',
350
+ * hasVideoGameScore: {
351
+ * game: {
352
+ * id: 'did:game:001',
353
+ * name: 'MineSweeper',
354
+ * version: '1.0'
355
+ * },
356
+ * wins: 10,
357
+ * bestScore: 5000
358
+ * }
359
+ * }
360
+ * );
361
+ * ```
362
+ *
363
+ * **Note:** The returned credential does not include `issuer`, `issuanceDate`, or `proof`
364
+ * fields. These should be added by the credential issuance system (e.g., using
365
+ * [@kravc/identity](http://github.com/alexkravets/identity) or similar libraries).
366
+ */
367
+ createCredential(id: string, holder: string, subject: TargetObject = {}) {
368
+ validateId('id', id);
369
+ validateId('holder', holder);
370
+
371
+ const type = [
372
+ 'VerifiableCredential',
373
+ this.credentialType
374
+ ];
375
+
376
+ const [ rootType ] = this._types;
377
+ const credentialSubject = this._validator.validate(subject, rootType.id);
378
+
379
+ return {
380
+ '@context': [
381
+ CREDENTIALS_CONTEXT_V1_URL,
382
+ this.context
383
+ ],
384
+ id,
385
+ type,
386
+ holder,
387
+ credentialSubject
388
+ };
389
+ }
390
+ }
391
+
392
+ export default CredentialFactory;