@fluxbase/sdk 0.0.1-rc.31 → 0.0.1-rc.33
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/index.cjs +234 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +148 -10
- package/dist/index.d.ts +148 -10
- package/dist/index.js +234 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -291,6 +291,14 @@ var FluxbaseAuth = class {
|
|
|
291
291
|
return { user: session.user, session };
|
|
292
292
|
});
|
|
293
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Refresh the session (Supabase-compatible alias)
|
|
296
|
+
* Alias for refreshSession() to maintain compatibility with Supabase naming
|
|
297
|
+
* Returns a new session with refreshed tokens
|
|
298
|
+
*/
|
|
299
|
+
async refreshToken() {
|
|
300
|
+
return this.refreshSession();
|
|
301
|
+
}
|
|
294
302
|
/**
|
|
295
303
|
* Get the current user from the server
|
|
296
304
|
*/
|
|
@@ -617,6 +625,136 @@ var FluxbaseAuth = class {
|
|
|
617
625
|
return { provider, url };
|
|
618
626
|
});
|
|
619
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* Sign in with OTP (One-Time Password) - Supabase-compatible
|
|
630
|
+
* Sends a one-time password via email or SMS for passwordless authentication
|
|
631
|
+
* @param credentials - Email or phone number and optional configuration
|
|
632
|
+
* @returns Promise with OTP-style response
|
|
633
|
+
*/
|
|
634
|
+
async signInWithOtp(credentials) {
|
|
635
|
+
return wrapAsync(async () => {
|
|
636
|
+
await this.fetch.post("/api/v1/auth/otp/signin", credentials);
|
|
637
|
+
return { user: null, session: null };
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Verify OTP (One-Time Password) - Supabase-compatible
|
|
642
|
+
* Verify OTP tokens for various authentication flows
|
|
643
|
+
* @param params - OTP verification parameters including token and type
|
|
644
|
+
* @returns Promise with user and session if successful
|
|
645
|
+
*/
|
|
646
|
+
async verifyOtp(params) {
|
|
647
|
+
return wrapAsync(async () => {
|
|
648
|
+
const response = await this.fetch.post(
|
|
649
|
+
"/api/v1/auth/otp/verify",
|
|
650
|
+
params
|
|
651
|
+
);
|
|
652
|
+
if (response.access_token && response.refresh_token) {
|
|
653
|
+
const session = {
|
|
654
|
+
...response,
|
|
655
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
656
|
+
};
|
|
657
|
+
this.setSessionInternal(session);
|
|
658
|
+
return { user: response.user, session };
|
|
659
|
+
}
|
|
660
|
+
return { user: response.user, session: null };
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Resend OTP (One-Time Password) - Supabase-compatible
|
|
665
|
+
* Resend OTP code when user doesn't receive it
|
|
666
|
+
* @param params - Resend parameters including type and email/phone
|
|
667
|
+
* @returns Promise with OTP-style response
|
|
668
|
+
*/
|
|
669
|
+
async resendOtp(params) {
|
|
670
|
+
return wrapAsync(async () => {
|
|
671
|
+
await this.fetch.post("/api/v1/auth/otp/resend", params);
|
|
672
|
+
return { user: null, session: null };
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Get user identities (linked OAuth providers) - Supabase-compatible
|
|
677
|
+
* Lists all OAuth identities linked to the current user
|
|
678
|
+
* @returns Promise with list of user identities
|
|
679
|
+
*/
|
|
680
|
+
async getUserIdentities() {
|
|
681
|
+
return wrapAsync(async () => {
|
|
682
|
+
if (!this.session) {
|
|
683
|
+
throw new Error("Not authenticated");
|
|
684
|
+
}
|
|
685
|
+
return await this.fetch.get(
|
|
686
|
+
"/api/v1/auth/user/identities"
|
|
687
|
+
);
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Link an OAuth identity to current user - Supabase-compatible
|
|
692
|
+
* Links an additional OAuth provider to the existing account
|
|
693
|
+
* @param credentials - Provider to link
|
|
694
|
+
* @returns Promise with OAuth URL to complete linking
|
|
695
|
+
*/
|
|
696
|
+
async linkIdentity(credentials) {
|
|
697
|
+
return wrapAsync(async () => {
|
|
698
|
+
if (!this.session) {
|
|
699
|
+
throw new Error("Not authenticated");
|
|
700
|
+
}
|
|
701
|
+
return await this.fetch.post(
|
|
702
|
+
"/api/v1/auth/user/identities",
|
|
703
|
+
credentials
|
|
704
|
+
);
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Unlink an OAuth identity from current user - Supabase-compatible
|
|
709
|
+
* Removes a linked OAuth provider from the account
|
|
710
|
+
* @param params - Identity to unlink
|
|
711
|
+
* @returns Promise with void response
|
|
712
|
+
*/
|
|
713
|
+
async unlinkIdentity(params) {
|
|
714
|
+
return wrapAsyncVoid(async () => {
|
|
715
|
+
if (!this.session) {
|
|
716
|
+
throw new Error("Not authenticated");
|
|
717
|
+
}
|
|
718
|
+
await this.fetch.delete(
|
|
719
|
+
`/api/v1/auth/user/identities/${params.identity.id}`
|
|
720
|
+
);
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Reauthenticate to get security nonce - Supabase-compatible
|
|
725
|
+
* Get a security nonce for sensitive operations (password change, etc.)
|
|
726
|
+
* @returns Promise with nonce for reauthentication
|
|
727
|
+
*/
|
|
728
|
+
async reauthenticate() {
|
|
729
|
+
return wrapAsync(async () => {
|
|
730
|
+
if (!this.session) {
|
|
731
|
+
throw new Error("Not authenticated");
|
|
732
|
+
}
|
|
733
|
+
return await this.fetch.post(
|
|
734
|
+
"/api/v1/auth/reauthenticate"
|
|
735
|
+
);
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Sign in with ID token (for native mobile apps) - Supabase-compatible
|
|
740
|
+
* Authenticate using native mobile app ID tokens (Google, Apple)
|
|
741
|
+
* @param credentials - Provider, ID token, and optional nonce
|
|
742
|
+
* @returns Promise with user and session
|
|
743
|
+
*/
|
|
744
|
+
async signInWithIdToken(credentials) {
|
|
745
|
+
return wrapAsync(async () => {
|
|
746
|
+
const response = await this.fetch.post(
|
|
747
|
+
"/api/v1/auth/signin/idtoken",
|
|
748
|
+
credentials
|
|
749
|
+
);
|
|
750
|
+
const session = {
|
|
751
|
+
...response,
|
|
752
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
753
|
+
};
|
|
754
|
+
this.setSessionInternal(session);
|
|
755
|
+
return { user: session.user, session };
|
|
756
|
+
});
|
|
757
|
+
}
|
|
620
758
|
/**
|
|
621
759
|
* Internal: Set the session and persist it
|
|
622
760
|
*/
|
|
@@ -700,6 +838,8 @@ var RealtimeChannel = class {
|
|
|
700
838
|
this.maxReconnectAttempts = 10;
|
|
701
839
|
this.reconnectDelay = 1e3;
|
|
702
840
|
this.heartbeatInterval = null;
|
|
841
|
+
this.pendingAcks = /* @__PURE__ */ new Map();
|
|
842
|
+
this.messageIdCounter = 0;
|
|
703
843
|
this.url = url;
|
|
704
844
|
this.channelName = channelName;
|
|
705
845
|
this.token = token;
|
|
@@ -822,16 +962,40 @@ var RealtimeChannel = class {
|
|
|
822
962
|
return "error";
|
|
823
963
|
}
|
|
824
964
|
try {
|
|
965
|
+
const messageId = this.config.broadcast?.ack ? `msg_${Date.now()}_${++this.messageIdCounter}` : void 0;
|
|
825
966
|
this.ws.send(
|
|
826
967
|
JSON.stringify({
|
|
827
968
|
type: "broadcast",
|
|
828
969
|
channel: this.channelName,
|
|
829
970
|
event: message.event,
|
|
830
|
-
payload: message.payload
|
|
971
|
+
payload: message.payload,
|
|
972
|
+
...messageId && { messageId }
|
|
831
973
|
})
|
|
832
974
|
);
|
|
833
|
-
if (this.config.broadcast?.ack) {
|
|
834
|
-
|
|
975
|
+
if (this.config.broadcast?.ack && messageId) {
|
|
976
|
+
const ackTimeout = this.config.broadcast.ackTimeout || 5e3;
|
|
977
|
+
return await new Promise((resolve, reject) => {
|
|
978
|
+
const timeout = setTimeout(() => {
|
|
979
|
+
this.pendingAcks.delete(messageId);
|
|
980
|
+
reject(new Error("Acknowledgment timeout"));
|
|
981
|
+
}, ackTimeout);
|
|
982
|
+
this.pendingAcks.set(messageId, {
|
|
983
|
+
resolve: (value) => {
|
|
984
|
+
clearTimeout(timeout);
|
|
985
|
+
this.pendingAcks.delete(messageId);
|
|
986
|
+
resolve(value);
|
|
987
|
+
},
|
|
988
|
+
reject: (reason) => {
|
|
989
|
+
clearTimeout(timeout);
|
|
990
|
+
this.pendingAcks.delete(messageId);
|
|
991
|
+
reject(reason);
|
|
992
|
+
},
|
|
993
|
+
timeout
|
|
994
|
+
});
|
|
995
|
+
}).catch((error) => {
|
|
996
|
+
console.error("[Fluxbase Realtime] Acknowledgment error:", error);
|
|
997
|
+
return "error";
|
|
998
|
+
});
|
|
835
999
|
}
|
|
836
1000
|
return "ok";
|
|
837
1001
|
} catch (error) {
|
|
@@ -1010,7 +1174,14 @@ var RealtimeChannel = class {
|
|
|
1010
1174
|
}
|
|
1011
1175
|
break;
|
|
1012
1176
|
case "ack":
|
|
1013
|
-
|
|
1177
|
+
if (message.messageId && this.pendingAcks.has(message.messageId)) {
|
|
1178
|
+
const ackHandler = this.pendingAcks.get(message.messageId);
|
|
1179
|
+
if (ackHandler) {
|
|
1180
|
+
ackHandler.resolve(message.status || "ok");
|
|
1181
|
+
}
|
|
1182
|
+
} else {
|
|
1183
|
+
console.log("[Fluxbase Realtime] Acknowledged:", message);
|
|
1184
|
+
}
|
|
1014
1185
|
break;
|
|
1015
1186
|
case "error":
|
|
1016
1187
|
console.error("[Fluxbase Realtime] Error:", message.error);
|
|
@@ -3964,6 +4135,7 @@ var QueryBuilder = class {
|
|
|
3964
4135
|
this.orderBys = [];
|
|
3965
4136
|
this.singleRow = false;
|
|
3966
4137
|
this.maybeSingleRow = false;
|
|
4138
|
+
this.operationType = "select";
|
|
3967
4139
|
this.fetch = fetch2;
|
|
3968
4140
|
this.table = table;
|
|
3969
4141
|
}
|
|
@@ -3980,16 +4152,10 @@ var QueryBuilder = class {
|
|
|
3980
4152
|
/**
|
|
3981
4153
|
* Insert a single row or multiple rows
|
|
3982
4154
|
*/
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
return
|
|
3987
|
-
data: response,
|
|
3988
|
-
error: null,
|
|
3989
|
-
count: Array.isArray(data) ? data.length : 1,
|
|
3990
|
-
status: 201,
|
|
3991
|
-
statusText: "Created"
|
|
3992
|
-
};
|
|
4155
|
+
insert(data) {
|
|
4156
|
+
this.operationType = "insert";
|
|
4157
|
+
this.insertData = data;
|
|
4158
|
+
return this;
|
|
3993
4159
|
}
|
|
3994
4160
|
/**
|
|
3995
4161
|
* Upsert (insert or update) rows (Supabase-compatible)
|
|
@@ -4026,32 +4192,17 @@ var QueryBuilder = class {
|
|
|
4026
4192
|
/**
|
|
4027
4193
|
* Update rows matching the filters
|
|
4028
4194
|
*/
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
return {
|
|
4034
|
-
data: response,
|
|
4035
|
-
error: null,
|
|
4036
|
-
count: null,
|
|
4037
|
-
status: 200,
|
|
4038
|
-
statusText: "OK"
|
|
4039
|
-
};
|
|
4195
|
+
update(data) {
|
|
4196
|
+
this.operationType = "update";
|
|
4197
|
+
this.updateData = data;
|
|
4198
|
+
return this;
|
|
4040
4199
|
}
|
|
4041
4200
|
/**
|
|
4042
4201
|
* Delete rows matching the filters
|
|
4043
4202
|
*/
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
await this.fetch.delete(path);
|
|
4048
|
-
return {
|
|
4049
|
-
data: null,
|
|
4050
|
-
error: null,
|
|
4051
|
-
count: null,
|
|
4052
|
-
status: 204,
|
|
4053
|
-
statusText: "No Content"
|
|
4054
|
-
};
|
|
4203
|
+
delete() {
|
|
4204
|
+
this.operationType = "delete";
|
|
4205
|
+
return this;
|
|
4055
4206
|
}
|
|
4056
4207
|
/**
|
|
4057
4208
|
* Equal to
|
|
@@ -4430,13 +4581,13 @@ var QueryBuilder = class {
|
|
|
4430
4581
|
* { name: 'Alice', email: 'alice@example.com' },
|
|
4431
4582
|
* { name: 'Bob', email: 'bob@example.com' },
|
|
4432
4583
|
* { name: 'Charlie', email: 'charlie@example.com' }
|
|
4433
|
-
* ])
|
|
4584
|
+
* ])
|
|
4434
4585
|
* ```
|
|
4435
4586
|
*
|
|
4436
4587
|
* @category Batch Operations
|
|
4437
4588
|
*/
|
|
4438
4589
|
async insertMany(rows) {
|
|
4439
|
-
return this.insert(rows);
|
|
4590
|
+
return this.insert(rows).execute();
|
|
4440
4591
|
}
|
|
4441
4592
|
/**
|
|
4442
4593
|
* Update multiple rows matching the filters (batch update)
|
|
@@ -4453,19 +4604,17 @@ var QueryBuilder = class {
|
|
|
4453
4604
|
* const { data } = await client.from('products')
|
|
4454
4605
|
* .eq('category', 'electronics')
|
|
4455
4606
|
* .updateMany({ discount: 10, updated_at: new Date() })
|
|
4456
|
-
* .execute()
|
|
4457
4607
|
*
|
|
4458
4608
|
* // Mark all pending orders as processing
|
|
4459
4609
|
* const { data } = await client.from('orders')
|
|
4460
4610
|
* .eq('status', 'pending')
|
|
4461
4611
|
* .updateMany({ status: 'processing' })
|
|
4462
|
-
* .execute()
|
|
4463
4612
|
* ```
|
|
4464
4613
|
*
|
|
4465
4614
|
* @category Batch Operations
|
|
4466
4615
|
*/
|
|
4467
4616
|
async updateMany(data) {
|
|
4468
|
-
return this.update(data);
|
|
4617
|
+
return this.update(data).execute();
|
|
4469
4618
|
}
|
|
4470
4619
|
/**
|
|
4471
4620
|
* Delete multiple rows matching the filters (batch delete)
|
|
@@ -4481,27 +4630,66 @@ var QueryBuilder = class {
|
|
|
4481
4630
|
* await client.from('users')
|
|
4482
4631
|
* .eq('active', false)
|
|
4483
4632
|
* .deleteMany()
|
|
4484
|
-
* .execute()
|
|
4485
4633
|
*
|
|
4486
4634
|
* // Delete old logs
|
|
4487
4635
|
* await client.from('logs')
|
|
4488
4636
|
* .lt('created_at', '2024-01-01')
|
|
4489
4637
|
* .deleteMany()
|
|
4490
|
-
* .execute()
|
|
4491
4638
|
* ```
|
|
4492
4639
|
*
|
|
4493
4640
|
* @category Batch Operations
|
|
4494
4641
|
*/
|
|
4495
4642
|
async deleteMany() {
|
|
4496
|
-
return this.delete();
|
|
4643
|
+
return this.delete().execute();
|
|
4497
4644
|
}
|
|
4498
4645
|
/**
|
|
4499
4646
|
* Execute the query and return results
|
|
4500
4647
|
*/
|
|
4501
4648
|
async execute() {
|
|
4502
|
-
const queryString = this.buildQueryString();
|
|
4503
|
-
const path = `/api/v1/tables/${this.table}${queryString}`;
|
|
4504
4649
|
try {
|
|
4650
|
+
if (this.operationType === "insert") {
|
|
4651
|
+
if (!this.insertData) {
|
|
4652
|
+
throw new Error("Insert data is required for insert operation");
|
|
4653
|
+
}
|
|
4654
|
+
const body = Array.isArray(this.insertData) ? this.insertData : this.insertData;
|
|
4655
|
+
const response = await this.fetch.post(`/api/v1/tables/${this.table}`, body);
|
|
4656
|
+
return {
|
|
4657
|
+
data: response,
|
|
4658
|
+
error: null,
|
|
4659
|
+
count: Array.isArray(this.insertData) ? this.insertData.length : 1,
|
|
4660
|
+
status: 201,
|
|
4661
|
+
statusText: "Created"
|
|
4662
|
+
};
|
|
4663
|
+
}
|
|
4664
|
+
if (this.operationType === "update") {
|
|
4665
|
+
if (!this.updateData) {
|
|
4666
|
+
throw new Error("Update data is required for update operation");
|
|
4667
|
+
}
|
|
4668
|
+
const queryString2 = this.buildQueryString();
|
|
4669
|
+
const path2 = `/api/v1/tables/${this.table}${queryString2}`;
|
|
4670
|
+
const response = await this.fetch.patch(path2, this.updateData);
|
|
4671
|
+
return {
|
|
4672
|
+
data: response,
|
|
4673
|
+
error: null,
|
|
4674
|
+
count: null,
|
|
4675
|
+
status: 200,
|
|
4676
|
+
statusText: "OK"
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
if (this.operationType === "delete") {
|
|
4680
|
+
const queryString2 = this.buildQueryString();
|
|
4681
|
+
const path2 = `/api/v1/tables/${this.table}${queryString2}`;
|
|
4682
|
+
await this.fetch.delete(path2);
|
|
4683
|
+
return {
|
|
4684
|
+
data: null,
|
|
4685
|
+
error: null,
|
|
4686
|
+
count: null,
|
|
4687
|
+
status: 204,
|
|
4688
|
+
statusText: "No Content"
|
|
4689
|
+
};
|
|
4690
|
+
}
|
|
4691
|
+
const queryString = this.buildQueryString();
|
|
4692
|
+
const path = `/api/v1/tables/${this.table}${queryString}`;
|
|
4505
4693
|
const data = await this.fetch.get(path);
|
|
4506
4694
|
if (this.singleRow) {
|
|
4507
4695
|
if (Array.isArray(data) && data.length === 0) {
|