skapi-js 2.0.3 → 2.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/dist/skapi.d.mts CHANGED
@@ -572,7 +572,9 @@ type RequestHistory = {
572
572
  compact?: boolean;
573
573
  poll?: (arg?: {
574
574
  latency?: number;
575
- onResponse?: (res: any) => void;
575
+ onResponse?: (res: any, meta?: {
576
+ executed?: number;
577
+ }) => void;
576
578
  onError?: (err: any) => void;
577
579
  onStream?: (chunk: string, seq: number) => void;
578
580
  }) => Promise<any>;
@@ -658,6 +660,243 @@ type Subscription = {
658
660
  get_notified: boolean;
659
661
  get_email: boolean;
660
662
  };
663
+ /** Comparison operator of a ticket condition row. The word forms are normalized to the symbols on registration. For a string value, '>=' means "starts with". */
664
+ type TicketConditionOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
665
+ /**
666
+ * One row of a ticket condition list (`headers`, `data`, `params`, `user`, `match`).
667
+ * Rows with the same key are alternatives (any one matching satisfies the key), rows with
668
+ * different keys must all match. A comparison between incompatible types is a mismatch.
669
+ */
670
+ type TicketConditionRow = {
671
+ /**
672
+ * `data` and `params` rows: a path into the request, such as "data[object][id]" (the leading
673
+ * "data" there is the request's own key, not the row list). `headers` rows: the header name,
674
+ * matched case-insensitively. `user` rows: the consumer attribute name. Never templated.
675
+ */
676
+ key: string;
677
+ /** Absent, with no `value`, on a capture-only row: it never fails and only fills `placeholder`. */
678
+ operator?: TicketConditionOperator;
679
+ /** A literal, never templated. A list passes when any member matches. */
680
+ value?: any;
681
+ /** `data` and `params` rows only. When the row matches, the value at `key` in the request data is replaced by this before anything else reads it. */
682
+ setValueWhenMatch?: any;
683
+ /** `data` and `params` rows only. Remembers the value at `key` under this name for the actions ("placeholder[NAME]"). Must match ^[A-Za-z_][A-Za-z0-9_]*$. */
684
+ placeholder?: string;
685
+ };
686
+ /** An HTTP call whose response must match. `url`, `headers`, `data` and `params` are templated like an action's `exe`; `match` rows are not. */
687
+ type TicketRequestCondition = {
688
+ /** http:// or https:// with a hostname. No IP literal, no userinfo, never under the api domain. */
689
+ url: string;
690
+ method?: 'GET' | 'POST';
691
+ headers?: {
692
+ [name: string]: string;
693
+ };
694
+ /** Sent as JSON when a content-type header says application/json, else form encoded. */
695
+ data?: any;
696
+ params?: {
697
+ [key: string]: any;
698
+ };
699
+ /** Rows matched against the response body. */
700
+ match?: TicketConditionRow[];
701
+ };
702
+ /**
703
+ * An HMAC signature over the request, computed with a secret shared with the sender. Verified
704
+ * before anything else runs: the `timestamp` (when set) must be an integer within `tolerance` of
705
+ * now, the bytes described by `signed` are signed with the stored secret (`secret_prefix` removed,
706
+ * then decoded per `secret_encoding`), and the result is compared in constant time against every
707
+ * `${signature}` captured from the header. Any failure is a plain mismatch.
708
+ *
709
+ * Templates (`signed`, `timestamp`) are literal text with these tokens: `${body}` (the raw request
710
+ * body exactly as received), `${method}` (the HTTP method, upper case), `${header:Name}` (a request
711
+ * header, case-insensitive; an absent header fails verification) and any capture from `parts` other
712
+ * than `${signature}`. An unknown token is refused at registration.
713
+ *
714
+ * Public-key signature schemes (RSA, ECDSA, Ed25519) are not supported.
715
+ */
716
+ type TicketSignatureCondition = {
717
+ /** The name of a Secret Key of the project, never the secret itself. Must exist at registration. */
718
+ secret: string;
719
+ /** The request header carrying the signature. Case-insensitive. Up to 256 characters. */
720
+ header: string;
721
+ /** Default 'sha256'. */
722
+ algorithm?: 'sha256' | 'sha1' | 'sha512';
723
+ /** How the signature value in the header is encoded. Default 'hex'. */
724
+ encoding?: 'hex' | 'base64';
725
+ /** Splits the header value into items, each trimmed. 1 to 8 characters. Absent: the whole header value is one item. */
726
+ separator?: string;
727
+ /**
728
+ * Patterns matched against each header item, in order; the first whose literal text fits
729
+ * captures the rest. Each pattern is literal text with at most one `${name}` capture (letters,
730
+ * digits and _; not `body` or `method`). `${signature}` may be captured by several items (any one matching passes) and
731
+ * must appear in at least one pattern; any other name becomes a token for the templates (the
732
+ * first capture wins). Items that match no pattern are ignored. Up to 10 patterns of up to 256
733
+ * characters each. Default ["${signature}"].
734
+ */
735
+ parts?: string[];
736
+ /** Template of the signed bytes. Up to 512 characters. Default "${body}". */
737
+ signed?: string;
738
+ /** Template resolving to unix time, in seconds or in milliseconds (a value above 10^12). Up to 512 characters. Absent: no timestamp check. */
739
+ timestamp?: string;
740
+ /** Seconds the timestamp may be away from now, 1 to 86400. Only used with `timestamp`. Default 300. */
741
+ tolerance?: number;
742
+ /** How the stored secret becomes the HMAC key bytes. Default 'raw'. */
743
+ secret_encoding?: 'raw' | 'base64' | 'hex';
744
+ /** Removed from the start of the stored secret before decoding. Up to 64 characters. */
745
+ secret_prefix?: string;
746
+ };
747
+ /** What a consumption request must look like before the ticket's actions run. Evaluated in the order the keys are listed here; the first failure is the reported one. */
748
+ type TicketCondition = {
749
+ /** Answer HTTP 200 even when the consumption fails. For webhooks that retry on errors. */
750
+ return200?: boolean;
751
+ /** Absent = both allowed. */
752
+ method?: 'GET' | 'POST';
753
+ /** Verified first, over the raw request body. See TicketSignatureCondition. */
754
+ signature?: TicketSignatureCondition;
755
+ ip?: {
756
+ operator: TicketConditionOperator;
757
+ value: string | string[];
758
+ };
759
+ user_agent?: {
760
+ operator: TicketConditionOperator;
761
+ value: string | string[];
762
+ };
763
+ headers?: TicketConditionRow[];
764
+ /** Rows against the POST body. On a GET the body root is {}. */
765
+ data?: TicketConditionRow[];
766
+ /** Rows against the GET query string. On a POST the query root is {}. */
767
+ params?: TicketConditionRow[];
768
+ /** Rows against the consumer's attributes (user_id, email, access_group, ...). Signed-in consumption only. */
769
+ user?: TicketConditionRow[];
770
+ /** Record ID the consumer must own or have been granted. Signed-in consumption only. */
771
+ record_access?: string;
772
+ request?: TicketRequestCondition;
773
+ };
774
+ /** The subset of a condition a `req` action can evaluate against its response. A response has no method, query string or status policy. */
775
+ type TicketResponseCondition = Pick<TicketCondition, 'headers' | 'data' | 'user' | 'record_access' | 'request'>;
776
+ /**
777
+ * One step of a ticket's action chain. Actions run in order; each one's result is readable by
778
+ * the next as "result[...]". `exe` is templated right before the action runs: a string that is
779
+ * a whole path ("data[object][id]", "placeholder[NAME]") keeps the value's type, "${...}" inside
780
+ * text becomes a string, bare words are literal. When an action fails its `err` chain runs
781
+ * (with "error[code]", "error[message]", ...) and the consumption stops. Nothing is rolled back.
782
+ */
783
+ type TicketAction = {
784
+ /** Update a Skapi service. Internal: registration refuses it unless the caller is a Skapi super master. */
785
+ act: 'srvc';
786
+ exe: {
787
+ [key: string]: any;
788
+ };
789
+ err?: TicketAction[];
790
+ } | {
791
+ /** Set the access group of a user. */
792
+ act: 'acsg';
793
+ exe: {
794
+ /** 1 ~ 99, or "admin". */
795
+ group: number | 'admin';
796
+ /** Blank = the consumer (signed-in consumption only). The project owner cannot be a target. */
797
+ user_id?: string;
798
+ };
799
+ err?: TicketAction[];
800
+ } | {
801
+ /** Grant private access to a record. */
802
+ act: 'acsr';
803
+ exe: {
804
+ /** A record ID, not a unique ID. */
805
+ record_id: string;
806
+ /** Blank = the consumer. */
807
+ user_id?: string | string[];
808
+ };
809
+ err?: TicketAction[];
810
+ } | {
811
+ /** Post a record. Everything but `user_id` is the payload postRecord() sends, so the same rules apply. A `unique_id` makes a retried webhook update the same record instead of adding one. */
812
+ act: 'pstr';
813
+ exe: {
814
+ table: string | {
815
+ name: string;
816
+ access_group?: number | 'public' | 'authorized' | 'admin' | 'private';
817
+ subscription?: NonNullable<PostRecordConfig['table']>['subscription'];
818
+ };
819
+ data?: any;
820
+ index?: {
821
+ name: string;
822
+ value: string | number | boolean;
823
+ };
824
+ tags?: string[];
825
+ unique_id?: string;
826
+ /** Update instead of create. */
827
+ record_id?: string;
828
+ reference?: string;
829
+ readonly?: boolean;
830
+ source?: PostRecordConfig['source'];
831
+ /** Post as this user instead of the project owner. */
832
+ user_id?: string;
833
+ };
834
+ err?: TicketAction[];
835
+ } | {
836
+ /** HTTP request with its own response condition and nested chain. Result: the parsed response body. */
837
+ act: 'req';
838
+ exe: {
839
+ /** http:// or https:// with a hostname. No IP literal, no userinfo, never under the api domain. Redirects are not followed. */
840
+ url: string;
841
+ /** Default GET. */
842
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
843
+ headers?: {
844
+ [name: string]: string;
845
+ };
846
+ /** POST and PUT body. Sent as JSON when a content-type header says application/json, else form encoded. */
847
+ data?: any;
848
+ /** Query string. */
849
+ params?: {
850
+ [key: string]: any;
851
+ };
852
+ /** Legacy. Rows matched against the response body like `condition.data`. */
853
+ match?: TicketConditionRow[];
854
+ /** Evaluated against the response. Captures land in the shared placeholder pool. */
855
+ condition?: TicketResponseCondition;
856
+ /** Nested chain. Its paths read the response body. */
857
+ actions?: TicketAction[];
858
+ };
859
+ err?: TicketAction[];
860
+ };
861
+ /** An issued ticket as getTickets() and registerTicket() return it. */
862
+ type Ticket = {
863
+ ticket_id: string;
864
+ description?: string;
865
+ /** Remaining consumptions. Absent = unlimited. */
866
+ count?: number;
867
+ /** Absolute expiry in ms since epoch. Absent = never. */
868
+ time_to_live?: number;
869
+ /** true = once per user, n = n times. Absent or 0 = unlimited. Only enforced for signed-in consumers. */
870
+ limit_per_user?: boolean | number;
871
+ /** Created at (ms). */
872
+ timestamp: number;
873
+ /** Last registered at (ms). */
874
+ updated?: number;
875
+ condition?: TicketCondition;
876
+ actions?: TicketAction[];
877
+ };
878
+ type TicketErrorCode = 'INVALID_SERVICE' | 'SERVICE_DISABLED' | 'TICKET_NOT_FOUND' | 'TICKET_EXPIRED' | 'TICKET_EXHAUSTED' | 'USER_LIMIT_REACHED' | 'ISSUER_CANNOT_CONSUME' | 'AUTH_REQUIRED' | 'METHOD_NOT_ALLOWED' | 'CONDITION_FAILED' | 'PATH_NOT_FOUND' | 'PLACEHOLDER_MISSING' | 'REQUEST_FAILED' | 'TIMEOUT' | 'ACTION_FAILED' | 'ACTION_FORBIDDEN' | 'INTERNAL_ERROR';
879
+ /**
880
+ * The body a consume endpoint answers with when the consumption fails, and the `cause` of the
881
+ * SkapiError consumeTicket() rejects with. A success body never has `stage`; an error body
882
+ * always does, also when the ticket answers HTTP 200 (`return200`).
883
+ */
884
+ type TicketError = {
885
+ code: TicketErrorCode;
886
+ /** Human readable, one sentence. */
887
+ message: string;
888
+ stage: 'ticket' | 'condition' | 'action';
889
+ /** Only when stage is "action": the action that failed and its place in the chain, such as "actions[1].err[0]". */
890
+ action?: {
891
+ act: TicketAction['act'];
892
+ path: string;
893
+ };
894
+ /** Code specific, JSON safe. For example { expired_at } on TICKET_EXPIRED, { field, keys } on CONDITION_FAILED, { status, body } on REQUEST_FAILED. */
895
+ detail?: {
896
+ [key: string]: any;
897
+ };
898
+ ticket_id: string;
899
+ };
661
900
 
662
901
  type Types_BinaryFile = BinaryFile;
663
902
  type Types_Condition = Condition;
@@ -687,13 +926,23 @@ type Types_RequestHistory = RequestHistory;
687
926
  type Types_Subscription = Subscription;
688
927
  type Types_Table = Table;
689
928
  type Types_Tag = Tag;
929
+ type Types_Ticket = Ticket;
930
+ type Types_TicketAction = TicketAction;
931
+ type Types_TicketCondition = TicketCondition;
932
+ type Types_TicketConditionOperator = TicketConditionOperator;
933
+ type Types_TicketConditionRow = TicketConditionRow;
934
+ type Types_TicketError = TicketError;
935
+ type Types_TicketErrorCode = TicketErrorCode;
936
+ type Types_TicketRequestCondition = TicketRequestCondition;
937
+ type Types_TicketResponseCondition = TicketResponseCondition;
938
+ type Types_TicketSignatureCondition = TicketSignatureCondition;
690
939
  type Types_UniqueId = UniqueId;
691
940
  type Types_UserAttributes = UserAttributes;
692
941
  type Types_UserProfile = UserProfile;
693
942
  type Types_UserPublic = UserPublic;
694
943
  type Types_WebSocketMessage = WebSocketMessage;
695
944
  declare namespace Types {
696
- export type { Types_BinaryFile as BinaryFile, Types_Condition as Condition, Types_Connection as Connection, Types_ConnectionInfo as ConnectionInfo, Types_DatabaseResponse as DatabaseResponse, Types_DelRecordQuery as DelRecordQuery, Types_EncryptionOptions as EncryptionOptions, Types_FetchOptions as FetchOptions, Types_FileInfo as FileInfo, Types_Form as Form, Types_GetRecordQuery as GetRecordQuery, Types_Index as Index, Types_Newsletter as Newsletter, Types_NewsletterGroup as NewsletterGroup, Types_PostRecordConfig as PostRecordConfig, Types_ProgressCallback as ProgressCallback, Types_RTCConnector as RTCConnector, Types_RTCConnectorParams as RTCConnectorParams, Types_RTCEvent as RTCEvent, Types_RTCReceiverParams as RTCReceiverParams, Types_RTCResolved as RTCResolved, Types_RealtimeCallback as RealtimeCallback, Types_RecordData as RecordData, Types_RecordEncryptionInfo as RecordEncryptionInfo, Types_RequestHistory as RequestHistory, Types_Subscription as Subscription, Types_Table as Table, Types_Tag as Tag, Types_UniqueId as UniqueId, Types_UserAttributes as UserAttributes, Types_UserProfile as UserProfile, Types_UserPublic as UserPublic, Types_WebSocketMessage as WebSocketMessage };
945
+ export type { Types_BinaryFile as BinaryFile, Types_Condition as Condition, Types_Connection as Connection, Types_ConnectionInfo as ConnectionInfo, Types_DatabaseResponse as DatabaseResponse, Types_DelRecordQuery as DelRecordQuery, Types_EncryptionOptions as EncryptionOptions, Types_FetchOptions as FetchOptions, Types_FileInfo as FileInfo, Types_Form as Form, Types_GetRecordQuery as GetRecordQuery, Types_Index as Index, Types_Newsletter as Newsletter, Types_NewsletterGroup as NewsletterGroup, Types_PostRecordConfig as PostRecordConfig, Types_ProgressCallback as ProgressCallback, Types_RTCConnector as RTCConnector, Types_RTCConnectorParams as RTCConnectorParams, Types_RTCEvent as RTCEvent, Types_RTCReceiverParams as RTCReceiverParams, Types_RTCResolved as RTCResolved, Types_RealtimeCallback as RealtimeCallback, Types_RecordData as RecordData, Types_RecordEncryptionInfo as RecordEncryptionInfo, Types_RequestHistory as RequestHistory, Types_Subscription as Subscription, Types_Table as Table, Types_Tag as Tag, Types_Ticket as Ticket, Types_TicketAction as TicketAction, Types_TicketCondition as TicketCondition, Types_TicketConditionOperator as TicketConditionOperator, Types_TicketConditionRow as TicketConditionRow, Types_TicketError as TicketError, Types_TicketErrorCode as TicketErrorCode, Types_TicketRequestCondition as TicketRequestCondition, Types_TicketResponseCondition as TicketResponseCondition, Types_TicketSignatureCondition as TicketSignatureCondition, Types_UniqueId as UniqueId, Types_UserAttributes as UserAttributes, Types_UserProfile as UserProfile, Types_UserPublic as UserPublic, Types_WebSocketMessage as WebSocketMessage };
697
946
  }
698
947
 
699
948
  declare function terminatePendingRequests(): void;
@@ -946,6 +1195,18 @@ declare class Skapi {
946
1195
  }): Promise<string | void>;
