@maravilla-labs/types 0.5.0 → 0.6.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/global.d.ts +23 -0
- package/index.d.ts +564 -97
- package/index.ts +656 -91
- package/package.json +3 -2
- package/tsconfig.test.json +10 -0
- package/types.test-d.ts +46 -0
package/index.ts
CHANGED
|
@@ -176,12 +176,33 @@ export interface DbFindOptions {
|
|
|
176
176
|
vector?: VectorQuery;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* A document as it is returned by the runtime. Every row the platform
|
|
181
|
+
* stores carries a string `id` (mirrored as `_id`) injected at insert
|
|
182
|
+
* time, plus optional server-managed timestamps. `find`/`findOne` return
|
|
183
|
+
* this shape; the generic `T` is your own document fields.
|
|
184
|
+
*
|
|
185
|
+
* This is a **type-level** contract — the runtime already injects/returns
|
|
186
|
+
* `id`+`_id` on both the dev (SQLite) and production (Mongo) paths, so no
|
|
187
|
+
* normalization happens here.
|
|
188
|
+
*/
|
|
189
|
+
export type DbDocument<T = Record<string, unknown>> = T & {
|
|
190
|
+
/** Stable string row id, injected by the runtime on insert. */
|
|
191
|
+
id: string;
|
|
192
|
+
/** Mirror of `id` for MongoDB-style call sites. Always equals `id`. */
|
|
193
|
+
_id: string;
|
|
194
|
+
/** Server-managed creation timestamp, when present. */
|
|
195
|
+
_created_at?: string;
|
|
196
|
+
/** Server-managed update timestamp, when present. */
|
|
197
|
+
_updated_at?: string;
|
|
198
|
+
};
|
|
199
|
+
|
|
179
200
|
/**
|
|
180
201
|
* Database collection interface
|
|
181
202
|
*/
|
|
182
203
|
export interface DbCollection {
|
|
183
|
-
find(filter?: Record<string, any>, options?: DbFindOptions): Promise<
|
|
184
|
-
findOne(filter?: Record<string, any>): Promise<
|
|
204
|
+
find<T = Record<string, unknown>>(filter?: Record<string, any>, options?: DbFindOptions): Promise<DbDocument<T>[]>;
|
|
205
|
+
findOne<T = Record<string, unknown>>(filter?: Record<string, any>): Promise<DbDocument<T> | null>;
|
|
185
206
|
insertOne(document: Record<string, any>): Promise<string>;
|
|
186
207
|
updateOne(filter: Record<string, any>, update: Record<string, any>): Promise<void>;
|
|
187
208
|
deleteOne(filter: Record<string, any>): Promise<void>;
|
|
@@ -317,7 +338,378 @@ export interface Storage {
|
|
|
317
338
|
getMetadata(key: string): Promise<StorageMetadata>;
|
|
318
339
|
}
|
|
319
340
|
|
|
320
|
-
//
|
|
341
|
+
// ════════════════════════════════════════════════════════════════════
|
|
342
|
+
// Auth, Stewardship, Resources, Circles, Groups, Relations
|
|
343
|
+
// ════════════════════════════════════════════════════════════════════
|
|
344
|
+
//
|
|
345
|
+
// These are the CANONICAL definitions for the entire auth/policy/relations
|
|
346
|
+
// surface. `@maravilla-labs/platform` re-exports every symbol below from
|
|
347
|
+
// its own `types.ts`, and `RemoteAuthService implements AuthService`. Keep
|
|
348
|
+
// this the single source of truth — do not fork divergent copies in the
|
|
349
|
+
// platform package.
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Authenticated user record from the platform auth service.
|
|
353
|
+
*/
|
|
354
|
+
export interface AuthUser {
|
|
355
|
+
/** Unique user ID (prefixed with "usr_") */
|
|
356
|
+
id: string;
|
|
357
|
+
/** User's email address */
|
|
358
|
+
email: string;
|
|
359
|
+
/** Whether the email has been verified */
|
|
360
|
+
email_verified: boolean;
|
|
361
|
+
/** Account status */
|
|
362
|
+
status: 'active' | 'suspended' | 'deactivated';
|
|
363
|
+
/** Authentication provider ("email", "google", "github", "managed", etc.) */
|
|
364
|
+
provider: string;
|
|
365
|
+
/** Group IDs the user belongs to */
|
|
366
|
+
groups: string[];
|
|
367
|
+
/**
|
|
368
|
+
* Caller-supplied external identifier used for idempotent
|
|
369
|
+
* managed-user creation (FR-3). `null`/absent for normal accounts.
|
|
370
|
+
*/
|
|
371
|
+
external_id?: string | null;
|
|
372
|
+
/** Unix timestamp when the user was created */
|
|
373
|
+
created_at: number;
|
|
374
|
+
/** Unix timestamp when the user was last updated */
|
|
375
|
+
updated_at: number;
|
|
376
|
+
/** Unix timestamp of last login (if any) */
|
|
377
|
+
last_login_at?: number;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Snapshot of whoever is currently bound to the request as the caller.
|
|
382
|
+
* This is exactly what per-resource policies see as `auth.*`.
|
|
383
|
+
*/
|
|
384
|
+
export interface AuthCaller {
|
|
385
|
+
/** Caller's user id, or `""` if anonymous */
|
|
386
|
+
user_id: string;
|
|
387
|
+
/** Caller's email, or `""` if anonymous */
|
|
388
|
+
email: string;
|
|
389
|
+
/** Admin flag from the session */
|
|
390
|
+
is_admin: boolean;
|
|
391
|
+
/** Role names (project-scoped) */
|
|
392
|
+
roles: string[];
|
|
393
|
+
/** `true` when no identity is bound to this request */
|
|
394
|
+
is_anonymous: boolean;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Session returned after successful login or token refresh.
|
|
399
|
+
*/
|
|
400
|
+
export interface AuthSession {
|
|
401
|
+
/** Short-lived JWT access token (default 15 min) */
|
|
402
|
+
access_token: string;
|
|
403
|
+
/** Single-use opaque refresh token (default 30 days) */
|
|
404
|
+
refresh_token: string;
|
|
405
|
+
/** Access token lifetime in seconds */
|
|
406
|
+
expires_in: number;
|
|
407
|
+
/** The authenticated user */
|
|
408
|
+
user: AuthUser;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Custom registration field defined in project auth settings.
|
|
413
|
+
*/
|
|
414
|
+
export interface AuthField {
|
|
415
|
+
/** Field key (used as form field name) */
|
|
416
|
+
key: string;
|
|
417
|
+
/** Display label */
|
|
418
|
+
label: string;
|
|
419
|
+
/** Field type: text, email, phone, date, number, select, boolean, url, textarea */
|
|
420
|
+
field_type: string;
|
|
421
|
+
/** Whether the field is required */
|
|
422
|
+
required: boolean;
|
|
423
|
+
/** Whether the field appears on the registration form */
|
|
424
|
+
show_on_register: boolean;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Options for registering a new user.
|
|
429
|
+
*/
|
|
430
|
+
export interface RegisterOptions {
|
|
431
|
+
/** User's email address */
|
|
432
|
+
email: string;
|
|
433
|
+
/** Password (minimum 8 characters) */
|
|
434
|
+
password: string;
|
|
435
|
+
/** Optional profile data (custom fields) */
|
|
436
|
+
profile?: Record<string, any>;
|
|
437
|
+
/**
|
|
438
|
+
* Caller-supplied external id. When set, registration is idempotent on
|
|
439
|
+
* `(tenant, external_id)` — re-registering with the same key returns the
|
|
440
|
+
* existing user instead of erroring (FR-3).
|
|
441
|
+
*/
|
|
442
|
+
external_id?: string;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Options for logging in.
|
|
447
|
+
*/
|
|
448
|
+
export interface LoginOptions {
|
|
449
|
+
/** User's email address */
|
|
450
|
+
email: string;
|
|
451
|
+
/** User's password */
|
|
452
|
+
password: string;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Options for creating a managed (no-login) user — e.g. an imported
|
|
457
|
+
* contact or service-owned record that authenticates out-of-band (FR-2).
|
|
458
|
+
* Created with no password; sessions are only minted when the account is
|
|
459
|
+
* later activated.
|
|
460
|
+
*/
|
|
461
|
+
export interface CreateManagedUserOptions {
|
|
462
|
+
/** Optional email. A synthetic no-login address is generated when omitted. */
|
|
463
|
+
email?: string;
|
|
464
|
+
/** Optional profile data. */
|
|
465
|
+
profile?: Record<string, any>;
|
|
466
|
+
/** Optional external id for idempotent create-by-key (FR-3). */
|
|
467
|
+
external_id?: string;
|
|
468
|
+
/** Optional group ids/slugs to add the user to on creation. */
|
|
469
|
+
groups?: string[];
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Filter options for listing users.
|
|
474
|
+
*/
|
|
475
|
+
export interface UserListFilter {
|
|
476
|
+
/** Max results per page (default 50) */
|
|
477
|
+
limit?: number;
|
|
478
|
+
/** Number of results to skip */
|
|
479
|
+
offset?: number;
|
|
480
|
+
/** Filter by account status */
|
|
481
|
+
status?: 'active' | 'suspended' | 'deactivated';
|
|
482
|
+
/** Filter by email (partial match) */
|
|
483
|
+
email_contains?: string;
|
|
484
|
+
/** Filter by group ID */
|
|
485
|
+
group_id?: string;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Paginated user list response.
|
|
490
|
+
*/
|
|
491
|
+
export interface UserListResponse {
|
|
492
|
+
/** Users in this page */
|
|
493
|
+
users: AuthUser[];
|
|
494
|
+
/** Total number of matching users */
|
|
495
|
+
total: number;
|
|
496
|
+
/** Page size */
|
|
497
|
+
limit: number;
|
|
498
|
+
/** Offset */
|
|
499
|
+
offset: number;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Options for updating a user.
|
|
504
|
+
*/
|
|
505
|
+
export interface UpdateUserOptions {
|
|
506
|
+
/** New email address */
|
|
507
|
+
email?: string;
|
|
508
|
+
/** New status */
|
|
509
|
+
status?: 'active' | 'suspended' | 'deactivated';
|
|
510
|
+
/** Profile data to merge */
|
|
511
|
+
profile?: Record<string, any>;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// ── Groups (RBAC) ──
|
|
515
|
+
|
|
516
|
+
export interface AuthGroup {
|
|
517
|
+
id: string;
|
|
518
|
+
name: string;
|
|
519
|
+
description: string | null;
|
|
520
|
+
permissions: string[];
|
|
521
|
+
member_count: number;
|
|
522
|
+
created_at: number;
|
|
523
|
+
updated_at: number;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export interface CreateGroupOptions {
|
|
527
|
+
name: string;
|
|
528
|
+
description?: string;
|
|
529
|
+
permissions?: string[];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export interface UpdateGroupOptions {
|
|
533
|
+
name?: string;
|
|
534
|
+
description?: string;
|
|
535
|
+
permissions?: string[];
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export interface GroupPermission {
|
|
539
|
+
resource_name: string;
|
|
540
|
+
actions: string[];
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// ── Circles ──
|
|
544
|
+
|
|
545
|
+
export interface AuthCircle {
|
|
546
|
+
id: string;
|
|
547
|
+
name: string;
|
|
548
|
+
metadata: Record<string, any> | null;
|
|
549
|
+
member_count: number;
|
|
550
|
+
created_at: number;
|
|
551
|
+
updated_at: number;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
export interface CreateCircleOptions {
|
|
555
|
+
name: string;
|
|
556
|
+
metadata?: Record<string, any>;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export interface UpdateCircleOptions {
|
|
560
|
+
name?: string;
|
|
561
|
+
metadata?: Record<string, any>;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export interface AddCircleMemberOptions {
|
|
565
|
+
user_id: string;
|
|
566
|
+
relationship: string;
|
|
567
|
+
is_primary_contact?: boolean;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/** Circle membership entry */
|
|
571
|
+
export interface CircleMembership {
|
|
572
|
+
user_id: string;
|
|
573
|
+
email: string;
|
|
574
|
+
relationship: string;
|
|
575
|
+
is_primary_contact: boolean;
|
|
576
|
+
joined_at: number;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// ── Resources ──
|
|
580
|
+
|
|
581
|
+
export type ResourceServiceType =
|
|
582
|
+
| 'kv'
|
|
583
|
+
| 'database'
|
|
584
|
+
| 'realtime'
|
|
585
|
+
| 'media'
|
|
586
|
+
| 'vector'
|
|
587
|
+
| 'storage'
|
|
588
|
+
| 'queue'
|
|
589
|
+
| 'push'
|
|
590
|
+
| 'workflow'
|
|
591
|
+
| 'transforms';
|
|
592
|
+
|
|
593
|
+
/** A platform resource definition. */
|
|
594
|
+
export interface Resource {
|
|
595
|
+
id: string;
|
|
596
|
+
resource_name: string;
|
|
597
|
+
title: string;
|
|
598
|
+
description: string | null;
|
|
599
|
+
actions: string[];
|
|
600
|
+
policy: string | null;
|
|
601
|
+
service_type: ResourceServiceType | null;
|
|
602
|
+
read_filter: string | null;
|
|
603
|
+
created_at: number;
|
|
604
|
+
updated_at: number;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
export interface CreateResourceOptions {
|
|
608
|
+
resource_name: string;
|
|
609
|
+
title: string;
|
|
610
|
+
description?: string;
|
|
611
|
+
actions?: string[];
|
|
612
|
+
policy?: string;
|
|
613
|
+
service_type?: ResourceServiceType;
|
|
614
|
+
read_filter?: string;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export interface UpdateResourceOptions {
|
|
618
|
+
title?: string;
|
|
619
|
+
description?: string;
|
|
620
|
+
actions?: string[];
|
|
621
|
+
policy?: string;
|
|
622
|
+
service_type?: ResourceServiceType;
|
|
623
|
+
read_filter?: string;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ── Relations (typed edges) ──
|
|
627
|
+
|
|
628
|
+
export interface RelationType {
|
|
629
|
+
id: string;
|
|
630
|
+
relation_name: string;
|
|
631
|
+
title: string;
|
|
632
|
+
description: string | null;
|
|
633
|
+
category: string;
|
|
634
|
+
icon: string | null;
|
|
635
|
+
color: string | null;
|
|
636
|
+
inverse_relation_id: string | null;
|
|
637
|
+
implies_stewardship: boolean;
|
|
638
|
+
requires_minor: boolean;
|
|
639
|
+
bidirectional: boolean;
|
|
640
|
+
is_system: boolean;
|
|
641
|
+
created_at: number;
|
|
642
|
+
updated_at: number;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export interface CreateRelationTypeOptions {
|
|
646
|
+
relation_name: string;
|
|
647
|
+
title: string;
|
|
648
|
+
description?: string;
|
|
649
|
+
category?: string;
|
|
650
|
+
icon?: string;
|
|
651
|
+
color?: string;
|
|
652
|
+
inverse_relation_id?: string;
|
|
653
|
+
implies_stewardship?: boolean;
|
|
654
|
+
requires_minor?: boolean;
|
|
655
|
+
bidirectional?: boolean;
|
|
656
|
+
is_system?: boolean;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
export interface UpdateRelationTypeOptions {
|
|
660
|
+
title?: string;
|
|
661
|
+
description?: string;
|
|
662
|
+
category?: string;
|
|
663
|
+
icon?: string;
|
|
664
|
+
color?: string;
|
|
665
|
+
inverse_relation_id?: string;
|
|
666
|
+
implies_stewardship?: boolean;
|
|
667
|
+
requires_minor?: boolean;
|
|
668
|
+
bidirectional?: boolean;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** A single typed relation edge between two users (FR-1). */
|
|
672
|
+
export interface Relation {
|
|
673
|
+
id: string;
|
|
674
|
+
from_user_id: string;
|
|
675
|
+
to_user_id: string;
|
|
676
|
+
/** The relation type's name (e.g. `STEWARDS`). */
|
|
677
|
+
relation_type: string;
|
|
678
|
+
/** The relation type's id (`rlt_…`). */
|
|
679
|
+
relation_type_id: string;
|
|
680
|
+
metadata?: Record<string, any> | null;
|
|
681
|
+
created_at: number;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** Options for adding a relation edge. */
|
|
685
|
+
export interface AddRelationOptions {
|
|
686
|
+
from_user_id: string;
|
|
687
|
+
to_user_id: string;
|
|
688
|
+
/** Either a relation-type id (`rlt_…`) or a relation name. */
|
|
689
|
+
relation_type: string;
|
|
690
|
+
metadata?: Record<string, any>;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/** Direction filter when listing a user's relations. */
|
|
694
|
+
export type RelationListDirection = 'outgoing' | 'incoming' | 'both';
|
|
695
|
+
|
|
696
|
+
/** Options for listing relation edges touching a user. */
|
|
697
|
+
export interface ListRelationsOptions {
|
|
698
|
+
user_id: string;
|
|
699
|
+
direction?: RelationListDirection;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// ── Auth config (extended) ──
|
|
703
|
+
|
|
704
|
+
export interface AuthConfig {
|
|
705
|
+
fields: AuthField[];
|
|
706
|
+
oauth_providers: any[];
|
|
707
|
+
branding: Record<string, any>;
|
|
708
|
+
password_policy: Record<string, any>;
|
|
709
|
+
session_config: Record<string, any>;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// ── Stewardship ──
|
|
321
713
|
|
|
322
714
|
/** Delegation mode for stewardship overrides */
|
|
323
715
|
export type DelegationMode = 'full' | 'scoped';
|
|
@@ -340,19 +732,19 @@ export interface StewardshipOverride {
|
|
|
340
732
|
ward_id: string;
|
|
341
733
|
delegation_mode: DelegationMode;
|
|
342
734
|
scoped_permissions: ScopedPermission[];
|
|
343
|
-
valid_from
|
|
344
|
-
valid_until
|
|
735
|
+
valid_from: number | null;
|
|
736
|
+
valid_until: number | null;
|
|
345
737
|
status: StewardshipStatus;
|
|
346
|
-
reason
|
|
738
|
+
reason: string | null;
|
|
347
739
|
source: string;
|
|
348
|
-
source_circle_id
|
|
349
|
-
source_relation_type_id
|
|
740
|
+
source_circle_id: string | null;
|
|
741
|
+
source_relation_type_id: string | null;
|
|
350
742
|
created_at: number;
|
|
351
743
|
updated_at: number;
|
|
352
744
|
}
|
|
353
745
|
|
|
354
746
|
/** Options for creating a stewardship override */
|
|
355
|
-
export interface
|
|
747
|
+
export interface CreateStewardshipOverrideOptions {
|
|
356
748
|
steward_id: string;
|
|
357
749
|
ward_id: string;
|
|
358
750
|
delegation_mode?: DelegationMode;
|
|
@@ -386,67 +778,261 @@ export interface StewardshipAuditEntry {
|
|
|
386
778
|
performed_by: string;
|
|
387
779
|
on_behalf_of: string;
|
|
388
780
|
action: string;
|
|
389
|
-
resource
|
|
390
|
-
details
|
|
781
|
+
resource: string | null;
|
|
782
|
+
details: Record<string, any> | null;
|
|
391
783
|
created_at: number;
|
|
392
784
|
}
|
|
393
785
|
|
|
394
|
-
/**
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
/** Stewardship service interface */
|
|
401
|
-
export interface Stewardship {
|
|
786
|
+
/**
|
|
787
|
+
* Sub-namespace exposed at `platform.auth.stewardship` mirroring the
|
|
788
|
+
* runtime's `globalThis.platform.auth.stewardship.*` surface.
|
|
789
|
+
*/
|
|
790
|
+
export interface AuthStewardshipApi {
|
|
402
791
|
resolve(userId: string): Promise<StewardshipResolution>;
|
|
403
|
-
createOverride(
|
|
792
|
+
createOverride(opts: CreateStewardshipOverrideOptions): Promise<StewardshipOverride>;
|
|
404
793
|
revoke(id: string): Promise<void>;
|
|
405
794
|
checkPermission(stewardId: string, wardId: string, resource: string, action: string): Promise<boolean>;
|
|
406
795
|
createActAs(stewardId: string, wardId: string): Promise<ActAsContext>;
|
|
407
|
-
listAudit(userId: string, options?:
|
|
796
|
+
listAudit(userId: string, options?: { limit?: number; offset?: number }): Promise<StewardshipAuditEntry[]>;
|
|
408
797
|
}
|
|
409
798
|
|
|
410
|
-
//
|
|
799
|
+
// ── Authorization explain / batch (FR-7) ──
|
|
411
800
|
|
|
412
|
-
/**
|
|
413
|
-
export interface
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
actions: string[];
|
|
419
|
-
created_at: number;
|
|
420
|
-
updated_at: number;
|
|
801
|
+
/** Result of an `explain()` policy check. */
|
|
802
|
+
export interface PolicyExplain {
|
|
803
|
+
allowed: boolean;
|
|
804
|
+
reason?: string;
|
|
805
|
+
/** The specific clause that caused a denial, when the engine can isolate it. */
|
|
806
|
+
failedClause?: string;
|
|
421
807
|
}
|
|
422
808
|
|
|
423
|
-
/**
|
|
424
|
-
export interface
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
actions?: string[];
|
|
809
|
+
/** A single authorization check for batched `canMany()`. */
|
|
810
|
+
export interface CanCheck {
|
|
811
|
+
action: string;
|
|
812
|
+
resourceId: string;
|
|
813
|
+
node?: Record<string, unknown> | null;
|
|
429
814
|
}
|
|
430
815
|
|
|
431
|
-
|
|
816
|
+
/**
|
|
817
|
+
* Auth service for end-user authentication and user management.
|
|
818
|
+
* Implemented by the in-runtime native bridge and by the dev-server
|
|
819
|
+
* remote client (`RemoteAuthService`).
|
|
820
|
+
*/
|
|
821
|
+
export interface AuthService {
|
|
822
|
+
/**
|
|
823
|
+
* Register a new user with email and password.
|
|
824
|
+
* @returns The created user (not yet email-verified)
|
|
825
|
+
*/
|
|
826
|
+
register(options: RegisterOptions): Promise<AuthUser>;
|
|
432
827
|
|
|
433
|
-
/**
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
is_primary_contact: boolean;
|
|
439
|
-
joined_at: number;
|
|
440
|
-
}
|
|
828
|
+
/**
|
|
829
|
+
* Authenticate a user and create a session.
|
|
830
|
+
* @returns Session with access token, refresh token, and user info
|
|
831
|
+
*/
|
|
832
|
+
login(options: LoginOptions): Promise<AuthSession>;
|
|
441
833
|
|
|
442
|
-
/**
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
834
|
+
/**
|
|
835
|
+
* Validate an access token and return the authenticated user.
|
|
836
|
+
* @throws If the token is invalid or expired
|
|
837
|
+
*/
|
|
838
|
+
validate(accessToken: string): Promise<AuthUser>;
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Refresh a session using a refresh token (single-use).
|
|
842
|
+
* @returns New session with fresh access and refresh tokens
|
|
843
|
+
*/
|
|
844
|
+
refresh(refreshToken: string): Promise<AuthSession>;
|
|
845
|
+
|
|
846
|
+
/** Revoke a specific session. */
|
|
847
|
+
logout(sessionId: string): Promise<void>;
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Get a user by ID.
|
|
851
|
+
* @returns The user, or null if not found
|
|
852
|
+
*/
|
|
853
|
+
getUser(userId: string): Promise<AuthUser | null>;
|
|
854
|
+
|
|
855
|
+
/** List users with optional filtering and pagination. */
|
|
856
|
+
listUsers(filter?: UserListFilter): Promise<UserListResponse>;
|
|
857
|
+
|
|
858
|
+
/** Update a user's email, status, or profile data. */
|
|
859
|
+
updateUser(userId: string, update: UpdateUserOptions): Promise<AuthUser>;
|
|
860
|
+
|
|
861
|
+
/** Delete a user and all their sessions. */
|
|
862
|
+
deleteUser(userId: string): Promise<void>;
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Create a managed (no-login) user (FR-2). Idempotent on `external_id`
|
|
866
|
+
* when supplied (FR-3).
|
|
867
|
+
*/
|
|
868
|
+
createManagedUser(options: CreateManagedUserOptions): Promise<AuthUser>;
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Create an email verification token.
|
|
872
|
+
* @returns The verification token (caller decides how to deliver it)
|
|
873
|
+
*/
|
|
874
|
+
sendVerification(userId: string): Promise<{ token: string }>;
|
|
875
|
+
|
|
876
|
+
/** Verify an email address using a verification token. */
|
|
877
|
+
verifyEmail(token: string): Promise<void>;
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Create a password reset token for an email address.
|
|
881
|
+
* @returns The reset token (caller decides how to deliver it)
|
|
882
|
+
*/
|
|
883
|
+
sendPasswordReset(email: string): Promise<{ token: string }>;
|
|
884
|
+
|
|
885
|
+
/** Reset a password using a reset token. */
|
|
886
|
+
resetPassword(token: string, newPassword: string): Promise<void>;
|
|
887
|
+
|
|
888
|
+
/** Change a user's password (requires old password). */
|
|
889
|
+
changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void>;
|
|
890
|
+
|
|
891
|
+
/** Get the configured registration fields for this project. */
|
|
892
|
+
getFieldConfig(): Promise<{ fields: AuthField[] }>;
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Start an OAuth flow by generating an authorization URL.
|
|
896
|
+
* @param provider - "google", "github", "okta", or "custom_oidc"
|
|
897
|
+
*/
|
|
898
|
+
getOAuthUrl(provider: string, options?: { redirectUri?: string }): Promise<{ auth_url: string; state: string }>;
|
|
899
|
+
|
|
900
|
+
/** Complete an OAuth flow by exchanging the authorization code. */
|
|
901
|
+
handleOAuthCallback(provider: string, params: { code: string; state: string }): Promise<
|
|
902
|
+
| AuthSession
|
|
903
|
+
| { type: 'LinkRequired'; email: string; provider: string; provider_id: string; existing_user_id: string }
|
|
904
|
+
>;
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Middleware helper that validates auth and injects `request.user`.
|
|
908
|
+
* Returns 401 JSON response if no valid token is found.
|
|
909
|
+
*/
|
|
910
|
+
withAuth<T extends (request: Request & { user: AuthUser }) => Promise<Response>>(
|
|
911
|
+
handler: T
|
|
912
|
+
): (request: Request) => Promise<Response>;
|
|
913
|
+
|
|
914
|
+
// ── Groups (RBAC) ──
|
|
915
|
+
|
|
916
|
+
createGroup(options: CreateGroupOptions): Promise<AuthGroup>;
|
|
917
|
+
listGroups(): Promise<AuthGroup[]>;
|
|
918
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
919
|
+
getGroup(groupId: string): Promise<AuthGroup | null>;
|
|
920
|
+
/**
|
|
921
|
+
* Look up a group by its declarative name (the same name used in
|
|
922
|
+
* `maravilla.config.ts`'s `groups: [...]` block). Returns null if the
|
|
923
|
+
* auth-settings reconciler hasn't created it yet.
|
|
924
|
+
*/
|
|
925
|
+
getGroupByName(name: string): Promise<AuthGroup | null>;
|
|
926
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
927
|
+
updateGroup(groupId: string, options: UpdateGroupOptions): Promise<AuthGroup>;
|
|
928
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
929
|
+
deleteGroup(groupId: string): Promise<void>;
|
|
930
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
931
|
+
addUserToGroup(userId: string, groupId: string): Promise<void>;
|
|
932
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
933
|
+
removeUserFromGroup(userId: string, groupId: string): Promise<void>;
|
|
934
|
+
getUserGroups(userId: string): Promise<AuthGroup[]>;
|
|
935
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
936
|
+
getGroupMembers(groupId: string): Promise<AuthUser[]>;
|
|
937
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
938
|
+
getGroupPermissions(groupId: string): Promise<GroupPermission[]>;
|
|
939
|
+
/** @param groupId - the group id **or slug** (resolved server-side). */
|
|
940
|
+
setGroupPermissions(groupId: string, permissions: GroupPermission[]): Promise<void>;
|
|
941
|
+
|
|
942
|
+
// ── Circles ──
|
|
943
|
+
|
|
944
|
+
createCircle(options: CreateCircleOptions): Promise<AuthCircle>;
|
|
945
|
+
listCircles(): Promise<AuthCircle[]>;
|
|
946
|
+
/** @param circleId - the circle id **or slug** (resolved server-side). */
|
|
947
|
+
getCircle(circleId: string): Promise<AuthCircle | null>;
|
|
948
|
+
/** @param circleId - the circle id **or slug** (resolved server-side). */
|
|
949
|
+
updateCircle(circleId: string, options: UpdateCircleOptions): Promise<AuthCircle>;
|
|
950
|
+
/** @param circleId - the circle id **or slug** (resolved server-side). */
|
|
951
|
+
deleteCircle(circleId: string): Promise<void>;
|
|
952
|
+
/** @param circleId - the circle id **or slug** (resolved server-side). */
|
|
953
|
+
addCircleMember(circleId: string, options: AddCircleMemberOptions): Promise<void>;
|
|
954
|
+
/** @param circleId - the circle id **or slug** (resolved server-side). */
|
|
955
|
+
removeCircleMember(circleId: string, userId: string): Promise<void>;
|
|
956
|
+
/** @param circleId - the circle id **or slug** (resolved server-side). */
|
|
957
|
+
getCircleMembers(circleId: string): Promise<CircleMembership[]>;
|
|
958
|
+
getUserCircles(userId: string): Promise<AuthCircle[]>;
|
|
959
|
+
|
|
960
|
+
// ── Resources ──
|
|
961
|
+
|
|
962
|
+
createResource(options: CreateResourceOptions): Promise<Resource>;
|
|
963
|
+
listResources(): Promise<Resource[]>;
|
|
964
|
+
updateResource(resourceId: string, options: UpdateResourceOptions): Promise<Resource>;
|
|
965
|
+
deleteResource(resourceId: string): Promise<void>;
|
|
966
|
+
|
|
967
|
+
// ── Relation types ──
|
|
968
|
+
|
|
969
|
+
createRelationType(options: CreateRelationTypeOptions): Promise<RelationType>;
|
|
970
|
+
listRelationTypes(): Promise<RelationType[]>;
|
|
971
|
+
/** @param id - the relation-type id **or slug** (resolved server-side). */
|
|
972
|
+
updateRelationType(id: string, options: UpdateRelationTypeOptions): Promise<RelationType>;
|
|
973
|
+
/** @param id - the relation-type id **or slug** (resolved server-side). */
|
|
974
|
+
deleteRelationType(id: string): Promise<void>;
|
|
975
|
+
|
|
976
|
+
// ── Relations (typed edges, FR-1) ──
|
|
977
|
+
|
|
978
|
+
/** Add a directed relation edge between two users. */
|
|
979
|
+
addRelation(options: AddRelationOptions): Promise<Relation>;
|
|
980
|
+
/** Remove a relation edge by its endpoints and relation-type id. */
|
|
981
|
+
removeRelation(fromUserId: string, toUserId: string, relationTypeId: string): Promise<void>;
|
|
982
|
+
/** List relation edges touching a user, optionally filtered by direction. */
|
|
983
|
+
listRelations(options: ListRelationsOptions): Promise<Relation[]>;
|
|
984
|
+
|
|
985
|
+
// ── Profile ──
|
|
986
|
+
|
|
987
|
+
getProfile(userId: string): Promise<Record<string, any>>;
|
|
988
|
+
setProfile(userId: string, data: Record<string, any>): Promise<void>;
|
|
989
|
+
|
|
990
|
+
// ── Auth config ──
|
|
991
|
+
|
|
992
|
+
getAuthConfig(): Promise<AuthConfig>;
|
|
993
|
+
setAuthConfig(config: AuthConfig): Promise<void>;
|
|
994
|
+
|
|
995
|
+
// ── Stewardship (sub-namespace mirroring the runtime bridge) ──
|
|
996
|
+
|
|
997
|
+
readonly stewardship: AuthStewardshipApi;
|
|
998
|
+
|
|
999
|
+
// ── Request-scoped identity + authorization ──
|
|
1000
|
+
//
|
|
1001
|
+
// These operate on the current request's caller context. They're
|
|
1002
|
+
// meaningful inside the runtime; on remote clients they throw (no
|
|
1003
|
+
// per-request context) or fail closed.
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Explicitly bind the caller for the remainder of this request.
|
|
1007
|
+
* Pass a JWT to validate + bind, or `null` / `""` to clear.
|
|
1008
|
+
*/
|
|
1009
|
+
setCurrentUser(token: string | null): Promise<void>;
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Snapshot of the currently bound caller. Returns an anonymous caller
|
|
1013
|
+
* (`is_anonymous: true`) when no identity has been bound.
|
|
1014
|
+
*/
|
|
1015
|
+
getCurrentUser(): AuthCaller;
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Ask the policy engine whether the bound caller would be allowed to
|
|
1019
|
+
* perform `action` on `resourceId`. Returns a boolean — never throws on
|
|
1020
|
+
* denial. Fails closed when no caller is bound.
|
|
1021
|
+
*/
|
|
1022
|
+
can(action: string, resourceId: string, node?: Record<string, unknown> | null): Promise<boolean>;
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Like {@link can}, but returns *why* (FR-7). Fails closed with
|
|
1026
|
+
* `{ allowed: false, reason: 'no bound user' }` when no caller is bound.
|
|
1027
|
+
*/
|
|
1028
|
+
explain(action: string, resourceId: string, node?: Record<string, unknown> | null): Promise<PolicyExplain>;
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Batch authorization check (FR-7). Results are returned in the same
|
|
1032
|
+
* order as `checks`. Fails closed (all `{ allowed: false }`) when no
|
|
1033
|
+
* caller is bound.
|
|
1034
|
+
*/
|
|
1035
|
+
canMany(checks: CanCheck[]): Promise<{ allowed: boolean }[]>;
|
|
450
1036
|
}
|
|
451
1037
|
|
|
452
1038
|
// Platform Types
|
|
@@ -538,39 +1124,13 @@ export interface Platform {
|
|
|
538
1124
|
storage: Storage;
|
|
539
1125
|
/** Durable multi-step workflows */
|
|
540
1126
|
workflows: Workflows;
|
|
541
|
-
/**
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
getUser(userId: string): Promise<any>;
|
|
549
|
-
listUsers(filter?: Record<string, any>): Promise<any>;
|
|
550
|
-
updateUser(userId: string, update: Record<string, any>): Promise<any>;
|
|
551
|
-
deleteUser(userId: string): Promise<void>;
|
|
552
|
-
sendVerification(userId: string): Promise<any>;
|
|
553
|
-
verifyEmail(token: string): Promise<void>;
|
|
554
|
-
sendPasswordReset(email: string): Promise<any>;
|
|
555
|
-
resetPassword(token: string, newPassword: string): Promise<void>;
|
|
556
|
-
changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void>;
|
|
557
|
-
getFieldConfig(): Promise<any>;
|
|
558
|
-
getOAuthUrl(provider: string, options?: { redirectUri?: string }): Promise<any>;
|
|
559
|
-
handleOAuthCallback(provider: string, params: { code: string; state: string }): Promise<any>;
|
|
560
|
-
|
|
561
|
-
/** Stewardship (guardian/ward delegation) */
|
|
562
|
-
stewardship: Stewardship;
|
|
563
|
-
|
|
564
|
-
/** List available resources */
|
|
565
|
-
listResources(): Promise<Resource[]>;
|
|
566
|
-
/** Create a resource definition */
|
|
567
|
-
createResource(options: CreateResourceRequest): Promise<Resource>;
|
|
568
|
-
|
|
569
|
-
/** Get circle members */
|
|
570
|
-
getCircleMembers(circleId: string): Promise<CircleMembership[]>;
|
|
571
|
-
/** Get circles a user belongs to */
|
|
572
|
-
getUserCircles(userId: string): Promise<Circle[]>;
|
|
573
|
-
};
|
|
1127
|
+
/**
|
|
1128
|
+
* Auth service — the full canonical {@link AuthService} surface:
|
|
1129
|
+
* authentication, user management, groups, circles, resources, relation
|
|
1130
|
+
* types, typed relation edges, stewardship, profile/config, and the
|
|
1131
|
+
* request-scoped `can`/`explain`/`canMany` authorization checks.
|
|
1132
|
+
*/
|
|
1133
|
+
auth: AuthService;
|
|
574
1134
|
/** Legacy aliases for compatibility */
|
|
575
1135
|
env: {
|
|
576
1136
|
KV: KvStore;
|
|
@@ -580,10 +1140,15 @@ export interface Platform {
|
|
|
580
1140
|
}
|
|
581
1141
|
|
|
582
1142
|
/**
|
|
583
|
-
*
|
|
1143
|
+
* The ambient `platform` global is declared in `./global.d.ts` (kept
|
|
1144
|
+
* separate so that *type-only* imports of these interfaces — e.g. from
|
|
1145
|
+
* `@maravilla-labs/platform`, which has its own `getPlatform()` global —
|
|
1146
|
+
* do not pull a conflicting ambient `const platform` into scope). Tooling
|
|
1147
|
+
* and app code that want the global reference it explicitly:
|
|
1148
|
+
*
|
|
1149
|
+
* ```ts
|
|
1150
|
+
* /// <reference types="@maravilla-labs/types/global" />
|
|
1151
|
+
* ```
|
|
584
1152
|
*/
|
|
585
|
-
declare global {
|
|
586
|
-
const platform: Platform;
|
|
587
|
-
}
|
|
588
1153
|
|
|
589
1154
|
export { Platform as default };
|