id.org.ai 0.1.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/.turbo/turbo-build.log +4 -0
- package/dist/index.d.ts +730 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +555 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -0
- package/src/index.ts +774 -0
- package/test/agent-identity.test.ts +356 -0
- package/test/credential.test.ts +337 -0
- package/test/identity.test.ts +202 -0
- package/test/session.test.ts +365 -0
- package/test/user.test.ts +307 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +8 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* @module id.org.ai
|
|
4
|
+
*
|
|
5
|
+
* Identity primitives for schema.org.ai
|
|
6
|
+
*
|
|
7
|
+
* This package provides identity types for humans and AI agents:
|
|
8
|
+
* - {@link Identity} - Base identity interface
|
|
9
|
+
* - {@link User} - Human user identity
|
|
10
|
+
* - {@link AgentIdentity} - AI agent identity
|
|
11
|
+
* - {@link Credential} - Authentication credentials
|
|
12
|
+
* - {@link Session} - Active sessions
|
|
13
|
+
*
|
|
14
|
+
* All types follow JSON-LD conventions with `$id` and `$type` fields.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { createUser, isUser, UserSchema } from 'id.org.ai'
|
|
19
|
+
*
|
|
20
|
+
* const user = createUser({
|
|
21
|
+
* email: 'alice@example.com',
|
|
22
|
+
* name: 'Alice'
|
|
23
|
+
* })
|
|
24
|
+
*
|
|
25
|
+
* // Runtime validation
|
|
26
|
+
* if (isUser(unknownData)) {
|
|
27
|
+
* console.log(unknownData.email)
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // Schema validation
|
|
31
|
+
* const result = UserSchema.safeParse(data)
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @version 0.1.0
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { z } from 'zod'
|
|
38
|
+
|
|
39
|
+
// === Internal Helpers ===
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generates a unique identifier URI for a given resource type
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
function generateId(prefix: string): string {
|
|
46
|
+
const uuid = crypto.randomUUID()
|
|
47
|
+
return `https://schema.org.ai/${prefix}/${uuid}`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Generates a secure random token for session authentication
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
function generateToken(): string {
|
|
55
|
+
return crypto.randomUUID().replace(/-/g, '') + crypto.randomUUID().replace(/-/g, '')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// === Identity ===
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Identity - Base interface for all identity types
|
|
62
|
+
*
|
|
63
|
+
* Represents a unique identity in the schema.org.ai system. All identity
|
|
64
|
+
* types ({@link User}, {@link AgentIdentity}) extend this base interface.
|
|
65
|
+
*
|
|
66
|
+
* @see https://schema.org.ai/Identity
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const identity: Identity = {
|
|
71
|
+
* $id: 'https://schema.org.ai/identities/123',
|
|
72
|
+
* $type: 'https://schema.org.ai/Identity',
|
|
73
|
+
* createdAt: '2024-01-01T00:00:00Z',
|
|
74
|
+
* updatedAt: '2024-01-01T00:00:00Z'
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export interface Identity {
|
|
79
|
+
/** Unique identifier URI (JSON-LD @id) */
|
|
80
|
+
$id: string
|
|
81
|
+
/** Type discriminator (JSON-LD @type) */
|
|
82
|
+
$type: 'https://schema.org.ai/Identity'
|
|
83
|
+
/** ISO 8601 timestamp when identity was created */
|
|
84
|
+
createdAt: string
|
|
85
|
+
/** ISO 8601 timestamp when identity was last updated */
|
|
86
|
+
updatedAt: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Zod schema for validating Identity objects
|
|
91
|
+
*
|
|
92
|
+
* Use this schema for runtime validation of identity data.
|
|
93
|
+
*
|
|
94
|
+
* @see {@link Identity}
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const result = IdentitySchema.safeParse(unknownData)
|
|
99
|
+
* if (result.success) {
|
|
100
|
+
* console.log(result.data.$id)
|
|
101
|
+
* } else {
|
|
102
|
+
* console.error(result.error)
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export const IdentitySchema = z.object({
|
|
107
|
+
$id: z.string(),
|
|
108
|
+
$type: z.literal('https://schema.org.ai/Identity'),
|
|
109
|
+
createdAt: z.string(),
|
|
110
|
+
updatedAt: z.string(),
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Type guard to check if an object is a valid Identity
|
|
115
|
+
*
|
|
116
|
+
* Uses Zod schema validation internally to ensure type safety.
|
|
117
|
+
*
|
|
118
|
+
* @param obj - Object to validate
|
|
119
|
+
* @returns True if the object is a valid Identity
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const data: unknown = fetchFromAPI()
|
|
124
|
+
* if (isIdentity(data)) {
|
|
125
|
+
* // TypeScript knows data is Identity
|
|
126
|
+
* console.log(data.createdAt)
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export function isIdentity(obj: unknown): obj is Identity {
|
|
131
|
+
return IdentitySchema.safeParse(obj).success
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Factory function to create a new Identity
|
|
136
|
+
*
|
|
137
|
+
* Creates a complete Identity object with auto-generated `$id` (if not provided)
|
|
138
|
+
* and timestamps. The `$type` is always set to the correct JSON-LD type.
|
|
139
|
+
*
|
|
140
|
+
* @param input - Optional partial identity data
|
|
141
|
+
* @param input.$id - Custom identifier URI (auto-generated if not provided)
|
|
142
|
+
* @returns A complete Identity object with all required fields
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // Auto-generate all fields
|
|
147
|
+
* const identity = createIdentity()
|
|
148
|
+
*
|
|
149
|
+
* // With custom $id
|
|
150
|
+
* const identity = createIdentity({ $id: 'https://example.com/id/custom' })
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export function createIdentity(input?: { $id?: string }): Identity {
|
|
154
|
+
const now = new Date().toISOString()
|
|
155
|
+
return {
|
|
156
|
+
$id: input?.$id ?? generateId('identities'),
|
|
157
|
+
$type: 'https://schema.org.ai/Identity',
|
|
158
|
+
createdAt: now,
|
|
159
|
+
updatedAt: now,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// === User ===
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* User - Human user identity
|
|
167
|
+
*
|
|
168
|
+
* Extends the base {@link Identity} interface with human-specific fields
|
|
169
|
+
* such as email, name, and an optional profile for additional metadata.
|
|
170
|
+
*
|
|
171
|
+
* @see https://schema.org.ai/User
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const user: User = {
|
|
176
|
+
* $id: 'https://schema.org.ai/users/123',
|
|
177
|
+
* $type: 'https://schema.org.ai/User',
|
|
178
|
+
* email: 'alice@example.com',
|
|
179
|
+
* name: 'Alice Smith',
|
|
180
|
+
* profile: { avatar: 'https://example.com/avatar.png' },
|
|
181
|
+
* createdAt: '2024-01-01T00:00:00Z',
|
|
182
|
+
* updatedAt: '2024-01-01T00:00:00Z'
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export interface User extends Omit<Identity, '$type'> {
|
|
187
|
+
/** Type discriminator for User (JSON-LD @type) */
|
|
188
|
+
$type: 'https://schema.org.ai/User'
|
|
189
|
+
/** User's email address (must be valid email format) */
|
|
190
|
+
email: string
|
|
191
|
+
/** User's display name */
|
|
192
|
+
name: string
|
|
193
|
+
/** Optional profile data for additional user information */
|
|
194
|
+
profile?: Record<string, unknown>
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Zod schema for validating User objects
|
|
199
|
+
*
|
|
200
|
+
* Validates all User fields including email format validation.
|
|
201
|
+
*
|
|
202
|
+
* @see {@link User}
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* const result = UserSchema.safeParse({
|
|
207
|
+
* $id: 'https://example.com/users/1',
|
|
208
|
+
* $type: 'https://schema.org.ai/User',
|
|
209
|
+
* email: 'alice@example.com',
|
|
210
|
+
* name: 'Alice',
|
|
211
|
+
* createdAt: new Date().toISOString(),
|
|
212
|
+
* updatedAt: new Date().toISOString()
|
|
213
|
+
* })
|
|
214
|
+
*
|
|
215
|
+
* if (!result.success) {
|
|
216
|
+
* console.error('Invalid email:', result.error.issues)
|
|
217
|
+
* }
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
export const UserSchema = z.object({
|
|
221
|
+
$id: z.string(),
|
|
222
|
+
$type: z.literal('https://schema.org.ai/User'),
|
|
223
|
+
email: z.string().email(),
|
|
224
|
+
name: z.string(),
|
|
225
|
+
profile: z.record(z.unknown()).optional(),
|
|
226
|
+
createdAt: z.string(),
|
|
227
|
+
updatedAt: z.string(),
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Type guard to check if an object is a valid User
|
|
232
|
+
*
|
|
233
|
+
* Uses Zod schema validation internally including email format validation.
|
|
234
|
+
*
|
|
235
|
+
* @param obj - Object to validate
|
|
236
|
+
* @returns True if the object is a valid User
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```typescript
|
|
240
|
+
* const data: unknown = await fetchUser(id)
|
|
241
|
+
* if (isUser(data)) {
|
|
242
|
+
* // TypeScript knows data is User
|
|
243
|
+
* sendEmail(data.email, `Hello ${data.name}!`)
|
|
244
|
+
* }
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export function isUser(obj: unknown): obj is User {
|
|
248
|
+
return UserSchema.safeParse(obj).success
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Factory function to create a new User
|
|
253
|
+
*
|
|
254
|
+
* Creates a complete User object with auto-generated `$id` (if not provided),
|
|
255
|
+
* timestamps, and the correct `$type`. Email and name are required.
|
|
256
|
+
*
|
|
257
|
+
* @param input - User data (email and name required)
|
|
258
|
+
* @param input.email - User's email address
|
|
259
|
+
* @param input.name - User's display name
|
|
260
|
+
* @param input.$id - Custom identifier URI (auto-generated if not provided)
|
|
261
|
+
* @param input.profile - Optional profile metadata
|
|
262
|
+
* @returns A complete User object ready for storage
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* // Basic user creation
|
|
267
|
+
* const user = createUser({
|
|
268
|
+
* email: 'alice@example.com',
|
|
269
|
+
* name: 'Alice Smith'
|
|
270
|
+
* })
|
|
271
|
+
*
|
|
272
|
+
* // With profile data
|
|
273
|
+
* const user = createUser({
|
|
274
|
+
* email: 'bob@example.com',
|
|
275
|
+
* name: 'Bob Jones',
|
|
276
|
+
* profile: {
|
|
277
|
+
* avatar: 'https://example.com/bob.png',
|
|
278
|
+
* bio: 'Software developer',
|
|
279
|
+
* settings: { theme: 'dark' }
|
|
280
|
+
* }
|
|
281
|
+
* })
|
|
282
|
+
*
|
|
283
|
+
* // With custom $id
|
|
284
|
+
* const user = createUser({
|
|
285
|
+
* $id: 'https://myapp.com/users/alice',
|
|
286
|
+
* email: 'alice@example.com',
|
|
287
|
+
* name: 'Alice'
|
|
288
|
+
* })
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
export function createUser(
|
|
292
|
+
input: Omit<User, '$type' | 'createdAt' | 'updatedAt' | '$id'> & { $id?: string }
|
|
293
|
+
): User {
|
|
294
|
+
const now = new Date().toISOString()
|
|
295
|
+
return {
|
|
296
|
+
$id: input.$id ?? generateId('users'),
|
|
297
|
+
$type: 'https://schema.org.ai/User',
|
|
298
|
+
email: input.email,
|
|
299
|
+
name: input.name,
|
|
300
|
+
profile: input.profile,
|
|
301
|
+
createdAt: now,
|
|
302
|
+
updatedAt: now,
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// === AgentIdentity ===
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* AgentIdentity - AI agent identity
|
|
310
|
+
*
|
|
311
|
+
* Extends the base {@link Identity} interface with AI agent-specific fields
|
|
312
|
+
* such as model name, capabilities, and autonomy level.
|
|
313
|
+
*
|
|
314
|
+
* @see https://schema.org.ai/AgentIdentity
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```typescript
|
|
318
|
+
* const agent: AgentIdentity = {
|
|
319
|
+
* $id: 'https://schema.org.ai/agents/assistant-1',
|
|
320
|
+
* $type: 'https://schema.org.ai/AgentIdentity',
|
|
321
|
+
* model: 'claude-3-opus',
|
|
322
|
+
* capabilities: ['text-generation', 'code-analysis', 'tool-use'],
|
|
323
|
+
* autonomous: false,
|
|
324
|
+
* createdAt: '2024-01-01T00:00:00Z',
|
|
325
|
+
* updatedAt: '2024-01-01T00:00:00Z'
|
|
326
|
+
* }
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
export interface AgentIdentity extends Omit<Identity, '$type'> {
|
|
330
|
+
/** Type discriminator for AgentIdentity (JSON-LD @type) */
|
|
331
|
+
$type: 'https://schema.org.ai/AgentIdentity'
|
|
332
|
+
/** The AI model powering this agent (e.g., 'claude-3-opus', 'gpt-4') */
|
|
333
|
+
model: string
|
|
334
|
+
/** List of capabilities this agent supports */
|
|
335
|
+
capabilities: string[]
|
|
336
|
+
/** Whether the agent can act autonomously without human approval */
|
|
337
|
+
autonomous: boolean
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Zod schema for validating AgentIdentity objects
|
|
342
|
+
*
|
|
343
|
+
* Validates all AgentIdentity fields including model, capabilities array,
|
|
344
|
+
* and autonomous flag.
|
|
345
|
+
*
|
|
346
|
+
* @see {@link AgentIdentity}
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```typescript
|
|
350
|
+
* const result = AgentIdentitySchema.safeParse(agentData)
|
|
351
|
+
* if (result.success) {
|
|
352
|
+
* const agent = result.data
|
|
353
|
+
* if (agent.autonomous) {
|
|
354
|
+
* console.log('Agent can act independently')
|
|
355
|
+
* }
|
|
356
|
+
* }
|
|
357
|
+
* ```
|
|
358
|
+
*/
|
|
359
|
+
export const AgentIdentitySchema = z.object({
|
|
360
|
+
$id: z.string(),
|
|
361
|
+
$type: z.literal('https://schema.org.ai/AgentIdentity'),
|
|
362
|
+
model: z.string(),
|
|
363
|
+
capabilities: z.array(z.string()),
|
|
364
|
+
autonomous: z.boolean(),
|
|
365
|
+
createdAt: z.string(),
|
|
366
|
+
updatedAt: z.string(),
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Type guard to check if an object is a valid AgentIdentity
|
|
371
|
+
*
|
|
372
|
+
* Uses Zod schema validation internally to ensure type safety.
|
|
373
|
+
*
|
|
374
|
+
* @param obj - Object to validate
|
|
375
|
+
* @returns True if the object is a valid AgentIdentity
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* ```typescript
|
|
379
|
+
* const identity: unknown = getIdentity(id)
|
|
380
|
+
* if (isAgentIdentity(identity)) {
|
|
381
|
+
* // TypeScript knows identity is AgentIdentity
|
|
382
|
+
* console.log(`Agent model: ${identity.model}`)
|
|
383
|
+
* console.log(`Capabilities: ${identity.capabilities.join(', ')}`)
|
|
384
|
+
* }
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
export function isAgentIdentity(obj: unknown): obj is AgentIdentity {
|
|
388
|
+
return AgentIdentitySchema.safeParse(obj).success
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Factory function to create a new AgentIdentity
|
|
393
|
+
*
|
|
394
|
+
* Creates a complete AgentIdentity object with auto-generated `$id` (if not provided),
|
|
395
|
+
* timestamps, and the correct `$type`. Model, capabilities, and autonomous flag are required.
|
|
396
|
+
*
|
|
397
|
+
* @param input - Agent identity data
|
|
398
|
+
* @param input.model - The AI model name (e.g., 'claude-3-opus')
|
|
399
|
+
* @param input.capabilities - Array of capability strings
|
|
400
|
+
* @param input.autonomous - Whether the agent can act autonomously
|
|
401
|
+
* @param input.$id - Custom identifier URI (auto-generated if not provided)
|
|
402
|
+
* @returns A complete AgentIdentity object ready for storage
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```typescript
|
|
406
|
+
* // Create a supervised agent
|
|
407
|
+
* const assistant = createAgentIdentity({
|
|
408
|
+
* model: 'claude-3-opus',
|
|
409
|
+
* capabilities: ['text-generation', 'code-analysis'],
|
|
410
|
+
* autonomous: false
|
|
411
|
+
* })
|
|
412
|
+
*
|
|
413
|
+
* // Create an autonomous agent
|
|
414
|
+
* const worker = createAgentIdentity({
|
|
415
|
+
* model: 'claude-3-haiku',
|
|
416
|
+
* capabilities: ['task-execution', 'tool-use'],
|
|
417
|
+
* autonomous: true
|
|
418
|
+
* })
|
|
419
|
+
*
|
|
420
|
+
* // With custom $id
|
|
421
|
+
* const namedAgent = createAgentIdentity({
|
|
422
|
+
* $id: 'https://myapp.com/agents/ralph',
|
|
423
|
+
* model: 'claude-3-opus',
|
|
424
|
+
* capabilities: ['coding', 'review'],
|
|
425
|
+
* autonomous: true
|
|
426
|
+
* })
|
|
427
|
+
* ```
|
|
428
|
+
*/
|
|
429
|
+
export function createAgentIdentity(
|
|
430
|
+
input: Omit<AgentIdentity, '$type' | 'createdAt' | 'updatedAt' | '$id'> & { $id?: string }
|
|
431
|
+
): AgentIdentity {
|
|
432
|
+
const now = new Date().toISOString()
|
|
433
|
+
return {
|
|
434
|
+
$id: input.$id ?? generateId('agents'),
|
|
435
|
+
$type: 'https://schema.org.ai/AgentIdentity',
|
|
436
|
+
model: input.model,
|
|
437
|
+
capabilities: input.capabilities,
|
|
438
|
+
autonomous: input.autonomous,
|
|
439
|
+
createdAt: now,
|
|
440
|
+
updatedAt: now,
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// === Credential ===
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Supported credential types for authentication
|
|
448
|
+
*
|
|
449
|
+
* - `password` - Traditional username/password authentication
|
|
450
|
+
* - `oauth` - OAuth 2.0 / OpenID Connect (Google, GitHub, etc.)
|
|
451
|
+
* - `api_key` - API key authentication for programmatic access
|
|
452
|
+
* - `sso` - Enterprise Single Sign-On (SAML, etc.)
|
|
453
|
+
*/
|
|
454
|
+
export type CredentialType = 'password' | 'oauth' | 'api_key' | 'sso'
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Credential - Authentication credential for an identity
|
|
458
|
+
*
|
|
459
|
+
* Represents an authentication method associated with an {@link Identity}.
|
|
460
|
+
* Each identity can have multiple credentials of different types.
|
|
461
|
+
*
|
|
462
|
+
* Note: This stores credential metadata, NOT the actual secret values.
|
|
463
|
+
* Secret handling should be done through a secure credential store.
|
|
464
|
+
*
|
|
465
|
+
* @see https://schema.org.ai/Credential
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```typescript
|
|
469
|
+
* const credential: Credential = {
|
|
470
|
+
* $id: 'https://schema.org.ai/credentials/cred-123',
|
|
471
|
+
* $type: 'https://schema.org.ai/Credential',
|
|
472
|
+
* identityId: 'https://schema.org.ai/users/user-456',
|
|
473
|
+
* credentialType: 'oauth',
|
|
474
|
+
* provider: 'google',
|
|
475
|
+
* expiresAt: '2024-12-31T23:59:59Z'
|
|
476
|
+
* }
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
export interface Credential {
|
|
480
|
+
/** Unique identifier URI (JSON-LD @id) */
|
|
481
|
+
$id: string
|
|
482
|
+
/** Type discriminator (JSON-LD @type) */
|
|
483
|
+
$type: 'https://schema.org.ai/Credential'
|
|
484
|
+
/** Reference to the identity this credential belongs to */
|
|
485
|
+
identityId: string
|
|
486
|
+
/** The type of credential (password, oauth, api_key, sso) */
|
|
487
|
+
credentialType: CredentialType
|
|
488
|
+
/** OAuth/SSO provider name (e.g., 'google', 'github', 'okta') */
|
|
489
|
+
provider?: string
|
|
490
|
+
/** ISO 8601 timestamp when the credential expires (if applicable) */
|
|
491
|
+
expiresAt?: string
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Zod schema for validating Credential objects
|
|
496
|
+
*
|
|
497
|
+
* Validates credential type as one of the allowed enum values.
|
|
498
|
+
*
|
|
499
|
+
* @see {@link Credential}
|
|
500
|
+
* @see {@link CredentialType}
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```typescript
|
|
504
|
+
* const result = CredentialSchema.safeParse(credentialData)
|
|
505
|
+
* if (result.success) {
|
|
506
|
+
* const cred = result.data
|
|
507
|
+
* if (cred.credentialType === 'oauth' && cred.provider) {
|
|
508
|
+
* console.log(`OAuth via ${cred.provider}`)
|
|
509
|
+
* }
|
|
510
|
+
* }
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
export const CredentialSchema = z.object({
|
|
514
|
+
$id: z.string(),
|
|
515
|
+
$type: z.literal('https://schema.org.ai/Credential'),
|
|
516
|
+
identityId: z.string(),
|
|
517
|
+
credentialType: z.enum(['password', 'oauth', 'api_key', 'sso']),
|
|
518
|
+
provider: z.string().optional(),
|
|
519
|
+
expiresAt: z.string().optional(),
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Type guard to check if an object is a valid Credential
|
|
524
|
+
*
|
|
525
|
+
* Uses Zod schema validation internally to ensure type safety.
|
|
526
|
+
*
|
|
527
|
+
* @param obj - Object to validate
|
|
528
|
+
* @returns True if the object is a valid Credential
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```typescript
|
|
532
|
+
* const data: unknown = await getCredential(id)
|
|
533
|
+
* if (isCredential(data)) {
|
|
534
|
+
* // TypeScript knows data is Credential
|
|
535
|
+
* console.log(`Credential type: ${data.credentialType}`)
|
|
536
|
+
* if (data.expiresAt) {
|
|
537
|
+
* console.log(`Expires: ${data.expiresAt}`)
|
|
538
|
+
* }
|
|
539
|
+
* }
|
|
540
|
+
* ```
|
|
541
|
+
*/
|
|
542
|
+
export function isCredential(obj: unknown): obj is Credential {
|
|
543
|
+
return CredentialSchema.safeParse(obj).success
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Factory function to create a new Credential
|
|
548
|
+
*
|
|
549
|
+
* Creates a complete Credential object with auto-generated `$id` (if not provided)
|
|
550
|
+
* and the correct `$type`. The identityId and credentialType are required.
|
|
551
|
+
*
|
|
552
|
+
* @param input - Credential data
|
|
553
|
+
* @param input.identityId - Reference to the owning identity
|
|
554
|
+
* @param input.credentialType - Type of credential (password, oauth, api_key, sso)
|
|
555
|
+
* @param input.$id - Custom identifier URI (auto-generated if not provided)
|
|
556
|
+
* @param input.provider - OAuth/SSO provider name (optional)
|
|
557
|
+
* @param input.expiresAt - Expiration timestamp (optional)
|
|
558
|
+
* @returns A complete Credential object ready for storage
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```typescript
|
|
562
|
+
* // Password credential (never expires)
|
|
563
|
+
* const passwordCred = createCredential({
|
|
564
|
+
* identityId: 'https://schema.org.ai/users/123',
|
|
565
|
+
* credentialType: 'password'
|
|
566
|
+
* })
|
|
567
|
+
*
|
|
568
|
+
* // OAuth credential with expiration
|
|
569
|
+
* const oauthCred = createCredential({
|
|
570
|
+
* identityId: 'https://schema.org.ai/users/123',
|
|
571
|
+
* credentialType: 'oauth',
|
|
572
|
+
* provider: 'google',
|
|
573
|
+
* expiresAt: '2024-12-31T23:59:59Z'
|
|
574
|
+
* })
|
|
575
|
+
*
|
|
576
|
+
* // API key for programmatic access
|
|
577
|
+
* const apiKeyCred = createCredential({
|
|
578
|
+
* identityId: 'https://schema.org.ai/agents/worker-1',
|
|
579
|
+
* credentialType: 'api_key'
|
|
580
|
+
* })
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
export function createCredential(
|
|
584
|
+
input: Omit<Credential, '$type' | '$id'> & { $id?: string }
|
|
585
|
+
): Credential {
|
|
586
|
+
return {
|
|
587
|
+
$id: input.$id ?? generateId('credentials'),
|
|
588
|
+
$type: 'https://schema.org.ai/Credential',
|
|
589
|
+
identityId: input.identityId,
|
|
590
|
+
credentialType: input.credentialType,
|
|
591
|
+
provider: input.provider,
|
|
592
|
+
expiresAt: input.expiresAt,
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// === Session ===
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Session - Active authentication session
|
|
600
|
+
*
|
|
601
|
+
* Represents an authenticated session for an {@link Identity}. Sessions have
|
|
602
|
+
* a secure token and an expiration time. Use {@link isSessionExpired} to check
|
|
603
|
+
* if a session is still valid.
|
|
604
|
+
*
|
|
605
|
+
* @see https://schema.org.ai/Session
|
|
606
|
+
*
|
|
607
|
+
* @example
|
|
608
|
+
* ```typescript
|
|
609
|
+
* const session: Session = {
|
|
610
|
+
* $id: 'https://schema.org.ai/sessions/sess-123',
|
|
611
|
+
* $type: 'https://schema.org.ai/Session',
|
|
612
|
+
* identityId: 'https://schema.org.ai/users/user-456',
|
|
613
|
+
* token: 'a1b2c3d4e5f6...',
|
|
614
|
+
* expiresAt: '2024-01-02T00:00:00Z',
|
|
615
|
+
* metadata: { userAgent: 'Mozilla/5.0...', ip: '192.168.1.1' }
|
|
616
|
+
* }
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
619
|
+
export interface Session {
|
|
620
|
+
/** Unique identifier URI (JSON-LD @id) */
|
|
621
|
+
$id: string
|
|
622
|
+
/** Type discriminator (JSON-LD @type) */
|
|
623
|
+
$type: 'https://schema.org.ai/Session'
|
|
624
|
+
/** Reference to the authenticated identity */
|
|
625
|
+
identityId: string
|
|
626
|
+
/** Secure session token (auto-generated if not provided) */
|
|
627
|
+
token: string
|
|
628
|
+
/** ISO 8601 timestamp when the session expires */
|
|
629
|
+
expiresAt: string
|
|
630
|
+
/** Optional metadata about the session (user agent, IP, etc.) */
|
|
631
|
+
metadata?: Record<string, unknown>
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Zod schema for validating Session objects
|
|
636
|
+
*
|
|
637
|
+
* Validates all Session fields including non-empty token requirement.
|
|
638
|
+
*
|
|
639
|
+
* @see {@link Session}
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```typescript
|
|
643
|
+
* const result = SessionSchema.safeParse(sessionData)
|
|
644
|
+
* if (result.success) {
|
|
645
|
+
* const session = result.data
|
|
646
|
+
* // Use the validated session
|
|
647
|
+
* } else {
|
|
648
|
+
* console.error('Invalid session:', result.error.issues)
|
|
649
|
+
* }
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
export const SessionSchema = z.object({
|
|
653
|
+
$id: z.string(),
|
|
654
|
+
$type: z.literal('https://schema.org.ai/Session'),
|
|
655
|
+
identityId: z.string(),
|
|
656
|
+
token: z.string().min(1),
|
|
657
|
+
expiresAt: z.string(),
|
|
658
|
+
metadata: z.record(z.unknown()).optional(),
|
|
659
|
+
})
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Type guard to check if an object is a valid Session
|
|
663
|
+
*
|
|
664
|
+
* Uses Zod schema validation internally to ensure type safety.
|
|
665
|
+
* Note: This only validates structure, not expiration. Use {@link isSessionExpired}
|
|
666
|
+
* to check if a session has expired.
|
|
667
|
+
*
|
|
668
|
+
* @param obj - Object to validate
|
|
669
|
+
* @returns True if the object is a valid Session
|
|
670
|
+
*
|
|
671
|
+
* @example
|
|
672
|
+
* ```typescript
|
|
673
|
+
* const data: unknown = await getSession(token)
|
|
674
|
+
* if (isSession(data)) {
|
|
675
|
+
* // TypeScript knows data is Session
|
|
676
|
+
* if (!isSessionExpired(data)) {
|
|
677
|
+
* console.log(`Session valid until: ${data.expiresAt}`)
|
|
678
|
+
* }
|
|
679
|
+
* }
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
export function isSession(obj: unknown): obj is Session {
|
|
683
|
+
return SessionSchema.safeParse(obj).success
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Factory function to create a new Session
|
|
688
|
+
*
|
|
689
|
+
* Creates a complete Session object with auto-generated `$id`, `token`, and
|
|
690
|
+
* `expiresAt` (24 hours from now) if not provided. Only `identityId` is required.
|
|
691
|
+
*
|
|
692
|
+
* @param input - Session data
|
|
693
|
+
* @param input.identityId - Reference to the authenticated identity
|
|
694
|
+
* @param input.$id - Custom identifier URI (auto-generated if not provided)
|
|
695
|
+
* @param input.token - Custom session token (auto-generated if not provided)
|
|
696
|
+
* @param input.expiresAt - Custom expiration (defaults to 24 hours from now)
|
|
697
|
+
* @param input.metadata - Optional session metadata
|
|
698
|
+
* @returns A complete Session object ready for storage
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```typescript
|
|
702
|
+
* // Basic session with defaults (24h expiry, auto-generated token)
|
|
703
|
+
* const session = createSession({
|
|
704
|
+
* identityId: 'https://schema.org.ai/users/123'
|
|
705
|
+
* })
|
|
706
|
+
*
|
|
707
|
+
* // Session with custom expiration
|
|
708
|
+
* const shortSession = createSession({
|
|
709
|
+
* identityId: 'https://schema.org.ai/users/123',
|
|
710
|
+
* expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString() // 1 hour
|
|
711
|
+
* })
|
|
712
|
+
*
|
|
713
|
+
* // Session with metadata
|
|
714
|
+
* const trackedSession = createSession({
|
|
715
|
+
* identityId: 'https://schema.org.ai/users/123',
|
|
716
|
+
* metadata: {
|
|
717
|
+
* userAgent: 'Mozilla/5.0...',
|
|
718
|
+
* ip: '192.168.1.1',
|
|
719
|
+
* device: 'desktop'
|
|
720
|
+
* }
|
|
721
|
+
* })
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
724
|
+
export function createSession(
|
|
725
|
+
input: Omit<Session, '$type' | '$id' | 'token' | 'expiresAt'> & {
|
|
726
|
+
$id?: string
|
|
727
|
+
token?: string
|
|
728
|
+
expiresAt?: string
|
|
729
|
+
}
|
|
730
|
+
): Session {
|
|
731
|
+
// Default expiration: 24 hours from now
|
|
732
|
+
const defaultExpiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
733
|
+
|
|
734
|
+
return {
|
|
735
|
+
$id: input.$id ?? generateId('sessions'),
|
|
736
|
+
$type: 'https://schema.org.ai/Session',
|
|
737
|
+
identityId: input.identityId,
|
|
738
|
+
token: input.token ?? generateToken(),
|
|
739
|
+
expiresAt: input.expiresAt ?? defaultExpiresAt,
|
|
740
|
+
metadata: input.metadata,
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Check if a session has expired
|
|
746
|
+
*
|
|
747
|
+
* Compares the session's `expiresAt` timestamp against the current time.
|
|
748
|
+
*
|
|
749
|
+
* @param session - The session to check
|
|
750
|
+
* @returns True if the session has expired (expiresAt is in the past)
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* ```typescript
|
|
754
|
+
* const session = await getSession(token)
|
|
755
|
+
* if (isSession(session)) {
|
|
756
|
+
* if (isSessionExpired(session)) {
|
|
757
|
+
* // Redirect to login
|
|
758
|
+
* throw new Error('Session expired')
|
|
759
|
+
* }
|
|
760
|
+
* // Continue with authenticated request
|
|
761
|
+
* }
|
|
762
|
+
* ```
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* ```typescript
|
|
766
|
+
* // Clean up expired sessions
|
|
767
|
+
* const sessions = await getAllSessions()
|
|
768
|
+
* const activeSessions = sessions.filter(s => !isSessionExpired(s))
|
|
769
|
+
* const expiredSessions = sessions.filter(isSessionExpired)
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
export function isSessionExpired(session: Session): boolean {
|
|
773
|
+
return new Date(session.expiresAt).getTime() < Date.now()
|
|
774
|
+
}
|