skapi-js 2.0.0-rc.6 → 2.0.0-rc.7

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
@@ -133,6 +133,46 @@ type BinaryFile = {
133
133
  uploaded: number;
134
134
  getFile: (dataType?: 'base64' | 'download' | 'endpoint' | 'blob' | 'text' | 'info', progress?: ProgressCallback) => Promise<Blob | string | void | FileInfo>;
135
135
  };
136
+ /**
137
+ * Per-record encryption outcome. Present ONLY when the record's data went
138
+ * through the client-side encryption layer, so its absence means the record was
139
+ * stored in the clear.
140
+ *
141
+ * status 'encrypted' means the data in this object was decrypted successfully.
142
+ * status 'failed' means `data` is null and `reason` says why:
143
+ * NO_SESSION_KEY encryption is locked; call unlockEncryption()
144
+ * NOT_A_RECIPIENT this user has no key wrap on the record
145
+ * BAD_KEY the wrap did not open (wrong or rotated key)
146
+ * BINDING_MISMATCH the envelope does not belong to this record
147
+ * CORRUPT the payload failed its authentication tag
148
+ * UNSUPPORTED_VERSION written by a newer SDK
149
+ * ENCRYPTION_DISABLED the record is encrypted but this instance is not
150
+ */
151
+ type RecordEncryptionInfo = {
152
+ status: 'encrypted' | 'failed';
153
+ reason?: string;
154
+ /** user_ids that hold a key wrap on this record. */
155
+ recipients?: string[];
156
+ };
157
+ /** Options for `new Skapi(..., { encryption })`. */
158
+ type EncryptionOptions = boolean | {
159
+ /** PBKDF2 iteration count. Default 600000. Minimum 100000. */
160
+ iterations?: number;
161
+ /** 'tofu' pins a peer's key on first sight (default). 'strict' requires a prior pin. */
162
+ trustPolicy?: 'tofu' | 'strict';
163
+ /** Keep the master key in IndexedDB so a page reload stays unlocked. Default true. */
164
+ persistDevice?: boolean;
165
+ /** Refuse to enroll a password shorter than this. Default 0 (no check). */
166
+ minPasswordLength?: number;
167
+ /**
168
+ * Issue a one-time recovery code at enrollment. Default 'code'.
169
+ * 'none' opts out and accepts that a forgotten password means the user's
170
+ * encrypted records are permanently unreadable.
171
+ */
172
+ recovery?: 'code' | 'none';
173
+ /** Reserved keyring table name. Default 'skapi__keyring'. */
174
+ table?: string;
175
+ };
136
176
  type RecordData = {
137
177
  record_id: string;
138
178
  unique_id?: string;
@@ -140,6 +180,8 @@ type RecordData = {
140
180
  updated: number;
141
181
  uploaded: number;
142
182
  referenced_count: number;
183
+ /** Set only when the record's data passed through the encryption layer. */
184
+ encrypted?: RecordEncryptionInfo;
143
185
  table: {
144
186
  name: string;
145
187
  /** Number range: 0 ~ 99 */
@@ -586,6 +628,7 @@ type Types_Connection = Connection;
586
628
  type Types_ConnectionInfo = ConnectionInfo;
587
629
  type Types_DatabaseResponse<T> = DatabaseResponse<T>;
588
630
  type Types_DelRecordQuery = DelRecordQuery;
631
+ type Types_EncryptionOptions = EncryptionOptions;
589
632
  type Types_FetchOptions = FetchOptions;
590
633
  type Types_FileInfo = FileInfo;
591
634
  type Types_Form<T> = Form<T>;
@@ -601,6 +644,7 @@ type Types_RTCReceiverParams = RTCReceiverParams;
601
644
  type Types_RTCResolved = RTCResolved;
602
645
  type Types_RealtimeCallback = RealtimeCallback;
603
646
  type Types_RecordData = RecordData;
647
+ type Types_RecordEncryptionInfo = RecordEncryptionInfo;
604
648
  type Types_RequestHistory = RequestHistory;
605
649
  type Types_Subscription = Subscription;
606
650
  type Types_Table = Table;
@@ -611,7 +655,7 @@ type Types_UserProfile = UserProfile;
611
655
  type Types_UserPublic = UserPublic;
612
656
  type Types_WebSocketMessage = WebSocketMessage;
613
657
  declare namespace Types {
614
- 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_FetchOptions as FetchOptions, Types_FileInfo as FileInfo, Types_Form as Form, Types_GetRecordQuery as GetRecordQuery, Types_Index as Index, Types_Newsletter as Newsletter, 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_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 };
658
+ 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_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 };
615
659
  }
616
660
 
617
661
  declare function terminatePendingRequests(): void;
@@ -654,6 +698,18 @@ type Options = {
654
698
  autoLogin: boolean;
655
699
  refetchServiceInfo?: boolean;
656
700
  requestBatchSize?: number;
701
+ /**
702
+ * Enable client-side encryption of `data` on records written to
703
+ * access_group 'private'. Off by default. See EncryptionOptions.
704
+ */
705
+ encryption?: boolean | {
706
+ iterations?: number;
707
+ trustPolicy?: 'tofu' | 'strict';
708
+ persistDevice?: boolean;
709
+ minPasswordLength?: number;
710
+ recovery?: 'code' | 'none';
711
+ table?: string;
712
+ };
657
713
  eventListener?: {
658
714
  onLogin?: (user: UserProfile | null) => void;
659
715
  onUserUpdate?: (user: UserProfile | null) => void;
@@ -1487,6 +1543,105 @@ declare class Skapi {
1487
1543
  /** username(e-mail) user wish to change to. */
1488
1544
  username: string;
1489
1545
  }): Promise<'SUCCESS: confirmation e-mail has been sent.'>;
1546
+ /**
1547
+ * Reports whether client-side record encryption is on, and whether it is
1548
+ * currently unlocked.
1549
+ * @returns { status: 'disabled' | 'locked' | 'unlocked', reason?, user_id?, fingerprint? }
1550
+ */
1551
+ getEncryptionStatus(): {
1552
+ status: string;
1553
+ reason?: string;
1554
+ user_id?: string;
1555
+ fingerprint?: string;
1556
+ };
1557
+ /**
1558
+ * True when a record's `data` is the placeholder returned in place of
1559
+ * content this session cannot decrypt.
1560
+ *
1561
+ * Only relevant with `encryption: { withheld: 'sentinel' }`. Under the
1562
+ * default, withheld data is `null` and an ordinary falsy check is enough.
1563
+ * @param data The value of `record.data`.
1564
+ * @returns boolean
1565
+ */
1566
+ isWithheld(data: any): boolean;
1567
+ /**
1568
+ * Unlocks record encryption with the user's password.
1569
+ *
1570
+ * Normally unnecessary: logging in unlocks automatically, and a page reload
1571
+ * unlocks from the device store. This is for a session restored from a token
1572
+ * on a device that has never been unlocked, and for Node, which has no
1573
+ * IndexedDB and is therefore always locked after a token restore.
1574
+ * @param params Request parameters.
1575
+ * @returns A promise that resolves to Promise<{ status: string }>.
1576
+ */
1577
+ unlockEncryption(params: {
1578
+ password: string;
1579
+ }): Promise<{
1580
+ status: string;
1581
+ }>;
1582
+ /**
1583
+ * Drops encryption keys from memory without logging out. Pass
1584
+ * { forgetDevice: true } to also clear the device store, which means the
1585
+ * next reload will require the password again.
1586
+ * @param params Request parameters.
1587
+ * @returns A promise that resolves to Promise<{ status: string }>.
1588
+ */
1589
+ lockEncryption(params?: {
1590
+ forgetDevice?: boolean;
1591
+ }): Promise<{
1592
+ status: string;
1593
+ }>;
1594
+ /**
1595
+ * Collects a freshly minted recovery code, ONCE.
1596
+ *
1597
+ * Call it right after a login or signup that may have enrolled the user; it
1598
+ * returns the code and forgets it. There is no way to fetch it again later,
1599
+ * and that is the point: if the SDK could hand it back on demand it would be
1600
+ * holding the key, and so could the service provider. Show it, make the user
1601
+ * confirm they saved it, and never send it anywhere.
1602
+ * @returns The code, or null if nothing was enrolled.
1603
+ */
1604
+ takeRecoveryCode(): string | null;
1605
+ /**
1606
+ * Unlocks with a recovery code after a forgotten-password reset, and repairs
1607
+ * the keyring for the new password.
1608
+ *
1609
+ * The order is: reset the password, log in with the new one (encryption will
1610
+ * report 'locked'), then call this with the code and that new password. A
1611
+ * used code is retired and a replacement is returned.
1612
+ * @param params Request parameters.
1613
+ * @returns A promise that resolves to Promise<{ status, repaired, recoveryCode }>.
1614
+ */
1615
+ unlockWithRecoveryCode(params: {
1616
+ code: string;
1617
+ password?: string;
1618
+ }): Promise<{
1619
+ status: string;
1620
+ repaired: boolean;
1621
+ recoveryCode: string | null;
1622
+ }>;
1623
+ /**
1624
+ * Retires the current recovery code and issues a new one. Requires an
1625
+ * unlocked session: only someone who can already decrypt can mint a code.
1626
+ * @returns A promise that resolves to Promise<{ recoveryCode: string }>.
1627
+ */
1628
+ regenerateRecoveryCode(): Promise<{
1629
+ recoveryCode: string;
1630
+ }>;
1631
+ /**
1632
+ * Pins another user's public key fingerprint after verifying it out of band.
1633
+ *
1634
+ * Only needed when a peer's key has changed, or when trustPolicy is
1635
+ * 'strict'. The provider serves the key directory, so a changed key is both
1636
+ * what a legitimate account reset looks like and what a key substitution
1637
+ * attack looks like: the SDK refuses to guess.
1638
+ * @param params Request parameters.
1639
+ * @returns A promise that resolves to Promise<void>.
1640
+ */
1641
+ pinPeerKey(params: {
1642
+ user_id: string;
1643
+ fingerprint: string;
1644
+ }): Promise<void>;
1490
1645
  /**
1491
1646
  * Grants users access to a private record.
1492
1647
  * @param params Request parameters.
package/dist/skapi.d.ts CHANGED
@@ -133,6 +133,46 @@ type BinaryFile = {
133
133
  uploaded: number;
134
134
  getFile: (dataType?: 'base64' | 'download' | 'endpoint' | 'blob' | 'text' | 'info', progress?: ProgressCallback) => Promise<Blob | string | void | FileInfo>;
135
135
  };
136
+ /**
137
+ * Per-record encryption outcome. Present ONLY when the record's data went
138
+ * through the client-side encryption layer, so its absence means the record was
139
+ * stored in the clear.
140
+ *
141
+ * status 'encrypted' means the data in this object was decrypted successfully.
142
+ * status 'failed' means `data` is null and `reason` says why:
143
+ * NO_SESSION_KEY encryption is locked; call unlockEncryption()
144
+ * NOT_A_RECIPIENT this user has no key wrap on the record
145
+ * BAD_KEY the wrap did not open (wrong or rotated key)
146
+ * BINDING_MISMATCH the envelope does not belong to this record
147
+ * CORRUPT the payload failed its authentication tag
148
+ * UNSUPPORTED_VERSION written by a newer SDK
149
+ * ENCRYPTION_DISABLED the record is encrypted but this instance is not
150
+ */
151
+ type RecordEncryptionInfo = {
152
+ status: 'encrypted' | 'failed';
153
+ reason?: string;
154
+ /** user_ids that hold a key wrap on this record. */
155
+ recipients?: string[];
156
+ };
157
+ /** Options for `new Skapi(..., { encryption })`. */
158
+ type EncryptionOptions = boolean | {
159
+ /** PBKDF2 iteration count. Default 600000. Minimum 100000. */
160
+ iterations?: number;
161
+ /** 'tofu' pins a peer's key on first sight (default). 'strict' requires a prior pin. */
162
+ trustPolicy?: 'tofu' | 'strict';
163
+ /** Keep the master key in IndexedDB so a page reload stays unlocked. Default true. */
164
+ persistDevice?: boolean;
165
+ /** Refuse to enroll a password shorter than this. Default 0 (no check). */
166
+ minPasswordLength?: number;
167
+ /**
168
+ * Issue a one-time recovery code at enrollment. Default 'code'.
169
+ * 'none' opts out and accepts that a forgotten password means the user's
170
+ * encrypted records are permanently unreadable.
171
+ */
172
+ recovery?: 'code' | 'none';
173
+ /** Reserved keyring table name. Default 'skapi__keyring'. */
174
+ table?: string;
175
+ };
136
176
  type RecordData = {
137
177
  record_id: string;
138
178
  unique_id?: string;
@@ -140,6 +180,8 @@ type RecordData = {
140
180
  updated: number;
141
181
  uploaded: number;
142
182
  referenced_count: number;
183
+ /** Set only when the record's data passed through the encryption layer. */
184
+ encrypted?: RecordEncryptionInfo;
143
185
  table: {
144
186
  name: string;
145
187
  /** Number range: 0 ~ 99 */
@@ -586,6 +628,7 @@ type Types_Connection = Connection;
586
628
  type Types_ConnectionInfo = ConnectionInfo;
587
629
  type Types_DatabaseResponse<T> = DatabaseResponse<T>;
588
630
  type Types_DelRecordQuery = DelRecordQuery;
631
+ type Types_EncryptionOptions = EncryptionOptions;
589
632
  type Types_FetchOptions = FetchOptions;
590
633
  type Types_FileInfo = FileInfo;
591
634
  type Types_Form<T> = Form<T>;
@@ -601,6 +644,7 @@ type Types_RTCReceiverParams = RTCReceiverParams;
601
644
  type Types_RTCResolved = RTCResolved;
602
645
  type Types_RealtimeCallback = RealtimeCallback;
603
646
  type Types_RecordData = RecordData;
647
+ type Types_RecordEncryptionInfo = RecordEncryptionInfo;
604
648
  type Types_RequestHistory = RequestHistory;
605
649
  type Types_Subscription = Subscription;
606
650
  type Types_Table = Table;
@@ -611,7 +655,7 @@ type Types_UserProfile = UserProfile;
611
655
  type Types_UserPublic = UserPublic;
612
656
  type Types_WebSocketMessage = WebSocketMessage;
613
657
  declare namespace Types {
614
- 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_FetchOptions as FetchOptions, Types_FileInfo as FileInfo, Types_Form as Form, Types_GetRecordQuery as GetRecordQuery, Types_Index as Index, Types_Newsletter as Newsletter, 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_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 };
658
+ 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_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 };
615
659
  }
616
660
 
617
661
  declare function terminatePendingRequests(): void;
@@ -654,6 +698,18 @@ type Options = {
654
698
  autoLogin: boolean;
655
699
  refetchServiceInfo?: boolean;
656
700
  requestBatchSize?: number;
701
+ /**
702
+ * Enable client-side encryption of `data` on records written to
703
+ * access_group 'private'. Off by default. See EncryptionOptions.
704
+ */
705
+ encryption?: boolean | {
706
+ iterations?: number;
707
+ trustPolicy?: 'tofu' | 'strict';
708
+ persistDevice?: boolean;
709
+ minPasswordLength?: number;
710
+ recovery?: 'code' | 'none';
711
+ table?: string;
712
+ };
657
713
  eventListener?: {
658
714
  onLogin?: (user: UserProfile | null) => void;
659
715
  onUserUpdate?: (user: UserProfile | null) => void;
@@ -1487,6 +1543,105 @@ declare class Skapi {
1487
1543
  /** username(e-mail) user wish to change to. */
1488
1544
  username: string;
1489
1545
  }): Promise<'SUCCESS: confirmation e-mail has been sent.'>;
1546
+ /**
1547
+ * Reports whether client-side record encryption is on, and whether it is
1548
+ * currently unlocked.
1549
+ * @returns { status: 'disabled' | 'locked' | 'unlocked', reason?, user_id?, fingerprint? }
1550
+ */
1551
+ getEncryptionStatus(): {
1552
+ status: string;
1553
+ reason?: string;
1554
+ user_id?: string;
1555
+ fingerprint?: string;
1556
+ };
1557
+ /**
1558
+ * True when a record's `data` is the placeholder returned in place of
1559
+ * content this session cannot decrypt.
1560
+ *
1561
+ * Only relevant with `encryption: { withheld: 'sentinel' }`. Under the
1562
+ * default, withheld data is `null` and an ordinary falsy check is enough.
1563
+ * @param data The value of `record.data`.
1564
+ * @returns boolean
1565
+ */
1566
+ isWithheld(data: any): boolean;
1567
+ /**
1568
+ * Unlocks record encryption with the user's password.
1569
+ *
1570
+ * Normally unnecessary: logging in unlocks automatically, and a page reload
1571
+ * unlocks from the device store. This is for a session restored from a token
1572
+ * on a device that has never been unlocked, and for Node, which has no
1573
+ * IndexedDB and is therefore always locked after a token restore.
1574
+ * @param params Request parameters.
1575
+ * @returns A promise that resolves to Promise<{ status: string }>.
1576
+ */
1577
+ unlockEncryption(params: {
1578
+ password: string;
1579
+ }): Promise<{
1580
+ status: string;
1581
+ }>;
1582
+ /**
1583
+ * Drops encryption keys from memory without logging out. Pass
1584
+ * { forgetDevice: true } to also clear the device store, which means the
1585
+ * next reload will require the password again.
1586
+ * @param params Request parameters.
1587
+ * @returns A promise that resolves to Promise<{ status: string }>.
1588
+ */
1589
+ lockEncryption(params?: {
1590
+ forgetDevice?: boolean;
1591
+ }): Promise<{
1592
+ status: string;
1593
+ }>;
1594
+ /**
1595
+ * Collects a freshly minted recovery code, ONCE.
1596
+ *
1597
+ * Call it right after a login or signup that may have enrolled the user; it
1598
+ * returns the code and forgets it. There is no way to fetch it again later,
1599
+ * and that is the point: if the SDK could hand it back on demand it would be
1600
+ * holding the key, and so could the service provider. Show it, make the user
1601
+ * confirm they saved it, and never send it anywhere.
1602
+ * @returns The code, or null if nothing was enrolled.
1603
+ */
1604
+ takeRecoveryCode(): string | null;
1605
+ /**
1606
+ * Unlocks with a recovery code after a forgotten-password reset, and repairs
1607
+ * the keyring for the new password.
1608
+ *
1609
+ * The order is: reset the password, log in with the new one (encryption will
1610
+ * report 'locked'), then call this with the code and that new password. A
1611
+ * used code is retired and a replacement is returned.
1612
+ * @param params Request parameters.
1613
+ * @returns A promise that resolves to Promise<{ status, repaired, recoveryCode }>.
1614
+ */
1615
+ unlockWithRecoveryCode(params: {
1616
+ code: string;
1617
+ password?: string;
1618
+ }): Promise<{
1619
+ status: string;
1620
+ repaired: boolean;
1621
+ recoveryCode: string | null;
1622
+ }>;
1623
+ /**
1624
+ * Retires the current recovery code and issues a new one. Requires an
1625
+ * unlocked session: only someone who can already decrypt can mint a code.
1626
+ * @returns A promise that resolves to Promise<{ recoveryCode: string }>.
1627
+ */
1628
+ regenerateRecoveryCode(): Promise<{
1629
+ recoveryCode: string;
1630
+ }>;
1631
+ /**
1632
+ * Pins another user's public key fingerprint after verifying it out of band.
1633
+ *
1634
+ * Only needed when a peer's key has changed, or when trustPolicy is
1635
+ * 'strict'. The provider serves the key directory, so a changed key is both
1636
+ * what a legitimate account reset looks like and what a key substitution
1637
+ * attack looks like: the SDK refuses to guess.
1638
+ * @param params Request parameters.
1639
+ * @returns A promise that resolves to Promise<void>.
1640
+ */
1641
+ pinPeerKey(params: {
1642
+ user_id: string;
1643
+ fingerprint: string;
1644
+ }): Promise<void>;
1490
1645
  /**
1491
1646
  * Grants users access to a private record.
1492
1647
  * @param params Request parameters.