947
1196
  /**
948
1197
  * Queries unique ID records by unique_id or condition filters.
1198
+ *
1199
+ * Refused to a signed out caller when the project's `require_login`
1200
+ * setting is on: REQUIRE_LOGIN from the SDK, INVALID_REQUEST from the
1201
+ * backend for a caller that skips it. A project that has never set the
1202
+ * flag counts as ON. This listing used to be served to anyone who knew
1203
+ * the project id, and called with no `unique_id` it enumerates every
1204
+ * unique ID in the project.
1205
+ *
1206
+ * The listing is NOT filtered by access group: a row is keyed by the
1207
+ * unique ID alone, so any caller who gets past the sign in check sees the
1208
+ * IDs of records in every group, each with the record ID it maps to.
1209
+ * Fetching those records still goes through the usual access checks.
949
1210
  * @param params Request parameters.
950
1211
  * @param fetchOptions Pagination and fetch behavior options.
951
1212
  * @returns A promise that resolves to Promise<DatabaseResponse<UniqueId>>.
@@ -989,6 +1250,23 @@ declare class Skapi {
989
1250
  openIdLogin(params: {
990
1251
  token: string;
991
1252
  id: string;
1253
+ /**
1254
+ * Merges this OpenID identity into the existing account whose ORIGINAL login
1255
+ * ID (its username, or the e-mail it was created with when it has none) is
1256
+ * this OpenID account's login ID. `true` merges; an array of OpenID attribute
1257
+ * names also copies those attributes to the account. Merging replaces the
1258
+ * account's password.
1259
+ *
1260
+ * Never merges through an e-mail login alias. openIdLogin() fails with EXISTS,
1261
+ * with or without merge, when this OpenID account's login ID is the verified
1262
+ * e-mail login of another account of the project (an account created with a
1263
+ * username, or one whose e-mail was changed to it), and in the rare case the
1264
+ * alias cannot be removed safely. Any other account holding that login ID as an
1265
+ * e-mail login alias has not verified the e-mail, so it loses the alias instead
1266
+ * and a new OpenID account is created for this login ID: the two accounts then
1267
+ * share the e-mail address. The alias is removed before signup restrictions are
1268
+ * checked, so it is gone even when the login is then refused for another reason.
1269
+ */
992
1270
  merge?: boolean | string[];
