@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/index.d.ts CHANGED
@@ -149,12 +149,32 @@ export interface DbFindOptions {
149
149
  /** Hybrid metadata + vector search clause. */
150
150
  vector?: VectorQuery;
151
151
  }
152
+ /**
153
+ * A document as it is returned by the runtime. Every row the platform
154
+ * stores carries a string `id` (mirrored as `_id`) injected at insert
155
+ * time, plus optional server-managed timestamps. `find`/`findOne` return
156
+ * this shape; the generic `T` is your own document fields.
157
+ *
158
+ * This is a **type-level** contract — the runtime already injects/returns
159
+ * `id`+`_id` on both the dev (SQLite) and production (Mongo) paths, so no
160
+ * normalization happens here.
161
+ */
162
+ export type DbDocument<T = Record<string, unknown>> = T & {
163
+ /** Stable string row id, injected by the runtime on insert. */
164
+ id: string;
165
+ /** Mirror of `id` for MongoDB-style call sites. Always equals `id`. */
166
+ _id: string;
167
+ /** Server-managed creation timestamp, when present. */
168
+ _created_at?: string;
169
+ /** Server-managed update timestamp, when present. */
170
+ _updated_at?: string;
171
+ };
152
172
  /**
153
173
  * Database collection interface
154
174
  */
