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/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
+ }