993
1271
  template?: {
994
1272
  /** message_id of the template to use for the welcome e-mail (sent the first time this OpenID user account is created). */
@@ -1033,9 +1311,12 @@ declare class Skapi {
1033
1311
  group: string;
1034
1312
  }>): Promise<string>;
1035
1313
  /**
1314
+ * **Deprecated. Use `forwardRequest(form, options)` instead.**
1315
+ *
1036
1316
  * Sends a secure outbound request using a Skapi client secret key.
1037
1317
  * @param params Request parameters.
1038
1318
  * @returns A promise that resolves to the final API response, or to a status object when the request is queued.
1319
+ * @deprecated Use forwardRequest(form, options), which takes the form as its first argument and makes the client secret optional. This name keeps working unchanged, and both dispatch the same request through the same code.
1039
1320
  */
1040
1321
  clientSecretRequest(params: {
1041
1322
  url: string;
@@ -1068,6 +1349,8 @@ declare class Skapi {
1068
1349
  }) => Promise<any>;
1069
1350
  }>;
1070
1351
  /**
1352
+ * **Deprecated. Use `forwardRequestHistory` instead, which is this same function.**
1353
+ *
1071
1354
  * Retrieves the history of client secret requests for a given URL and method.
1072
1355
  *
1073
1356
  * Listing modifiers: `compact` returns lightweight label/marker stubs
@@ -1081,6 +1364,7 @@ declare class Skapi {
1081
1364
  * @param params Request parameters.
1082
1365
  * @param fetchOptions Pagination and fetch behavior options.
1083
1366
  * @returns A promise that resolves to a paginated list of request history items.
1367
+ * @deprecated Use forwardRequestHistory, which is this same function. A request started under either name is listed by both.
1084
1368
  */