155
175
  export interface DbCollection {
156
- find(filter?: Record<string, any>, options?: DbFindOptions): Promise<any[]>;
157
- findOne(filter?: Record<string, any>): Promise<any | null>;
176
+ find<T = Record<string, unknown>>(filter?: Record<string, any>, options?: DbFindOptions): Promise<DbDocument<T>[]>;
177
+ findOne<T = Record<string, unknown>>(filter?: Record<string, any>): Promise<DbDocument<T> | null>;
158
178
  insertOne(document: Record<string, any>): Promise<string>;
159
179
  updateOne(filter: Record<string, any>, update: Record<string, any>): Promise<void>;
160
180
  deleteOne(filter: Record<string, any>): Promise<void>;
@@ -274,6 +294,316 @@ export interface Storage {
274
294
  */
275
295
  getMetadata(key: string): Promise<StorageMetadata>;
276
296
  }
297
+ /**
298
+ * Authenticated user record from the platform auth service.
299
+ */
300
+ export interface AuthUser {
301
+ /** Unique user ID (prefixed with "usr_") */
302
+ id: string;
303
+ /** User's email address */
304
+ email: string;
305
+ /** Whether the email has been verified */
306
+ email_verified: boolean;
307
+ /** Account status */
308
+ status: 'active' | 'suspended' | 'deactivated';
309
+ /** Authentication provider ("email", "google", "github", "managed", etc.) */
310
+ provider: string;
311
+ /** Group IDs the user belongs to */
312
+ groups: string[];
313
+ /**
314
+ * Caller-supplied external identifier used for idempotent
315
+ * managed-user creation (FR-3). `null`/absent for normal accounts.
316
+ */
317
+ external_id?: string | null;
318
+ /** Unix timestamp when the user was created */
319
+ created_at: number;
320
+ /** Unix timestamp when the user was last updated */
321
+ updated_at: number;
322
+ /** Unix timestamp of last login (if any) */
323
+ last_login_at?: number;
324
+ }
325
+ /**
326
+ * Snapshot of whoever is currently bound to the request as the caller.
327
+ * This is exactly what per-resource policies see as `auth.*`.
328
+ */
329
+ export interface AuthCaller {
330
+ /** Caller's user id, or `""` if anonymous */
331
+ user_id: string;
332
+ /** Caller's email, or `""` if anonymous */
333
+ email: string;
334
+ /** Admin flag from the session */
335
+ is_admin: boolean;
336
+ /** Role names (project-scoped) */
337
+ roles: string[];
338
+ /** `true` when no identity is bound to this request */
339
+ is_anonymous: boolean;
340
+ }
341
+ /**
342
+ * Session returned after successful login or token refresh.
343
+ */
344
+ export interface AuthSession {
345
+ /** Short-lived JWT access token (default 15 min) */
346
+ access_token: string;
347
+ /** Single-use opaque refresh token (default 30 days) */
348
+ refresh_token: string;
349
+ /** Access token lifetime in seconds */
350
+ expires_in: number;
351
+ /** The authenticated user */
352
+ user: AuthUser;
353
+ }
354
+ /**
355
+ * Custom registration field defined in project auth settings.
356
+ */
357
+ export interface AuthField {
358
+ /** Field key (used as form field name) */
359
+ key: string;
360
+ /** Display label */
361
+ label: string;
362
+ /** Field type: text, email, phone, date, number, select, boolean, url, textarea */
363
+ field_type: string;
364
+ /** Whether the field is required */
365
+ required: boolean;
366
+ /** Whether the field appears on the registration form */
367
+ show_on_register: boolean;
368
+ }
369
+ /**
370
+ * Options for registering a new user.
371
+ */
372
+ export interface RegisterOptions {
373
+ /** User's email address */
374
+ email: string;
375
+ /** Password (minimum 8 characters) */
376
+ password: string;
377
+ /** Optional profile data (custom fields) */
378
+ profile?: Record<string, any>;
379
+ /**
380
+ * Caller-supplied external id. When set, registration is idempotent on
381
+ * `(tenant, external_id)` — re-registering with the same key returns the
382
+ * existing user instead of erroring (FR-3).
383
+ */
384
+ external_id?: string;
385
+ }
386
+ /**
387
+ * Options for logging in.
388
+ */
389
+ export interface LoginOptions {
390
+ /** User's email address */
391
+ email: string;
392
+ /** User's password */
393
+ password: string;
394
+ }
395
+ /**
396
+ * Options for creating a managed (no-login) user — e.g. an imported
397
+ * contact or service-owned record that authenticates out-of-band (FR-2).
398
+ * Created with no password; sessions are only minted when the account is
399
+ * later activated.
400
+ */
401
+ export interface CreateManagedUserOptions {
402
+ /** Optional email. A synthetic no-login address is generated when omitted. */
403
+ email?: string;
404
+ /** Optional profile data. */
405
+ profile?: Record<string, any>;
406
+ /** Optional external id for idempotent create-by-key (FR-3). */
407
+ external_id?: string;
408
+ /** Optional group ids/slugs to add the user to on creation. */
409
+ groups?: string[];
410
+ }
411
+ /**
412
+ * Filter options for listing users.
413
+ */
414
+ export interface UserListFilter {
415
+ /** Max results per page (default 50) */
416
+ limit?: number;
417
+ /** Number of results to skip */
418
+ offset?: number;
419
+ /** Filter by account status */
420
+ status?: 'active' | 'suspended' | 'deactivated';
421
+ /** Filter by email (partial match) */
422
+ email_contains?: string;
423
+ /** Filter by group ID */
424
+ group_id?: string;
425
+ }
426
+ /**
427
+ * Paginated user list response.
428
+ */
429
+ export interface UserListResponse {
430
+ /** Users in this page */
431
+ users: AuthUser[];
432
+ /** Total number of matching users */
433
+ total: number;
434
+ /** Page size */
435
+ limit: number;
436
+ /** Offset */
437
+ offset: number;
438
+ }
439
+ /**
440
+ * Options for updating a user.
441
+ */
442
+ export interface UpdateUserOptions {
443
+ /** New email address */
444
+ email?: string;
445
+ /** New status */
446
+ status?: 'active' | 'suspended' | 'deactivated';
447
+ /** Profile data to merge */
448
+ profile?: Record<string, any>;
449
+ }
450
+ export interface AuthGroup {
451
+ id: string;
452
+ name: string;
453
+ description: string | null;
454
+ permissions: string[];
455
+ member_count: number;
456
+ created_at: number;
457
+ updated_at: number;
458
+ }
459
+ export interface CreateGroupOptions {
460
+ name: string;
461
+ description?: string;
462
+ permissions?: string[];
463
+ }
464
+ export interface UpdateGroupOptions {
465
+ name?: string;
466
+ description?: string;
467
+ permissions?: string[];
468
+ }
469
+ export interface GroupPermission {
470
+ resource_name: string;
471
+ actions: string[];
472
+ }
473
+ export interface AuthCircle {
474
+ id: string;
475
+ name: string;
476
+ metadata: Record<string, any> | null;
477
+ member_count: number;
478
+ created_at: number;
479
+ updated_at: number;
480
+ }
481
+ export interface CreateCircleOptions {
482
+ name: string;
483
+ metadata?: Record<string, any>;
484
+ }
485
+ export interface UpdateCircleOptions {
486
+ name?: string;
487
+ metadata?: Record<string, any>;
488
+ }
489
+ export interface AddCircleMemberOptions {
490
+ user_id: string;
491
+ relationship: string;
492
+ is_primary_contact?: boolean;
493
+ }
494
+ /** Circle membership entry */
495
+ export interface CircleMembership {
496
+ user_id: string;
497
+ email: string;
498
+ relationship: string;
499
+ is_primary_contact: boolean;
500
+ joined_at: number;
501
+ }
502
+ export type ResourceServiceType = 'kv' | 'database' | 'realtime' | 'media' | 'vector' | 'storage' | 'queue' | 'push' | 'workflow' | 'transforms';
503
+ /** A platform resource definition. */
504
+ export interface Resource {
505
+ id: string;
506
+ resource_name: string;
507
+ title: string;
508
+ description: string | null;
509
+ actions: string[];
510
+ policy: string | null;
511
+ service_type: ResourceServiceType | null;
512
+ read_filter: string | null;
513
+ created_at: number;
514
+ updated_at: number;
515
+ }
516
+ export interface CreateResourceOptions {
517
+ resource_name: string;
518
+ title: string;
519
+ description?: string;
520
+ actions?: string[];
521
+ policy?: string;
522
+ service_type?: ResourceServiceType;
523
+ read_filter?: string;
524
+ }
525
+ export interface UpdateResourceOptions {
526
+ title?: string;
527
+ description?: string;
528
+ actions?: string[];
529
+ policy?: string;
530
+ service_type?: ResourceServiceType;
531
+ read_filter?: string;
532
+ }
533
+ export interface RelationType {
534
+ id: string;
535
+ relation_name: string;
536
+ title: string;
537
+ description: string | null;
538
+ category: string;
539
+ icon: string | null;
540
+ color: string | null;
541
+ inverse_relation_id: string | null;
542
+ implies_stewardship: boolean;
543
+ requires_minor: boolean;
544
+ bidirectional: boolean;
545
+ is_system: boolean;
546
+ created_at: number;
547
+ updated_at: number;
548
+ }
549
+ export interface CreateRelationTypeOptions {
550
+ relation_name: string;
551
+ title: string;
552
+ description?: string;
553
+ category?: string;
554
+ icon?: string;
555
+ color?: string;
556
+ inverse_relation_id?: string;
557
+ implies_stewardship?: boolean;
558
+ requires_minor?: boolean;
559
+ bidirectional?: boolean;
560
+ is_system?: boolean;
561
+ }
562
+ export interface UpdateRelationTypeOptions {
563
+ title?: string;
564
+ description?: string;
565
+ category?: string;
566
+ icon?: string;
567
+ color?: string;
568
+ inverse_relation_id?: string;
569
+ implies_stewardship?: boolean;
570
+ requires_minor?: boolean;
571
+ bidirectional?: boolean;
572
+ }
573
+ /** A single typed relation edge between two users (FR-1). */
574
+ export interface Relation {
575
+ id: string;
576
+ from_user_id: string;
577
+ to_user_id: string;
578
+ /** The relation type's name (e.g. `STEWARDS`). */
579
+ relation_type: string;
580
+ /** The relation type's id (`rlt_…`). */
581
+ relation_type_id: string;
582
+ metadata?: Record<string, any> | null;
583
+ created_at: number;
584
+ }
585
+ /** Options for adding a relation edge. */
586
+ export interface AddRelationOptions {
587
+ from_user_id: string;
588
+ to_user_id: string;
589
+ /** Either a relation-type id (`rlt_…`) or a relation name. */
590
+ relation_type: string;
591
+ metadata?: Record<string, any>;
592
+ }
593
+ /** Direction filter when listing a user's relations. */
594
+ export type RelationListDirection = 'outgoing' | 'incoming' | 'both';
595
+ /** Options for listing relation edges touching a user. */
596
+ export interface ListRelationsOptions {
597
+ user_id: string;
598
+ direction?: RelationListDirection;
599
+ }
600
+ export interface AuthConfig {
601
+ fields: AuthField[];
602
+ oauth_providers: any[];
603
+ branding: Record<string, any>;
604
+ password_policy: Record<string, any>;
605
+ session_config: Record<string, any>;
606
+ }
277
607
  /** Delegation mode for stewardship overrides */
278
608
  export type DelegationMode = 'full' | 'scoped';
279
609
  /** Status of a stewardship override */
@@ -292,18 +622,18 @@ export interface StewardshipOverride {
292
622
  ward_id: string;
293
623
  delegation_mode: DelegationMode;
294
624
  scoped_permissions: ScopedPermission[];
295
- valid_from?: number;
296
- valid_until?: number;
625
+ valid_from: number | null;
626
+ valid_until: number | null;
297
627
  status: StewardshipStatus;
298
- reason?: string;
628
+ reason: string | null;
299
629
  source: string;
300
- source_circle_id?: string;
301
- source_relation_type_id?: string;
630
+ source_circle_id: string | null;
631
+ source_relation_type_id: string | null;
302
632
  created_at: number;
303
633
  updated_at: number;
304
634
  }
305
635
  /** Options for creating a stewardship override */
306
- export interface CreateStewardshipOverrideRequest {
636
+ export interface CreateStewardshipOverrideOptions {
307
637
  steward_id: string;
308
638
  ward_id: string;
309
639
  delegation_mode?: DelegationMode;
@@ -334,57 +664,224 @@ export interface StewardshipAuditEntry {
334
664
  performed_by: string;
335
665
  on_behalf_of: string;
336
666
  action: string;
337
- resource?: string;
338
- details?: Record<string, any>;
667
+ resource: string | null;
668
+ details: Record<string, any> | null;
339
669
  created_at: number;
340
670
  }
341
- /** Options for listing audit log entries */
342
- export interface AuditListOptions {
343
- limit?: number;
344
- offset?: number;
345
- }
346
- /** Stewardship service interface */
347
- export interface Stewardship {
671
+ /**
672
+ * Sub-namespace exposed at `platform.auth.stewardship` mirroring the
673
+ * runtime's `globalThis.platform.auth.stewardship.*` surface.
674
+ */
675
+ export interface AuthStewardshipApi {
348
676
  resolve(userId: string): Promise<StewardshipResolution>;
349
- createOverride(options: CreateStewardshipOverrideRequest): Promise<StewardshipOverride>;
677
+ createOverride(opts: CreateStewardshipOverrideOptions): Promise<StewardshipOverride>;
350
678
  revoke(id: string): Promise<void>;
351
679
  checkPermission(stewardId: string, wardId: string, resource: string, action: string): Promise<boolean>;
352
680
  createActAs(stewardId: string, wardId: string): Promise<ActAsContext>;
353
- listAudit(userId: string, options?: AuditListOptions): Promise<StewardshipAuditEntry[]>;
354
- }
355
- /** A platform resource definition */
356
- export interface Resource {
357
- id: string;
358
- resource_name: string;
359
- title: string;
360
- description?: string;
361
- actions: string[];
362
- created_at: number;
363
- updated_at: number;
364
- }
365
- /** Options for creating a resource */
366
- export interface CreateResourceRequest {
367
- resource_name: string;
368
- title: string;
369
- description?: string;
370
- actions?: string[];
681
+ listAudit(userId: string, options?: {
682
+ limit?: number;
683
+ offset?: number;
684
+ }): Promise<StewardshipAuditEntry[]>;
685
+ }
686
+ /** Result of an `explain()` policy check. */
687
+ export interface PolicyExplain {
688
+ allowed: boolean;
689
+ reason?: string;
690
+ /** The specific clause that caused a denial, when the engine can isolate it. */
691
+ failedClause?: string;
371
692
  }
372
- /** Circle membership entry */
373
- export interface CircleMembership {
374
- user_id: string;
375
- email: string;
376
- relationship: string;
377
- is_primary_contact: boolean;
378
- joined_at: number;
693
+ /** A single authorization check for batched `canMany()`. */
694
+ export interface CanCheck {
695
+ action: string;
696
+ resourceId: string;
697
+ node?: Record<string, unknown> | null;
379
698
  }
380
- /** A circle */
381
- export interface Circle {
382
- id: string;
383
- name: string;
384
- metadata?: Record<string, any>;
385
- member_count: number;
386
- created_at: number;
387
- updated_at: number;
699
+ /**
700
+ * Auth service for end-user authentication and user management.
701
+ * Implemented by the in-runtime native bridge and by the dev-server
702
+ * remote client (`RemoteAuthService`).
703
+ */
704
+ export interface AuthService {
705
+ /**
706
+ * Register a new user with email and password.
707
+ * @returns The created user (not yet email-verified)
708
+ */
709
+ register(options: RegisterOptions): Promise<AuthUser>;
710
+ /**
711
+ * Authenticate a user and create a session.
712
+ * @returns Session with access token, refresh token, and user info
713
+ */
714
+ login(options: LoginOptions): Promise<AuthSession>;
715
+ /**
716
+ * Validate an access token and return the authenticated user.
717
+ * @throws If the token is invalid or expired
718
+ */
719
+ validate(accessToken: string): Promise<AuthUser>;
720
+ /**
721
+ * Refresh a session using a refresh token (single-use).
722
+ * @returns New session with fresh access and refresh tokens
723
+ */
724
+ refresh(refreshToken: string): Promise<AuthSession>;
725
+ /** Revoke a specific session. */
726
+ logout(sessionId: string): Promise<void>;
727
+ /**
728
+ * Get a user by ID.
729
+ * @returns The user, or null if not found
730
+ */
731
+ getUser(userId: string): Promise<AuthUser | null>;
732
+ /** List users with optional filtering and pagination. */
733
+ listUsers(filter?: UserListFilter): Promise<UserListResponse>;
734
+ /** Update a user's email, status, or profile data. */
735
+ updateUser(userId: string, update: UpdateUserOptions): Promise<AuthUser>;
736
+ /** Delete a user and all their sessions. */
737
+ deleteUser(userId: string): Promise<void>;
738
+ /**
739
+ * Create a managed (no-login) user (FR-2). Idempotent on `external_id`
740
+ * when supplied (FR-3).
741
+ */
742
+ createManagedUser(options: CreateManagedUserOptions): Promise<AuthUser>;
743
+ /**
744
+ * Create an email verification token.
745
+ * @returns The verification token (caller decides how to deliver it)
746
+ */
747
+ sendVerification(userId: string): Promise<{
748
+ token: string;
749
+ }>;
750
+ /** Verify an email address using a verification token. */
751
+ verifyEmail(token: string): Promise<void>;
752
+ /**
753
+ * Create a password reset token for an email address.
754
+ * @returns The reset token (caller decides how to deliver it)
755
+ */
756
+ sendPasswordReset(email: string): Promise<{
757
+ token: string;
758
+ }>;
759
+ /** Reset a password using a reset token. */
760
+ resetPassword(token: string, newPassword: string): Promise<void>;
761
+ /** Change a user's password (requires old password). */
762
+ changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void>;
763
+ /** Get the configured registration fields for this project. */
764
+ getFieldConfig(): Promise<{
765
+ fields: AuthField[];
766
+ }>;
767
+ /**
768
+ * Start an OAuth flow by generating an authorization URL.
769
+ * @param provider - "google", "github", "okta", or "custom_oidc"
770
+ */
771
+ getOAuthUrl(provider: string, options?: {
772
+ redirectUri?: string;
773
+ }): Promise<{
774
+ auth_url: string;
775
+ state: string;
776
+ }>;
777
+ /** Complete an OAuth flow by exchanging the authorization code. */
778
+ handleOAuthCallback(provider: string, params: {
779
+ code: string;
780
+ state: string;
781
+ }): Promise<AuthSession | {
782
+ type: 'LinkRequired';
783
+ email: string;
784
+ provider: string;
785
+ provider_id: string;
786
+ existing_user_id: string;
787
+ }>;
788
+ /**
789
+ * Middleware helper that validates auth and injects `request.user`.
790
+ * Returns 401 JSON response if no valid token is found.
791
+ */
792
+ withAuth<T extends (request: Request & {
793
+ user: AuthUser;
794
+ }) => Promise<Response>>(handler: T): (request: Request) => Promise<Response>;
795
+ createGroup(options: CreateGroupOptions): Promise<AuthGroup>;
796
+ listGroups(): Promise<AuthGroup[]>;
797
+ /** @param groupId - the group id **or slug** (resolved server-side). */
798
+ getGroup(groupId: string): Promise<AuthGroup | null>;
799
+ /**
800
+ * Look up a group by its declarative name (the same name used in
801
+ * `maravilla.config.ts`'s `groups: [...]` block). Returns null if the
802
+ * auth-settings reconciler hasn't created it yet.
803
+ */
804
+ getGroupByName(name: string): Promise<AuthGroup | null>;
805
+ /** @param groupId - the group id **or slug** (resolved server-side). */
806
+ updateGroup(groupId: string, options: UpdateGroupOptions): Promise<AuthGroup>;
807
+ /** @param groupId - the group id **or slug** (resolved server-side). */
808
+ deleteGroup(groupId: string): Promise<void>;
809
+ /** @param groupId - the group id **or slug** (resolved server-side). */
810
+ addUserToGroup(userId: string, groupId: string): Promise<void>;
811
+ /** @param groupId - the group id **or slug** (resolved server-side). */
812
+ removeUserFromGroup(userId: string, groupId: string): Promise<void>;
813
+ getUserGroups(userId: string): Promise<AuthGroup[]>;
814
+ /** @param groupId - the group id **or slug** (resolved server-side). */
815
+ getGroupMembers(groupId: string): Promise<AuthUser[]>;
816
+ /** @param groupId - the group id **or slug** (resolved server-side). */
817
+ getGroupPermissions(groupId: string): Promise<GroupPermission[]>;
818
+ /** @param groupId - the group id **or slug** (resolved server-side). */
819
+ setGroupPermissions(groupId: string, permissions: GroupPermission[]): Promise<void>;
820
+ createCircle(options: CreateCircleOptions): Promise<AuthCircle>;
821
+ listCircles(): Promise<AuthCircle[]>;
822
+ /** @param circleId - the circle id **or slug** (resolved server-side). */
823
+ getCircle(circleId: string): Promise<AuthCircle | null>;
824
+ /** @param circleId - the circle id **or slug** (resolved server-side). */
825
+ updateCircle(circleId: string, options: UpdateCircleOptions): Promise<AuthCircle>;
826
+ /** @param circleId - the circle id **or slug** (resolved server-side). */
827
+ deleteCircle(circleId: string): Promise<void>;
828
+ /** @param circleId - the circle id **or slug** (resolved server-side). */
829
+ addCircleMember(circleId: string, options: AddCircleMemberOptions): Promise<void>;
830
+ /** @param circleId - the circle id **or slug** (resolved server-side). */
831
+ removeCircleMember(circleId: string, userId: string): Promise<void>;
832
+ /** @param circleId - the circle id **or slug** (resolved server-side). */
833
+ getCircleMembers(circleId: string): Promise<CircleMembership[]>;
834
+ getUserCircles(userId: string): Promise<AuthCircle[]>;
835
+ createResource(options: CreateResourceOptions): Promise<Resource>;
836
+ listResources(): Promise<Resource[]>;
837
+ updateResource(resourceId: string, options: UpdateResourceOptions): Promise<Resource>;
838
+ deleteResource(resourceId: string): Promise<void>;
839
+ createRelationType(options: CreateRelationTypeOptions): Promise<RelationType>;
840
+ listRelationTypes(): Promise<RelationType[]>;
841
+ /** @param id - the relation-type id **or slug** (resolved server-side). */
842
+ updateRelationType(id: string, options: UpdateRelationTypeOptions): Promise<RelationType>;
843
+ /** @param id - the relation-type id **or slug** (resolved server-side). */
844
+ deleteRelationType(id: string): Promise<void>;
845
+ /** Add a directed relation edge between two users. */
846
+ addRelation(options: AddRelationOptions): Promise<Relation>;
847
+ /** Remove a relation edge by its endpoints and relation-type id. */
848
+ removeRelation(fromUserId: string, toUserId: string, relationTypeId: string): Promise<void>;
849
+ /** List relation edges touching a user, optionally filtered by direction. */
850
+ listRelations(options: ListRelationsOptions): Promise<Relation[]>;
851
+ getProfile(userId: string): Promise<Record<string, any>>;
852
+ setProfile(userId: string, data: Record<string, any>): Promise<void>;
853
+ getAuthConfig(): Promise<AuthConfig>;
854
+ setAuthConfig(config: AuthConfig): Promise<void>;
855
+ readonly stewardship: AuthStewardshipApi;
856
+ /**
857
+ * Explicitly bind the caller for the remainder of this request.
858
+ * Pass a JWT to validate + bind, or `null` / `""` to clear.
859
+ */
860
+ setCurrentUser(token: string | null): Promise<void>;
861
+ /**
862
+ * Snapshot of the currently bound caller. Returns an anonymous caller
863
+ * (`is_anonymous: true`) when no identity has been bound.
864
+ */
865
+ getCurrentUser(): AuthCaller;
866
+ /**
867
+ * Ask the policy engine whether the bound caller would be allowed to
868
+ * perform `action` on `resourceId`. Returns a boolean — never throws on
869
+ * denial. Fails closed when no caller is bound.
870
+ */
871
+ can(action: string, resourceId: string, node?: Record<string, unknown> | null): Promise<boolean>;
872
+ /**
873
+ * Like {@link can}, but returns *why* (FR-7). Fails closed with
874
+ * `{ allowed: false, reason: 'no bound user' }` when no caller is bound.
875
+ */
876
+ explain(action: string, resourceId: string, node?: Record<string, unknown> | null): Promise<PolicyExplain>;
877
+ /**
878
+ * Batch authorization check (FR-7). Results are returned in the same
879
+ * order as `checks`. Fails closed (all `{ allowed: false }`) when no
880
+ * caller is bound.
881
+ */
882
+ canMany(checks: CanCheck[]): Promise<{
883
+ allowed: boolean;
884
+ }[]>;
388
885
  }
389
886
  /** State of a workflow run. */
390
887
  export type WorkflowRunStatus = 'queued' | 'running' | 'sleeping' | 'waiting_event' | 'completed' | 'failed' | 'cancelled';
@@ -460,48 +957,13 @@ export interface Platform {
460
957
  storage: Storage;
461
958
  /** Durable multi-step workflows */
462
959
  workflows: Workflows;
463
- /** Auth service with stewardship, resources, and circle helpers */
464
- auth: {
465
- register(options: {
466
- email: string;
467
- password: string;
468
- profile?: Record<string, any>;
469
- }): Promise<any>;
470
- login(options: {
471
- email: string;
472
- password: string;
473
- }): Promise<any>;
474
- validate(accessToken: string): Promise<any>;
475
- refresh(refreshToken: string): Promise<any>;
476
- logout(sessionId: string): Promise<void>;
477
- getUser(userId: string): Promise<any>;
478
- listUsers(filter?: Record<string, any>): Promise<any>;
479
- updateUser(userId: string, update: Record<string, any>): Promise<any>;
480
- deleteUser(userId: string): Promise<void>;
481
- sendVerification(userId: string): Promise<any>;
482
- verifyEmail(token: string): Promise<void>;
483
- sendPasswordReset(email: string): Promise<any>;
484
- resetPassword(token: string, newPassword: string): Promise<void>;
485
- changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void>;
486
- getFieldConfig(): Promise<any>;
487
- getOAuthUrl(provider: string, options?: {
488
- redirectUri?: string;
489
- }): Promise<any>;
490
- handleOAuthCallback(provider: string, params: {
491
- code: string;
492
- state: string;
493
- }): Promise<any>;
494
- /** Stewardship (guardian/ward delegation) */
495
- stewardship: Stewardship;
496
- /** List available resources */
497
- listResources(): Promise<Resource[]>;
498
- /** Create a resource definition */
499
- createResource(options: CreateResourceRequest): Promise<Resource>;
500
- /** Get circle members */
501
- getCircleMembers(circleId: string): Promise<CircleMembership[]>;
502
- /** Get circles a user belongs to */
503
- getUserCircles(userId: string): Promise<Circle[]>;
504
- };
960
+ /**
961
+ * Auth service — the full canonical {@link AuthService} surface:
962
+ * authentication, user management, groups, circles, resources, relation
963
+ * types, typed relation edges, stewardship, profile/config, and the
964
+ * request-scoped `can`/`explain`/`canMany` authorization checks.
965
+ */
966
+ auth: AuthService;
505
967
  /** Legacy aliases for compatibility */
506
968
  env: {
507
969
  KV: KvStore;
@@ -510,9 +972,14 @@ export interface Platform {
510
972
  };
511
973
  }
512
974
  /**
513
- * Global platform object available in runtime
975
+ * The ambient `platform` global is declared in `./global.d.ts` (kept
976
+ * separate so that *type-only* imports of these interfaces — e.g. from
977
+ * `@maravilla-labs/platform`, which has its own `getPlatform()` global —
978
+ * do not pull a conflicting ambient `const platform` into scope). Tooling
979
+ * and app code that want the global reference it explicitly:
980
+ *
981
+ * ```ts
982
+ * /// <reference types="@maravilla-labs/types/global" />
983
+ * ```
514
984
  */
515
- declare global {
516
- const platform: Platform;
517
- }
518
985
  export { Platform as default };