1085
1369
  clientSecretRequestHistory(params: {
1086
1370
  url: string;
@@ -1092,11 +1376,8 @@ declare class Skapi {
1092
1376
  queue_exclude?: string;
1093
1377
  }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<RequestHistory[]>>;
1094
1378
  /**
1095
- * Cancels a pending client secret request and removes it from the client-side queue if applicable.
1096
- * @param params Request parameters.
1097
- * @returns A promise that resolves to a result object with removed status and message.
1098
- */
1099
- /**
1379
+ * **Deprecated. Use `forwardRequestStream` instead, which is this same function.**
1380
+ *
1100
1381
  * Reads a streamed clientSecretRequest that this call did not start: a page reload,
1101
1382
  * a second tab, or a turn from history that was never finalized.
1102
1383
  *
@@ -1115,6 +1396,7 @@ declare class Skapi {
1115
1396
  * onStream: (chunk) => parser.feed(chunk)
1116
1397
  * });
1117
1398
  * ```
1399
+ * @deprecated Use forwardRequestStream, which is this same function.
1118
1400
  */
1119
1401
  clientSecretRequestStream(requestId: string, options: {
1120
1402
  url?: string;
@@ -1128,6 +1410,8 @@ declare class Skapi {
1128
1410
  owner?: string;
1129
1411
  }): Promise<any>;
1130
1412
  /**
1413
+ * **Deprecated. Use `forwardRequestFinalize` instead, which is this same function.**
1414
+ *
1131
1415
  * Stores the version of a streamed response you want KEPT as history, and releases
1132
1416
  * the relayed chunks it was assembled from.
1133
1417
  *
@@ -1143,6 +1427,7 @@ declare class Skapi {
1143
1427
  * method: 'POST'
1144
1428
  * });
1145
1429
  * ```
1430
+ * @deprecated Use forwardRequestFinalize, which is this same function.
1146
1431
  */
1147
1432
  clientSecretRequestFinalize(requestId: string, data?: any, options?: {
1148
1433
  url?: string;
@@ -1153,6 +1438,14 @@ declare class Skapi {
1153
1438
  finalized: boolean;
1154
1439
  message: string;
1155
1440
  }>;
1441
+ /**
1442
+ * **Deprecated. Use `cancelForwardRequest` instead, which is this same function.**
1443
+ *
1444
+ * Cancels a pending client secret request and removes it from the client-side queue if applicable.
1445
+ * @param params Request parameters.
1446
+ * @returns A promise that resolves to a result object with removed status and message.
1447
+ * @deprecated Use cancelForwardRequest, which is this same function.
1448
+ */
1156
1449
  cancelClientSecretRequest(params: {
1157
1450
  url: string;
1158
1451
  method: 'GET' | 'POST' | 'DELETE' | 'PUT';
@@ -1163,6 +1456,8 @@ declare class Skapi {
1163
1456
  message: string;
1164
1457
  }>;
1165
1458
  /**
1459
+ * **Deprecated. Use `stopForwardRequestPolling` instead, which is this same function.**
1460
+ *
1166
1461
  * Stops live polling for client secret requests without cancelling the requests
1167
1462
  * themselves. The server-side work continues; only this client stops asking about it.
1168
1463
  *
@@ -1178,6 +1473,7 @@ declare class Skapi {
1178
1473
  *
1179
1474
  * @param params Which polls to stop.
1180
1475
  * @returns The number of polls stopped.
1476
+ * @deprecated Use stopForwardRequestPolling, which is this same function.
1181
1477
  */
1182
1478
  stopClientSecretPolling(params?: {
1183
1479
  url?: string;
@@ -1188,14 +1484,18 @@ declare class Skapi {
1188
1484
  owner?: string;
1189
1485
  }): number;
1190
1486
  /**
1191
- * True if a poll result came from stopClientSecretPolling rather than the server.
1487
+ * True if a poll result came from stopForwardRequestPolling (or its deprecated alias
1488
+ * stopClientSecretPolling) rather than the server.
1192
1489
  * @param res A resolved poll result.
1193
1490
  */
1194
1491
  isPollStopped(res: any): boolean;
1195
1492
  /**
1493
+ * **Deprecated. Use `forwardRequestQueueCount` instead, which is this same function.**
1494
+ *
1196
1495
  * Returns the number of requests currently waiting in a named client secret request queue.
1197
1496
  * @param params Request parameters.
1198
1497
  * @returns A promise that resolves to queue count information.
1498
+ * @deprecated Use forwardRequestQueueCount, which is this same function.
1199
1499
  */
1200
1500
  clientSecretRequestQueueCount(params: {
1201
1501
  queue: string;
@@ -1206,34 +1506,191 @@ declare class Skapi {
1206
1506
  in_queue: number;
1207
1507
  }>;
1208
1508
  /**
1209
- * Consumes a one-time ticket and executes the ticketed request payload.
1509
+ * Lists forwarded requests for a given url and method, newest first.
1510
+ *
1511
+ * Listing modifiers: `compact` returns lightweight label/marker stubs
1512
+ * (request_text, response_text, response_complete_marker) instead of the
1513
+ * full request/response bodies, which stay on the server; `queue_exact`
1514
+ * restricts a queue listing to exactly the named queue (the queue lookup is
1515
+ * otherwise a prefix range, so queue "u1" would also match "u1-bg");
1516
+ * `queue_exclude` drops one queue's rows from the listing. Both queue
1517
+ * filters are applied server-side after the range read, so a page can come
1518
+ * back short while more matches remain - keep paging by startKey/endOfList.
1519
+ *
1520
+ * The same function as the deprecated clientSecretRequestHistory, so a request
1521
+ * sent under either name is listed here.
1210
1522
  * @param params Request parameters.
1211
- * @returns A promise that resolves to Promise<any>.
1523
+ * @param fetchOptions Pagination and fetch behavior options.
1524
+ * @returns A promise that resolves to a paginated list of request history items.
1525
+ */
1526
+ forwardRequestHistory(params: {
1527
+ url: string;
1528
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
1529
+ queue?: string;
1530
+ status?: 'pending' | 'running' | 'resolved' | 'failed';
1531
+ compact?: boolean;
1532
+ queue_exact?: boolean;
1533
+ queue_exclude?: string;
1534
+ }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<RequestHistory[]>>;
1535
+ /**
1536
+ * Reads a streamed forwardRequest that this call did not start: a page reload,
1537
+ * a second tab, or a turn from history that was never finalized.
1538
+ *
1539
+ * `onStream` receives the relayed text in order, exactly as the destination sent it.
1540
+ * skapi does not parse it; whatever grammar those bytes are in belongs to the caller
1541
+ * and its destination. Pass `since` to resume rather than re-read from the beginning.
1542
+ *
1543
+ * When the request is still running this polls until it settles. When it has already
1544
+ * settled it reads every chunk once and resolves.
1545
+ *
1546
+ * ```js
1547
+ * const parser = createMyParser();
1548
+ * await skapi.forwardRequestStream(requestId, {
1549
+ * url: 'https://api.example.com/v1/chat',
1550
+ * method: 'POST',
1551
+ * onStream: (chunk) => parser.feed(chunk)
1552
+ * });
1553
+ * ```
1554
+ */
1555
+ forwardRequestStream(requestId: string, options: {
1556
+ url?: string;
1557
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
1558
+ onStream?: (chunk: string, seq: number, via?: 'socket' | 'poll') => void;
1559
+ since?: number;
1560
+ poll?: number;
1561
+ realtimeGroup?: string;
1562
+ onResponse?: (res: any) => void;
1563
+ onError?: (err: any) => void;
1564
+ service?: string;
1565
+ owner?: string;
1566
+ }): Promise<any>;
1567
+ /**
1568
+ * Stores the version of a streamed response you want KEPT as history, and releases
1569
+ * the relayed chunks it was assembled from.
1570
+ *
1571
+ * The content is entirely yours. skapi neither validates nor interprets it: it stores
1572
+ * what you tell it to store. Only the caller who made the request may finalize it.
1573
+ *
1574
+ * Until you finalize, the chunks stay indefinitely and every read of that turn has to
1575
+ * fetch and re-parse all of them, so finalize as soon as you have rendered the answer.
1576
+ *
1577
+ * ```js
1578
+ * await skapi.forwardRequestFinalize(requestId, parser.result(), {
1579
+ * url: 'https://api.example.com/v1/chat',
1580
+ * method: 'POST'
1581
+ * });
1582
+ * ```
1583
+ */
1584
+ forwardRequestFinalize(requestId: string, data?: any, options?: {
1585
+ url?: string;
1586
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
1587
+ service?: string;
1588
+ owner?: string;
1589
+ }): Promise<{
1590
+ finalized: boolean;
1591
+ message: string;
1592
+ }>;
1593
+ /**
1594
+ * Cancels a queued forwarded request and removes it from the client-side queue if applicable.
1595
+ * @param params Request parameters.
1596
+ * @returns A promise that resolves to a result object with removed status and message.
1597
+ */
1598
+ cancelForwardRequest(params: {
1599
+ url: string;
1600
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
1601
+ id: string;
1602
+ queue?: string;
1603
+ }): Promise<{
1604
+ removed: boolean;
1605
+ message: string;
1606
+ }>;
1607
+ /**
1608
+ * Stops live polling of forwarded requests without cancelling the requests
1609
+ * themselves. The server-side work continues; only this client stops asking about it.
1610
+ *
1611
+ * Use it to drop polling traffic while the user is not looking at the results (a
1612
+ * hidden tab, a closed view), then simply poll again when they return. Polls run
1613
+ * one-at-a-time per queue, so a poll for a request that never settles otherwise
1614
+ * blocks every poll queued behind it.
1615
+ *
1616
+ * Pass `id` (with `url` and `method`) to stop one request, `queue` to stop a queue's
1617
+ * polls, or neither to stop all of them. A stopped poll resolves with
1618
+ * `{ id, status: 'stopped' }` rather than rejecting, and its `onResponse`/`onError`
1619
+ * callbacks are not called.
1620
+ *
1621
+ * @param params Which polls to stop.
1622
+ * @returns The number of polls stopped.
1623
+ */
1624
+ stopForwardRequestPolling(params?: {
1625
+ url?: string;
1626
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
1627
+ id?: string;
1628
+ queue?: string;
1629
+ service?: string;
1630
+ owner?: string;
1631
+ }): number;
1632
+ /**
1633
+ * Returns the number of requests currently waiting in a named forward request queue.
1634
+ * @param params Request parameters.
1635
+ * @returns A promise that resolves to queue count information.
1636
+ */
1637
+ forwardRequestQueueCount(params: {
1638
+ queue: string;
1639
+ service?: string;
1640
+ owner?: string;
1641
+ }): Promise<{
1642
+ queue_name: string;
1643
+ in_queue: number;
1644
+ }>;
1645
+ /**
1646
+ * Consumes a ticket: calls its endpoint with `data`, so the ticket's condition is checked and
1647
+ * its actions run. An anonymous POST goes to `/tp/`, an anonymous GET to `/tg/` with `data`
1648
+ * as the query string, and a signed-in consumption (`auth: true`) to `/tpa/`, which is POST only.
1649
+ *
1650
+ * A failed consumption rejects with a SkapiError: `err.code` is the ticket error code and
1651
+ * `err.cause` the flat TicketError body (`err.cause.stage`, `err.cause.action`, `err.cause.detail`).
1652
+ * This holds when the ticket answers HTTP 200 as well (`return200`): the body is inspected, not the status.
1653
+ * @param params Request parameters.
1654
+ * @returns A promise that resolves to Promise<{ ticket_id: string; consume_id: string; user_id: string; is_test: boolean; timestamp: number; hash: string; }>.
1212
1655
  */
1213
1656
  consumeTicket(params: {
1657
+ /** ID of the ticket to consume. */
1214
1658
  ticket_id: string;
1215
- method: string;
1659
+ /** "GET" or "POST". */
1660
+ method: 'GET' | 'POST';
1661
+ /** Consume as the signed-in user. POST only. */
1216
1662
  auth?: boolean;
1663
+ /** The POST body, or the query string of a GET. */
1217
1664
  data?: {
1218
1665
  [key: string]: any;
1219
1666
  };
1220
- }): Promise<any>;
1667
+ }): Promise<{
1668
+ ticket_id: string;
1669
+ consume_id: string;
1670
+ user_id: string;
1671
+ is_test: boolean;
1672
+ timestamp: number;
1673
+ hash: string;
1674
+ }>;
1221
1675
  /**
1222
- * Lists consumed tickets with optional filters and pagination.
1676
+ * Lists the tickets the signed-in user has consumed, with optional filters and pagination.
1223
1677
  * @param params Request parameters.
1224
1678
  * @param fetchOptions Pagination and fetch behavior options.
1225
1679
  * @returns A promise that resolves to Promise<DatabaseResponse<any>>.
1226
1680
  */
1227
1681
  getConsumedTickets(params: {
1682
+ /** Only the consumptions of this ticket. */
1228
1683
  ticket_id?: string;
1229
1684
  }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<any>>;
1230
1685
  /**
1231
1686
  * Lists issued tickets with optional filters and pagination.
1687
+ * The project owner gets the full ticket (condition, actions, count, expiry, per-user limit); any other signed-in user gets `ticket_id`, `description`, `count`, `time_to_live` and `timestamp` only.
1232
1688
  * @param params Request parameters.
1233
1689
  * @param fetchOptions Pagination and fetch behavior options.
1234
1690
  * @returns A promise that resolves to Promise<DatabaseResponse<any>>.
1235
1691
  */
1236
1692
  getTickets(params: {
1693
+ /** Absent = every ticket. "<id>" = that ticket. "#<id>#" = the consumption log of that ticket (project owner only). */
1237
1694
  ticket_id?: string;
1238
1695
  }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<any>>;
1239
1696
  /**
@@ -1290,30 +1747,71 @@ declare class Skapi {
1290
1747
  user_id: string;
1291
1748
  }): Promise<'SUCCESS: Account has been deleted.'>;
1292
1749
  /**
1293
- * Invites a user by email with optional attributes and invitation email options.
1294
- * @param params Payload for the request.
1295
- * @param options Optional behavior configuration.
1750
+ * Invites a user by e-mail. The invitation carries a temporary password and a
1751
+ * link to accept, valid for 7 days. Admin only (access_group 90 and above).
1752
+ *
1753
+ * Every profile attribute passed in `params` is written to the account when the
1754
+ * invitation is sent and is kept unchanged when the user accepts; accepting only
1755
+ * activates the account and marks its e-mail verified.
1756
+ *
1757
+ * Refused with EXISTS when the e-mail, or the username when one is given, is
1758
+ * already another account's login ID, and with EXISTS "User is already
1759
+ * invited." while the e-mail has a pending invitation.
1760
+ * @param params The invited user's e-mail, and optionally their profile.
1761
+ * @param options Where to send them after accepting, newsletter opt-in, and a custom template.
1296
1762
  * @returns A promise that resolves to Promise<'SUCCESS: Invitation has been sent. (User ID: xxx...)'>.
1297
1763
  */
1298
1764
  inviteUser(params: UserAttributes & {
1765
+ /** Required. The invitation, with the temporary password and the link to accept, is sent here. */
1766
+ email: string;
1767
+ /**
1768
+ * Optional. Becomes the invited account's PERMANENT login username and
1769
+ * can never be changed. The e-mail also logs the account in once the
1770
+ * invitation is accepted (best effort), not while it is pending. Refused
1771
+ * with EXISTS when the username or the e-mail is already another
1772
+ * account's login ID.
1773
+ */
1774
+ username?: string;
1775
+ /** ID of an OpenID logger registered in the project, to link the invited account to it. */
1299
1776
  openid_id?: string;
1777
+ /** 1~99. Defaults to 1. 99 is admin level. */
1300
1778
  access_group?: number;
1301
1779
  }, options?: {
1780
+ /** URL the user is taken to after accepting. Must not contain "#". */
1302
1781
  confirmation_url?: string;
1782
+ /**
1783
+ * Subscribe the user to Service Email (group 1) once they accept.
1784
+ * Requires `confirmation_url`. Defaults to false.
1785
+ */
1303
1786
  email_subscription?: boolean;
1787
+ /** A custom HTML template for this invitation e-mail. Both fields are required. */
1304
1788
  template?: {
1789
+ /** URL of the HTML template. Must include the required invitation placeholders. */
1305
1790
  url: string;
1791
+ /** Subject line of the e-mail. */
1306
1792
  subject: string;
1307
1793
  };
1308
1794
  }): Promise<'SUCCESS: Invitation has been sent. (User ID: xxx...)'>;
1309
1795
  /**
1310
1796
  * Creates a user account directly from admin context.
1797
+ *
1798
+ * Refused with EXISTS when the e-mail, or the username when one is given, is
1799
+ * already another account's login ID.
1311
1800
  * @param params Payload for the request.
1312
1801
  * @returns A promise that resolves to Promise<UserProfile & { email_admin: string; username: string; }>.
1313
1802
  */
1314
1803
  createAccount(params: UserAttributes & {
1804
+ /** Required. Always. */
1315
1805
  email: string;
1316
1806
  password: string;
1807
+ /**
1808
+ * Optional. Becomes the account's PERMANENT login username, which
1809
+ * always logs the account in. The e-mail is created unverified, so it
1810
+ * logs the account in only once the user verifies it with verifyEmail()
1811
+ * after logging in with the username. Refused with EXISTS when the
1812
+ * username or the e-mail is already another account's login ID.
1813
+ */
1814
+ username?: string;
1317
1815
  access_group?: number;
1318
1816
  }): Promise<UserProfile & {
1319
1817
  email_admin: string;
@@ -1331,6 +1829,29 @@ declare class Skapi {
1331
1829
  /**
1332
1830
  * Updates another user's profile attributes from admin context.
1333
1831
  * Requires the target user's user_id plus at least one attribute to update.
1832
+ *
1833
+ * Never the caller's own account, the project owner and access group 99 admins
1834
+ * included: the caller's own user_id is refused with INVALID_REQUEST and
1835
+ * 'Cannot modify attributes of the current user.' before any request is sent.
1836
+ * Change your own profile with updateProfile() without user_id.
1837
+ *
1838
+ * An admin in access groups 90 ~ 98 cannot update an account whose access group
1839
+ * is at or above their own, a disabled account included (it counts at the group
1840
+ * it had): refused with INVALID_REQUEST and 'No access to modify admin.', and
1841
+ * nothing is written. Access group 99 admins and the project owner are not limited.
1842
+ *
1843
+ * A changed e-mail is written unverified: this method never marks an e-mail
1844
+ * verified, for an admin in access groups 90 ~ 98 or anyone else. The new e-mail
1845
+ * logs the account in only once the user verifies it with verifyEmail(). The
1846
+ * previous e-mail stops logging in when the change is written, unless it is the
1847
+ * e-mail an account without a username was created with, which stays its login
1848
+ * ID. A username always logs in. The change is refused with EXISTS and
1849
+ * 'E-mail "user@email.com" is already a login ID in this service.' when the
1850
+ * e-mail is a login ID another account of the project was granted: the address
1851
+ * that account was created or invited with, or one it has verified. An e-mail
1852
+ * login another account holds without having verified the address blocks
1853
+ * nothing; it is removed, and that account keeps the login ID it was created
1854
+ * with.
1334
1855
  * @param params Target user_id and the attributes to update.
1335
1856
  * @returns A promise that resolves to Promise<'SUCCESS: User attributes updated.'>.
1336
1857
  */
@@ -1433,32 +1954,67 @@ declare class Skapi {
1433
1954
  }>(params: Params[] | Form<Params>, url?: string): Promise<Response | Response[]>;
1434
1955
  /**
1435
1956
  * Relays a request to a destination of your choosing from the server side,
1436
- * with the service api key added where the browser cannot read it, and
1437
- * streams the destination's response back as it arrives.
1438
- *
1439
- * The body is relayed verbatim, so an html form reaches the destination as
1440
- * multipart/form-data, files included; the form's own enctype and method
1441
- * attributes are not used. Supply options.onStream to receive the response
1442
- * chunk by chunk. An error response throws a SkapiError either way; reading
1443
- * the status code or a response header needs options.responseType
1444
- * 'response'. options.signal stops the client receiving the response, it
1445
- * does not cancel the request already sent to the destination.
1446
- *
1447
- * @param form Form element, submit event, FormData, or a plain object. Sent to the destination verbatim.
1448
- * @param options Destination url, method, headers, and an optional onStream callback.
1449
- * @returns A promise that resolves to the destination's response.
1450
- */
1451
- forwardRequest(form: any, options: {
1957
+ * optionally with one of your stored client secrets substituted in where you
1958
+ * put "$CLIENT_SECRET", so the secret never reaches the browser.
1959
+ *
1960
+ * The first argument is the form: a submit event, a form element, FormData, a
1961
+ * plain object, or null when everything is already in the options. It is
1962
+ * FLATTENED to fields and merged into the request, into `params` for GET,
1963
+ * DELETE and HEAD and into `data` otherwise; where a key is in both, the one
1964
+ * you typed in options wins. Files are dropped by that flattening, because
1965
+ * the merged object travels as JSON. Pass `multipart: true` to relay the
1966
+ * form's own bytes instead, files included, the way a browser would send
1967
+ * them; keep that body under 2 MB once encoded, and `data` is refused
1968
+ * alongside it.
1969
+ *
1970
+ * `secretName` is optional: name one and its value is substituted server side
1971
+ * and its allowed destinations are enforced, name none and only what you
1972
+ * supplied is forwarded. `skapiHeaders` tells the destination who is calling,
1973
+ * from the verified identity of the request; a header of your own starting
1974
+ * with "x-skapi-" is refused, which is what makes that prefix worth trusting.
1975
+ *
1976
+ * Queueing, polling, `stream`, `realtime` and the poll handle on the reply all
1977
+ * behave exactly as they do for the deprecated clientSecretRequest, because
1978
+ * they are the same code.
1979
+ *
1980
+ * @param form Submit event, form element, FormData, plain object, or null for no form body.
1981
+ * @param options Destination url and method, the optional secret name, and how to read the answer.
1982
+ * @returns A promise that resolves to the destination's response, or to a status object when the request is queued.
1983
+ */
1984
+ forwardRequest(form: SubmitEvent | HTMLFormElement | FormData | {
1985
+ [key: string]: any;
1986
+ } | null, options: {
1987
+ secretName?: string;
1452
1988
  url: string;
1453
1989
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
1454
1990
  headers?: {
1455
1991
  [key: string]: string;
1456
1992
  };
1457
- apiKeyHeader?: string;
1458
- apiKeyScheme?: string;
1459
- onStream?: (chunk: string) => void;
1460
- signal?: AbortSignal;
1993
+ data?: {
1994
+ [key: string]: any;
1995
+ };
1996
+ params?: {
1997
+ [key: string]: string;
1998
+ };
1999
+ multipart?: boolean;
2000
+ skapiHeaders?: boolean | {
2001
+ user?: boolean;
2002
+ service?: boolean;
2003
+ };
2004
+ service?: string;
2005
+ owner?: string;
2006
+ poll?: number;
2007
+ queue?: string;
2008
+ expires?: number;
2009
+ stream?: boolean;
2010
+ realtime?: boolean;
2011
+ onStream?: (chunk: string, seq: number, via?: 'socket' | 'poll') => void;
2012
+ onResponse?: (res: any, meta?: {
2013
+ executed?: number;
2014
+ }) => void;
2015
+ onError?: (err: any) => void;
1461
2016
  responseType?: 'json' | 'text' | 'response';
2017
+ signal?: AbortSignal;
1462
2018
  }): Promise<any>;
1463
2019
  /**
1464
2020
  * Returns a normalized response object for form-based handler flows.
@@ -1476,6 +2032,52 @@ declare class Skapi {
1476
2032
  }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<RecordData>>;
1477
2033
  /**
1478
2034
  * Lists table metadata with optional table-name filters.
2035
+ *
2036
+ * With `table` given and `condition` omitted, the table name is matched
2037
+ * exactly. With `table` omitted entirely, every table is returned.
2038
+ * `gte` / `>=` is a prefix search ("table names starting with x"), while
2039
+ * `gt` / `>` is a lexicographic greater-than that spills past the prefix
2040
+ * into every later table name.
2041
+ *
2042
+ * Every combination:
2043
+ * - `getTables({})`: every table in the project.
2044
+ * - `getTables({ table: 'x' })`: exact match on 'x'.
2045
+ * - `getTables({ table: 'x', condition: 'gte' })`: prefix, every table
2046
+ * name starting with 'x'.
2047
+ * - `getTables({ table: '' })`: errors with '"table" should not be empty.'
2048
+ * - `getTables({ condition: 'gte' })` with no `table`: errors with
2049
+ * '"table" is required for condition.'
2050
+ *
2051
+ * When you are looking for a table rather than listing them all, pass
2052
+ * `gte`: a name recorded two ways, such as "Asian Spice House" and "Asian
2053
+ * Spice House (alias)", is only found by the prefix. Real table names
2054
+ * very often carry a leading name shared with the rest of their data set,
2055
+ * a source or dataset prefix, so an exact match finds one spelling and
2056
+ * silently misses its siblings while the prefix finds the family. When
2057
+ * you already know the exact name, omitting `condition` is the exact
2058
+ * match you want. Omit the key to get the default: an explicit undefined
2059
+ * or null `condition` is rejected with INVALID_PARAMETER.
2060
+ *
2061
+ * Refused to a signed out caller when the project's `require_login`
2062
+ * setting is on. The SDK throws REQUIRE_LOGIN before the request leaves,
2063
+ * and the backend refuses the same call with INVALID_REQUEST, so a direct
2064
+ * HTTP call or an older SDK with no gate is refused too. A project that
2065
+ * has never set the flag counts as ON, the same default
2066
+ * `getConnectionInfo()` already reports as `conf.require_login`. Table
2067
+ * metadata used to be served to anyone who knew the project id, so an
2068
+ * integration that listed tables with no signed in user now gets an error
2069
+ * where it used to get a list.
2070
+ *
2071
+ * Table NAMES are never filtered: every table in the project is listed,
2072
+ * whatever access group the records inside it live in. The per access
2073
+ * group counters are filtered to the caller.
2074
+ * `number_of_records_in_access_group_public` goes to everyone, a signed in
2075
+ * user also receives the counters up to and including their own access
2076
+ * group, and `number_of_records_in_access_group_private` and
2077
+ * `number_of_records_in_access_group_admin` go to an admin or the project
2078
+ * owner only. `number_of_records` and `size` are NOT filtered: both stay
2079
+ * totals over every access group, so `number_of_records` is normally
2080
+ * larger than the counters you can see add up to.
1479
2081
  * @param query Query object used to filter results.
1480
2082
  * @param fetchOptions Pagination and fetch behavior options.
1481
2083
  * @returns A promise that resolves to Promise<DatabaseResponse<Table>>.
@@ -1484,11 +2086,58 @@ declare class Skapi {
1484
2086
  /** If omitted, fetches the full list of tables. */
1485
2087
  query?: {
1486
2088
  table?: string;
1487
- /** Condition operator of table name. */
2089
+ /** Condition operator of table name. Omitted: exact match on the given table name. `gte` / `>=`: prefix. */
1488
2090
  condition?: Condition;
1489
2091
  }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<Table>>;
1490
2092
  /**
1491
2093
  * Lists index metadata and aggregated index statistics for a table.
2094
+ *
2095
+ * There is no top-level `condition` here. With no `index` and no `order`,
2096
+ * every index of the table is listed, which is a prefix read. An `index`
2097
+ * without a trailing '.' is an exact match on that index name; an `index`
2098
+ * ending in '.' is a prefix that lists the children of that compound
2099
+ * index, so "Band." lists Band.name, Band.year.
2100
+ *
2101
+ * The only condition is `order.condition`, which requires `order.value`.
2102
+ * Omitting it matches `order.value` exactly.
2103
+ *
2104
+ * Every combination (`table` is required, so there is no no-argument
2105
+ * form):
2106
+ * - `getIndexes({ table: 't' })`: every index of table 't'.
2107
+ * - `getIndexes({ table: 't', index: 'Band' })`: exact match on the index
2108
+ * 'Band'.
2109
+ * - `getIndexes({ table: 't', index: 'Band.' })`: prefix, the children of
2110
+ * the compound index, so Band.name, Band.year.
2111
+ * - `getIndexes({ table: 't', order: { by: 'index_name', value: 'B' } })`:
2112
+ * exact match against the value.
2113
+ * - `getIndexes({ table: 't', order: { by: 'total_number' } })`: the whole
2114
+ * partition, ordered by that attribute.
2115
+ * - `order.condition` without `order.value`: errors.
2116
+ * - no `table`: errors with '"table" is required.'
2117
+ *
2118
+ * When you are looking for an index rather than listing them all, use the
2119
+ * prefix form (no `index`, or a name ending in '.') the way `gte` explores
2120
+ * in getTables and getTags, since an exact `index` name returns only that
2121
+ * one entry. Index names very often carry a leading name shared with the
2122
+ * rest of their data set, so an exact match finds one spelling and
2123
+ * silently misses its siblings while the prefix finds the family. Omit the
2124
+ * key to get the default: an explicit undefined or null condition is
2125
+ * rejected with INVALID_PARAMETER.
2126
+ *
2127
+ * Refused to a signed out caller when the project's `require_login`
2128
+ * setting is on: REQUIRE_LOGIN from the SDK, INVALID_REQUEST from the
2129
+ * backend for a caller that skips it. A project that has never set the
2130
+ * flag counts as ON. Index metadata used to be served to anyone who knew
2131
+ * the project id.
2132
+ *
2133
+ * The listing is NOT filtered by access group, for any caller. A stored
2134
+ * index row is keyed by table and index name with the access group left
2135
+ * out of the key, so one row aggregates every group: `number_of_records`,
2136
+ * `total_number`, `average_number` and the rest span the whole table,
2137
+ * private and admin records included. Anyone who gets past the sign in
2138
+ * check therefore reads the project's entire index vocabulary. The
2139
+ * records behind it stay gated, but an index name is visible to every
2140
+ * signed in user.
1492
2141
  * @param query Query object used to filter results.
1493
2142
  * @param fetchOptions Pagination and fetch behavior options.
1494
2143
  * @returns A promise that resolves to Promise<DatabaseResponse<Index>>.
@@ -1504,11 +2153,56 @@ declare class Skapi {
1504
2153
  by: 'average_number' | 'total_number' | 'number_count' | 'average_bool' | 'total_bool' | 'bool_count' | 'string_count' | 'index_name' | 'number_of_records';
1505
2154
  /** Value to query. */
1506
2155
  value?: number | boolean | string;
2156
+ /** Requires "value". Omitted: exact match against "value". */
1507
2157
  condition?: Condition;
1508
2158
  };
1509
2159
  }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<Index>>;
1510
2160
  /**
1511
2161
  * Lists tags used in a table with optional tag-name filtering.
2162
+ *
2163
+ * With `condition` omitted the default depends on what else you gave:
2164
+ * `table` and `tag` together match the tag exactly, `table` alone is a
2165
+ * prefix ('>=') that lists every tag in that table, and neither one
2166
+ * returns every tag in the project, ordered by record count, descending.
2167
+ * `gte` / `>=` is a prefix search.
2168
+ *
2169
+ * Every combination:
2170
+ * - `getTags({})`: every tag in the project, ordered by record count,
2171
+ * descending.
2172
+ * - `getTags({ table: 't' })`: every tag in table 't'.
2173
+ * - `getTags({ table: 't', tag: 'g' })`: exact match on the tag 'g' in
2174
+ * table 't'.
2175
+ * - `getTags({ tag: 'g' })` with no `table`: the tag 'g' across all
2176
+ * tables.
2177
+ * - `getTags({ table: 't', tag: 'g', condition: 'gte' })`: prefix, every
2178
+ * tag in 't' starting with 'g'.
2179
+ * - `getTags({ condition: 'gte' })` with neither `table` nor `tag`: errors
2180
+ * with '"table" or "tag" is required for condition.'
2181
+ *
2182
+ * When you are looking for a tag rather than listing them all, pass
2183
+ * `gte`: a name recorded two ways, such as "Asian Spice House" and "Asian
2184
+ * Spice House (alias)", is only found by the prefix. Real tags very often
2185
+ * carry a leading name shared with the rest of their data set, a series
2186
+ * name or an artist recorded once plainly and once with a parenthesised
2187
+ * alias, so an exact match finds one spelling and silently misses its
2188
+ * siblings while the prefix finds the family. When you already know the
2189
+ * exact name, omitting `condition` is the exact match you want. Omit the
2190
+ * key to get the default: an explicit undefined or null `condition` is
2191
+ * rejected with INVALID_PARAMETER.
2192
+ *
2193
+ * Refused to a signed out caller when the project's `require_login`
2194
+ * setting is on: REQUIRE_LOGIN from the SDK, INVALID_REQUEST from the
2195
+ * backend for a caller that skips it. A project that has never set the
2196
+ * flag counts as ON. Tag metadata used to be served to anyone who knew
2197
+ * the project id.
2198
+ *
2199
+ * The listing is NOT filtered by access group, for any caller. A stored
2200
+ * tag row is keyed by tag and table name with the access group left out
2201
+ * of the key, so `number_of_records` counts the records in every group
2202
+ * together. Anyone who gets past the sign in check reads the project's
2203
+ * entire tag vocabulary, tags carried only by private or admin records
2204
+ * included. The records behind it stay gated, but a tag name is visible
2205
+ * to every signed in user, so do not put anything secret in one.
1512
2206
  * @param query Query object used to filter results.
1513
2207
  * @param fetchOptions Pagination and fetch behavior options.
1514
2208
  * @returns A promise that resolves to Promise<DatabaseResponse<Tag>>.
@@ -1518,7 +2212,7 @@ declare class Skapi {
1518
2212
  table?: string;
1519
2213
  /** Tag name */
1520
2214
  tag?: string;
1521
- /** String query condition for tag name. */
2215
+ /** String query condition for tag name. Omitted: exact match when `table` and `tag` are both given, prefix when only `table` is given. */
1522
2216
  condition?: Condition;
1523
2217
  }, fetchOptions?: FetchOptions): Promise<DatabaseResponse<Tag>>;
1524
2218
  /**
@@ -1646,36 +2340,45 @@ declare class Skapi {
1646
2340
  /**
1647
2341
  * Gets newsletter subscription status for the requested groups.
1648
2342
  * Takes a numeric group, "public", "authorized" or a named newsletter group. Omit the group for every group the user is subscribed to.
2343
+ * The project owner, Skapi staff and access group 99 admins get every subscriber of the group instead, and can pass "email" to get only the subscribers whose e-mail address starts with it.
2344
+ * An admin in access groups 90 ~ 98 gets the subscriber list too, but reads it through a privacy layer:
2345
+ * "subscribed_email" is masked ("j**@**.com") and the mask is lossy, so two different subscribers can read the same;
2346
+ * "subscriber_token" comes with each masked row as an opaque, stable, per address key, and it is the only value that tells such rows apart, so key lists and selections on it, never on the masked address;
2347
+ * the token is not a readable address and is scoped to this service, owner and group, so it cannot be matched against a token from another group or project;
2348
+ * "startKey" is sealed by the server and has to be handed back verbatim, which fetchMore already does;
2349
+ * and "email" is refused with "No access.".
1649
2350
  * @param params Request parameters.
1650
2351
  * @param fetchOptions Pagination and fetch behavior options.
1651
- * @returns A promise that resolves to Promise<{ active: boolean; timestamp: number; group: number | string; subscribed_email: string; }[]>.
2352
+ * @returns A promise that resolves to Promise<{ active: boolean; timestamp: number; group: number | string; subscribed_email: string; subscriber_token?: string; }[]>.
1652
2353
  */
1653
2354
  getNewsletterSubscription(params?: {
1654
2355
  /** Numeric group, "public", "authorized" or a named newsletter group. Omit or null for every group. */
1655
2356
  group?: number | 'public' | 'authorized' | (string & {}) | null;
2357
+ /**
2358
+ * Another user's subscriptions. The project owner's account, Skapi staff and
2359
+ * admins (access groups 90 ~ 99) only. An admin in access groups 90 ~ 98 reads
2360
+ * that user's address masked.
2361
+ */
1656
2362
  user_id?: string;
2363
+ /** Owner, Skapi staff and access group 99 only. Returns the subscribers of "group" whose e-mail address starts with this text. Requires "group", cannot be used with "user_id". Admins 90 ~ 98 are refused with "No access.". */
2364
+ email?: string;
1657
2365
  }, fetchOptions?: FetchOptions): Promise<{
1658
2366
  active: boolean;
2367
+ /** Undefined on a group's whole subscriber list, which the index does not carry it on. */
1659
2368
  timestamp: number;
1660
2369
  group: number | string;
1661
2370
  subscribed_email: string;
2371
+ /** Only when the address above is masked. Opaque, stable per address key. */
2372
+ subscriber_token?: string;
1662
2373
  }[] | DatabaseResponse<{
1663
2374
  active: boolean;
2375
+ /** Undefined on a group's whole subscriber list, which the index does not carry it on. */
1664
2376
  timestamp: number;
1665
2377
  group: number | string;
1666
2378
  subscribed_email: string;
2379
+ /** Only when the address above is masked. Opaque, stable per address key. */
2380
+ subscriber_token?: string;
1667
2381
  }>>;
1668
- /**
1669
- * Requests a username change confirmation flow for the current user.
1670
- * @param params Request parameters.
1671
- * @returns A promise that resolves to Promise<'SUCCESS: confirmation e-mail has been sent.'>.
1672
- */
1673
- requestUsernameChange(params: {
1674
- /** Redirect URL when user clicks on the link. */
1675
- redirect?: string;
1676
- /** username(e-mail) user wish to change to. */
1677
- username: string;
1678
- }): Promise<'SUCCESS: confirmation e-mail has been sent.'>;
1679
2382
  /**
1680
2383
  * Reports whether client-side record encryption is on, and whether it is
1681
2384
  * currently unlocked.
@@ -1863,9 +2566,13 @@ declare class Skapi {
1863
2566
  * @returns A promise that resolves to Promise<UserProfile>.
1864
2567
  */
1865
2568
  login(params: Form<{
1866
- /** if given, username will be used instead of email. */
2569
+ /** if given, username will be used instead of email. A username always logs its account in. */
1867
2570
  username?: string;
1868
- /** E-Mail for signin. 64 character max. */
2571
+ /**
2572
+ * E-Mail for signin. 64 character max. The e-mail an account without a username
2573
+ * was created with always logs it in. Any other e-mail (that of an account created
2574
+ * with a username, or a changed e-mail) logs in only once it is verified (verifyEmail()).
2575
+ */
1869
2576
  email: string;
1870
2577
  /** Password for signin. Should be at least 6 characters. */
1871
2578
  password: string;
@@ -1885,7 +2592,19 @@ declare class Skapi {
1885
2592
  * @returns A promise that resolves to Promise<UserProfile | "SUCCESS: The account has been created. User's signup confirmation is required." | 'SUCCESS: The account has been created.'>.
1886
2593
  */
1887
2594
  signup(params: Form<UserAttributes & {
2595
+ /** Required. Always. */
2596
+ email: string;
1888
2597
  password: String;
2598
+ /**
2599
+ * Optional. When given it becomes the account's PERMANENT login
2600
+ * username, which always logs the account in and can never be changed.
2601
+ * The e-mail also logs the account in, but only once it is verified:
2602
+ * opening the signup confirmation link verifies it, and without signup
2603
+ * confirmation the user verifies it with verifyEmail() after logging in
2604
+ * with the username. After an e-mail change the new e-mail logs in once
2605
+ * it is verified the same way. E-mail login is not enabled while that
2606
+ * e-mail is already another account's login ID.
2607
+ */
1889
2608
  username?: string;
1890
2609
  }>, option?: {
1891
2610
  /**
@@ -1896,7 +2615,7 @@ declare class Skapi {
1896
2615
  */
1897
2616
  signup_confirmation?: boolean | string;
1898
2617
  /**
1899
- * When true, user will be subscribed to the service newsletter (group 1) once they are signed up.
2618
+ * When true, user will be subscribed to Service Email (group 1) once they are signed up.
1900
2619
  * User's signup confirmation is required for this parameter.
1901
2620
  * Default is false.
1902
2621
  */
@@ -1927,6 +2646,13 @@ declare class Skapi {
1927
2646
  }>): Promise<'SUCCESS: New password has been set.'>;
1928
2647
  /**
1929
2648
  * Verifies the user email address with a confirmation code.
2649
+ * Call it without `code` to send the code, then with the code the user received.
2650
+ * Once the e-mail is verified it also logs the account in (a few seconds after
2651
+ * the verification succeeds) when the account was created with a username or its
2652
+ * e-mail was changed, unless that e-mail is a login ID another account of the
2653
+ * project was granted: the address that account was created or invited with, or
2654
+ * one it has verified. An e-mail login another account holds without having
2655
+ * verified the address is removed, and this account gets the login.
1930
2656
  * @param params Payload for the request.
1931
2657
  * @returns A promise that resolves to Promise<string>.
1932
2658
  */
@@ -1974,6 +2700,19 @@ declare class Skapi {
1974
2700
  }): Promise<'SUCCESS: Password has been changed.'>;
1975
2701
  /**
1976
2702
  * Updates profile attributes for the authenticated user.
2703
+ *
2704
+ * A changed e-mail is written unverified, and it logs the account in only once
2705
+ * it is verified with verifyEmail(): the login follows a few seconds after the
2706
+ * verification succeeds. The previous e-mail stops logging in right after the
2707
+ * change, unless it is the e-mail an account without a username was created with,
2708
+ * which stays its login ID. A username always logs in. E-mail login is not added
2709
+ * while the new e-mail is a login ID another account of the project was granted:
2710
+ * the address that account was created or invited with, or one it has verified.
2711
+ * An e-mail login another account holds without having verified the address is
2712
+ * removed instead, and this account gets the login.
2713
+ *
2714
+ * With another user's `user_id` it updates that user from admin context and
2715
+ * follows the rules of updateUserAttributes(). Your own `user_id` is ignored.
1977
2716
  * @param params Payload for the request.
1978
2717
  * @returns A promise that resolves to Promise<UserProfile>.
1979
2718
  */
@@ -2060,12 +2799,13 @@ declare class Skapi {
2060
2799
 
2061
2800
  declare class SkapiError extends Error {
2062
2801
  code: string | number;
2063
- cause: Error;
2802
+ /** The underlying Error, or the flat TicketError body of a failed ticket consumption (consumeTicket). */
2803
+ cause: Error | TicketError;
2064
2804
  constructor(error: any, options?: {
2065
2805
  name?: string;
2066
2806
  code?: string;
2067
- cause?: Error;
2807
+ cause?: Error | TicketError;
2068
2808
  });
2069
2809
  }
2070
2810
 
2071
- export { type BinaryFile, type Condition, type Connection, type ConnectionInfo, type DatabaseResponse, type DelRecordQuery, type FetchOptions, type FileInfo, type Form, type GetRecordQuery, type Index, type Newsletter, type NewsletterGroup, type PostRecordConfig, type ProgressCallback, type RTCConnector, type RTCConnectorParams, type RTCEvent, type RTCReceiverParams, type RTCResolved, type RealtimeCallback, type RecordData, Skapi, SkapiError, type Subscription, type Table, type Tag, Types, type UniqueId, type UserAttributes, type UserProfile, type UserPublic, type WebSocketMessage };
2811
+ export { type BinaryFile, type Condition, type Connection, type ConnectionInfo, type DatabaseResponse, type DelRecordQuery, type FetchOptions, type FileInfo, type Form, type GetRecordQuery, type Index, type Newsletter, type NewsletterGroup, type PostRecordConfig, type ProgressCallback, type RTCConnector, type RTCConnectorParams, type RTCEvent, type RTCReceiverParams, type RTCResolved, type RealtimeCallback, type RecordData, Skapi, SkapiError, type Subscription, type Table, type Tag, type Ticket, type TicketAction, type TicketCondition, type TicketConditionOperator, type TicketConditionRow, type TicketError, type TicketErrorCode, type TicketRequestCondition, type TicketResponseCondition, type TicketSignatureCondition, Types, type UniqueId, type UserAttributes, type UserProfile, type UserPublic, type WebSocketMessage };