@adventurelabs/scout-core 1.4.68 → 1.4.70

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.
@@ -0,0 +1,5 @@
1
+ import { Database } from "../types/supabase";
2
+ import { IHerdAllowedDomain } from "../types/db";
3
+ import { IWebResponseCompatible } from "../types/requests";
4
+ import { SupabaseClient } from "@supabase/supabase-js";
5
+ export declare function server_get_herd_allowed_domains(herd_id: number, supabaseClient?: SupabaseClient<Database>): Promise<IWebResponseCompatible<IHerdAllowedDomain[]>>;
@@ -0,0 +1,15 @@
1
+ "use server";
2
+ import { newServerClient } from "../supabase/server";
3
+ import { IWebResponse } from "../types/requests";
4
+ export async function server_get_herd_allowed_domains(herd_id, supabaseClient) {
5
+ const supabase = supabaseClient ?? (await newServerClient());
6
+ const { data, error } = await supabase
7
+ .from("herd_allowed_domains")
8
+ .select("*")
9
+ .eq("herd_id", herd_id)
10
+ .order("domain", { ascending: true });
11
+ if (error) {
12
+ return IWebResponse.error(error.message).to_compatible();
13
+ }
14
+ return IWebResponse.success(data ?? []).to_compatible();
15
+ }
@@ -7,5 +7,8 @@ export declare function get_herds(client: SupabaseClient<Database>): Promise<IWe
7
7
  export declare function get_herds_with_location(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IHerdPrettyLocation[]>>;
8
8
  export declare function get_herd_by_slug(slug: string): Promise<IWebResponseCompatible<IHerd>>;
9
9
  export declare function deleteHerd(herd_id: number): Promise<IWebResponseCompatible<boolean>>;
10
- export declare function createHerd(newHerd: any): Promise<IWebResponseCompatible<boolean>>;
10
+ export declare function createHerd(newHerd: Omit<IHerd, "id" | "inserted_at"> & {
11
+ id?: number;
12
+ inserted_at?: string;
13
+ }): Promise<IWebResponseCompatible<boolean>>;
11
14
  export declare function server_load_herd_modules(): Promise<IHerdModulesResponseWithStatus>;
@@ -66,11 +66,7 @@ export async function deleteHerd(herd_id) {
66
66
  }
67
67
  export async function createHerd(newHerd) {
68
68
  const supabase = await newServerClient();
69
- const user = await supabase.auth.getUser();
70
- const userId = user?.data?.user?.id;
71
- newHerd.created_by = userId;
72
- // strip id field from herd object
73
- const { data, error } = await supabase.from("herds").insert([newHerd]);
69
+ const { error } = await supabase.from("herds").insert([newHerd]);
74
70
  if (error) {
75
71
  return {
76
72
  status: EnumWebResponse.ERROR,
@@ -13,6 +13,7 @@ export * from "./health_metrics";
13
13
  export * from "./heartbeats";
14
14
  export * from "./herds";
15
15
  export * from "./location";
16
+ export * from "./lifecycle";
16
17
  export * from "./plans";
17
18
  export * from "./sessions";
18
19
  export * from "./segmentations_sam3";
@@ -30,4 +31,6 @@ export * from "./versions_software";
30
31
  export * from "./versions_software_server";
31
32
  export * from "./artifacts";
32
33
  export * from "./pins";
34
+ export * from "./pubsub_token";
35
+ export * from "./pubsub_token_server";
33
36
  export * from "./storagePath";
@@ -13,6 +13,7 @@ export * from "./health_metrics";
13
13
  export * from "./heartbeats";
14
14
  export * from "./herds";
15
15
  export * from "./location";
16
+ export * from "./lifecycle";
16
17
  export * from "./plans";
17
18
  export * from "./sessions";
18
19
  export * from "./segmentations_sam3";
@@ -30,4 +31,6 @@ export * from "./versions_software";
30
31
  export * from "./versions_software_server";
31
32
  export * from "./artifacts";
32
33
  export * from "./pins";
34
+ export * from "./pubsub_token";
35
+ export * from "./pubsub_token_server";
33
36
  export * from "./storagePath";
@@ -0,0 +1,8 @@
1
+ import { Database } from "../types/supabase";
2
+ import { IHerdInvitation, Role } from "../types/db";
3
+ import { IWebResponseCompatible } from "../types/requests";
4
+ import { SupabaseClient } from "@supabase/supabase-js";
5
+ export declare function server_create_herd_invitation(herd_id: number, email: string, role: Role, expires_at?: string | null): Promise<IWebResponseCompatible<IHerdInvitation | null>>;
6
+ export declare function accept_herd_invitations_for_current_user(invitation_ids: number[], supabaseClient?: SupabaseClient<Database>): Promise<IWebResponseCompatible<IHerdInvitation[]>>;
7
+ export declare function server_get_herd_invitations_for_current_user(): Promise<IWebResponseCompatible<IHerdInvitation[]>>;
8
+ export declare function server_get_herd_invitations_by_herd(herd_id: number, supabaseClient?: SupabaseClient<Database>): Promise<IWebResponseCompatible<IHerdInvitation[]>>;
@@ -0,0 +1,47 @@
1
+ "use server";
2
+ import { newServerClient } from "../supabase/server";
3
+ import { IWebResponse } from "../types/requests";
4
+ export async function server_create_herd_invitation(herd_id, email, role, expires_at) {
5
+ const supabase = await newServerClient();
6
+ const { data, error } = await supabase.rpc("create_herd_invitation", {
7
+ p_herd_id: herd_id,
8
+ p_email: email,
9
+ p_role: role,
10
+ p_expires_at: expires_at ?? undefined,
11
+ });
12
+ if (error) {
13
+ return IWebResponse.error(error.message).to_compatible();
14
+ }
15
+ return IWebResponse.success(data).to_compatible();
16
+ }
17
+ export async function accept_herd_invitations_for_current_user(invitation_ids, supabaseClient) {
18
+ if (invitation_ids.length === 0) {
19
+ return IWebResponse.error("At least one invitation id is required").to_compatible();
20
+ }
21
+ const supabase = supabaseClient ?? (await newServerClient());
22
+ const { data, error } = await supabase.rpc("accept_herd_invitations_for_current_user", { p_invitation_ids: invitation_ids });
23
+ if (error) {
24
+ return IWebResponse.error(error.message).to_compatible();
25
+ }
26
+ return IWebResponse.success(data ?? []).to_compatible();
27
+ }
28
+ export async function server_get_herd_invitations_for_current_user() {
29
+ const supabase = await newServerClient();
30
+ const { data, error } = await supabase.rpc("get_herd_invitations_for_current_user");
31
+ if (error) {
32
+ return IWebResponse.error(error.message).to_compatible();
33
+ }
34
+ return IWebResponse.success(data ?? []).to_compatible();
35
+ }
36
+ export async function server_get_herd_invitations_by_herd(herd_id, supabaseClient) {
37
+ const supabase = supabaseClient ?? (await newServerClient());
38
+ const { data, error } = await supabase
39
+ .from("herd_invitations")
40
+ .select("*")
41
+ .eq("herd_id", herd_id)
42
+ .order("created_at", { ascending: false });
43
+ if (error) {
44
+ return IWebResponse.error(error.message).to_compatible();
45
+ }
46
+ return IWebResponse.success(data ?? []).to_compatible();
47
+ }
@@ -0,0 +1,14 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { Database } from "../types/supabase";
3
+ import { EntityLifecycle, IArtifact, IDeviceRow, IEvent, IPart, ISession, IUserProfileRow } from "../types/db";
4
+ import { IWebResponseCompatible } from "../types/requests";
5
+ export interface LifecycleUpdateOptions {
6
+ lifecycle_reason?: string | null;
7
+ lifecycle_changed_by?: string | null;
8
+ }
9
+ export declare function update_user_lifecycle(client: SupabaseClient<Database>, user_id: string, lifecycle: EntityLifecycle, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IUserProfileRow | null>>;
10
+ export declare function update_device_lifecycle(client: SupabaseClient<Database>, device_id: number, lifecycle: EntityLifecycle, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IDeviceRow | null>>;
11
+ export declare function update_session_lifecycle(client: SupabaseClient<Database>, session_id: number, lifecycle: EntityLifecycle, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<ISession | null>>;
12
+ export declare function update_part_lifecycle(client: SupabaseClient<Database>, part_id: number, lifecycle: EntityLifecycle, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IPart | null>>;
13
+ export declare function update_event_lifecycle(client: SupabaseClient<Database>, event_id: number, lifecycle: EntityLifecycle, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IEvent | null>>;
14
+ export declare function update_artifact_lifecycle(client: SupabaseClient<Database>, artifact_id: number, lifecycle: EntityLifecycle, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IArtifact | null>>;
@@ -0,0 +1,74 @@
1
+ import { IWebResponse } from "../types/requests";
2
+ function build_lifecycle_patch(lifecycle, options) {
3
+ const patch = { lifecycle };
4
+ if (options && "lifecycle_reason" in options) {
5
+ patch.lifecycle_reason = options.lifecycle_reason ?? null;
6
+ }
7
+ if (options && "lifecycle_changed_by" in options) {
8
+ patch.lifecycle_changed_by = options.lifecycle_changed_by ?? null;
9
+ }
10
+ return patch;
11
+ }
12
+ function lifecycle_result(data, error, not_found_msg) {
13
+ if (error) {
14
+ return IWebResponse.error(error.message).to_compatible();
15
+ }
16
+ if (!data) {
17
+ return IWebResponse.error(not_found_msg).to_compatible();
18
+ }
19
+ return IWebResponse.success(data).to_compatible();
20
+ }
21
+ export async function update_user_lifecycle(client, user_id, lifecycle, options) {
22
+ const { data, error } = await client
23
+ .from("users")
24
+ .update(build_lifecycle_patch(lifecycle, options))
25
+ .eq("id", user_id)
26
+ .select("*")
27
+ .single();
28
+ return lifecycle_result(data, error, "User not found or lifecycle update failed");
29
+ }
30
+ export async function update_device_lifecycle(client, device_id, lifecycle, options) {
31
+ const { data, error } = await client
32
+ .from("devices")
33
+ .update(build_lifecycle_patch(lifecycle, options))
34
+ .eq("id", device_id)
35
+ .select("*")
36
+ .single();
37
+ return lifecycle_result(data, error, "Device not found or lifecycle update failed");
38
+ }
39
+ export async function update_session_lifecycle(client, session_id, lifecycle, options) {
40
+ const { data, error } = await client
41
+ .from("sessions")
42
+ .update(build_lifecycle_patch(lifecycle, options))
43
+ .eq("id", session_id)
44
+ .select("*")
45
+ .single();
46
+ return lifecycle_result(data, error, "Session not found or lifecycle update failed");
47
+ }
48
+ export async function update_part_lifecycle(client, part_id, lifecycle, options) {
49
+ const { data, error } = await client
50
+ .from("parts")
51
+ .update(build_lifecycle_patch(lifecycle, options))
52
+ .eq("id", part_id)
53
+ .select("*")
54
+ .single();
55
+ return lifecycle_result(data, error, "Part not found or lifecycle update failed");
56
+ }
57
+ export async function update_event_lifecycle(client, event_id, lifecycle, options) {
58
+ const { data, error } = await client
59
+ .from("events")
60
+ .update(build_lifecycle_patch(lifecycle, options))
61
+ .eq("id", event_id)
62
+ .select("*")
63
+ .single();
64
+ return lifecycle_result(data, error, "Event not found or lifecycle update failed");
65
+ }
66
+ export async function update_artifact_lifecycle(client, artifact_id, lifecycle, options) {
67
+ const { data, error } = await client
68
+ .from("artifacts")
69
+ .update(build_lifecycle_patch(lifecycle, options))
70
+ .eq("id", artifact_id)
71
+ .select("*")
72
+ .single();
73
+ return lifecycle_result(data, error, "Artifact not found or lifecycle update failed");
74
+ }
@@ -1,7 +1,8 @@
1
1
  import { Database } from "../types/supabase";
2
- import { IPart, PartInsert } from "../types/db";
2
+ import { EntityLifecycle, IPart, PartInsert } from "../types/db";
3
3
  import { IWebResponseCompatible } from "../types/requests";
4
4
  import { SupabaseClient } from "@supabase/supabase-js";
5
+ import { LifecycleUpdateOptions } from "./lifecycle";
5
6
  /**
6
7
  * Retrieves all active parts for a specific device
7
8
  * @param client - Supabase client instance
@@ -27,11 +28,13 @@ export declare function get_parts_by_serial_number(client: SupabaseClient<Databa
27
28
  */
28
29
  export declare function get_parts_by_product_number(client: SupabaseClient<Database>, product_number: string): Promise<IWebResponseCompatible<IPart[]>>;
29
30
  /**
30
- * Retrieves all active parts with a specific status
31
+ * Retrieves all parts with a specific lifecycle state
31
32
  * @param client - Supabase client instance
32
- * @param status - Component status to filter by
33
+ * @param lifecycle - Lifecycle state to filter by
33
34
  */
34
- export declare function get_parts_by_status(client: SupabaseClient<Database>, status: Database["public"]["Enums"]["component_status"]): Promise<IWebResponseCompatible<IPart[]>>;
35
+ export declare function get_parts_by_lifecycle(client: SupabaseClient<Database>, lifecycle: EntityLifecycle): Promise<IWebResponseCompatible<IPart[]>>;
36
+ /** @deprecated Use get_parts_by_lifecycle */
37
+ export declare const get_parts_by_status: typeof get_parts_by_lifecycle;
35
38
  /**
36
39
  * Creates a new part with validation
37
40
  * @param client - Supabase client instance
@@ -46,18 +49,20 @@ export declare function create_part(client: SupabaseClient<Database>, newPart: P
46
49
  */
47
50
  export declare function update_part(client: SupabaseClient<Database>, part_id: number, updatedPart: Partial<PartInsert>): Promise<IWebResponseCompatible<IPart | null>>;
48
51
  /**
49
- * Soft deletes a part by setting deleted_at timestamp
52
+ * Marks an active part as deleted (lifecycle)
50
53
  * @param client - Supabase client instance
51
54
  * @param part_id - ID of the part to delete
55
+ * @param options - Optional lifecycle metadata
52
56
  */
53
- export declare function delete_part(client: SupabaseClient<Database>, part_id: number): Promise<IWebResponseCompatible<IPart | null>>;
57
+ export declare function delete_part(client: SupabaseClient<Database>, part_id: number, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IPart | null>>;
54
58
  /**
55
- * Updates the status of a specific part
59
+ * Updates the lifecycle of a specific part
56
60
  * @param client - Supabase client instance
57
61
  * @param part_id - ID of the part to update
58
- * @param status - New status to set
62
+ * @param lifecycle - New lifecycle state
63
+ * @param options - Optional lifecycle metadata
59
64
  */
60
- export declare function update_part_status(client: SupabaseClient<Database>, part_id: number, status: Database["public"]["Enums"]["component_status"]): Promise<IWebResponseCompatible<IPart | null>>;
65
+ export declare function update_part_status(client: SupabaseClient<Database>, part_id: number, lifecycle: EntityLifecycle, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IPart | null>>;
61
66
  /**
62
67
  * Retrieves all active parts associated with a certificate
63
68
  * @param client - Supabase client instance
@@ -71,19 +76,20 @@ export declare function get_parts_by_certificate_id(client: SupabaseClient<Datab
71
76
  */
72
77
  export declare function get_parts_by_herd_id(client: SupabaseClient<Database>, herd_id: number): Promise<IWebResponseCompatible<IPart[]>>;
73
78
  /**
74
- * Restores a soft-deleted part by clearing deleted_at timestamp
79
+ * Restores a deleted part to active lifecycle
75
80
  * @param client - Supabase client instance
76
81
  * @param part_id - ID of the part to restore
82
+ * @param options - Optional lifecycle metadata
77
83
  */
78
- export declare function restore_part(client: SupabaseClient<Database>, part_id: number): Promise<IWebResponseCompatible<IPart | null>>;
84
+ export declare function restore_part(client: SupabaseClient<Database>, part_id: number, options?: LifecycleUpdateOptions): Promise<IWebResponseCompatible<IPart | null>>;
79
85
  /**
80
- * Permanently deletes a soft-deleted part from database
86
+ * Permanently deletes a part that is already in deleted lifecycle
81
87
  * @param client - Supabase client instance
82
88
  * @param part_id - ID of the part to permanently delete
83
89
  */
84
90
  export declare function hard_delete_part(client: SupabaseClient<Database>, part_id: number): Promise<IWebResponseCompatible<IPart | null>>;
85
91
  /**
86
- * Retrieves all soft-deleted parts for a specific device
92
+ * Retrieves all deleted parts for a specific device
87
93
  * @param client - Supabase client instance
88
94
  * @param device_id - ID of the device to get deleted parts for
89
95
  */
@@ -1,4 +1,5 @@
1
1
  import { IWebResponse } from "../types/requests";
2
+ import { update_part_lifecycle } from "./lifecycle";
2
3
  /**
3
4
  * Retrieves all active parts for a specific device
4
5
  * @param client - Supabase client instance
@@ -9,7 +10,7 @@ export async function get_parts_by_device_id(client, device_id) {
9
10
  .from("parts")
10
11
  .select("*")
11
12
  .eq("device_id", device_id)
12
- .is("deleted_at", null)
13
+ .eq("lifecycle", "active")
13
14
  .order("created_at", { ascending: false });
14
15
  if (error) {
15
16
  return IWebResponse.error(error.message).to_compatible();
@@ -29,7 +30,7 @@ export async function get_part_by_id(client, part_id) {
29
30
  .from("parts")
30
31
  .select("*")
31
32
  .eq("id", part_id)
32
- .is("deleted_at", null)
33
+ .eq("lifecycle", "active")
33
34
  .single();
34
35
  if (error) {
35
36
  return IWebResponse.error(error.message).to_compatible();
@@ -49,7 +50,7 @@ export async function get_parts_by_serial_number(client, serial_number) {
49
50
  .from("parts")
50
51
  .select("*")
51
52
  .eq("serial_number", serial_number)
52
- .is("deleted_at", null)
53
+ .eq("lifecycle", "active")
53
54
  .order("created_at", { ascending: false });
54
55
  if (error) {
55
56
  return IWebResponse.error(error.message).to_compatible();
@@ -69,7 +70,7 @@ export async function get_parts_by_product_number(client, product_number) {
69
70
  .from("parts")
70
71
  .select("*")
71
72
  .eq("product_number", product_number)
72
- .is("deleted_at", null)
73
+ .eq("lifecycle", "active")
73
74
  .order("created_at", { ascending: false });
74
75
  if (error) {
75
76
  return IWebResponse.error(error.message).to_compatible();
@@ -80,25 +81,26 @@ export async function get_parts_by_product_number(client, product_number) {
80
81
  return IWebResponse.success(data).to_compatible();
81
82
  }
82
83
  /**
83
- * Retrieves all active parts with a specific status
84
+ * Retrieves all parts with a specific lifecycle state
84
85
  * @param client - Supabase client instance
85
- * @param status - Component status to filter by
86
+ * @param lifecycle - Lifecycle state to filter by
86
87
  */
87
- export async function get_parts_by_status(client, status) {
88
+ export async function get_parts_by_lifecycle(client, lifecycle) {
88
89
  const { data, error } = await client
89
90
  .from("parts")
90
91
  .select("*")
91
- .eq("status", status)
92
- .is("deleted_at", null)
92
+ .eq("lifecycle", lifecycle)
93
93
  .order("created_at", { ascending: false });
94
94
  if (error) {
95
95
  return IWebResponse.error(error.message).to_compatible();
96
96
  }
97
97
  if (!data) {
98
- return IWebResponse.error(`No parts found with status: ${status}`).to_compatible();
98
+ return IWebResponse.error(`No parts found with lifecycle: ${lifecycle}`).to_compatible();
99
99
  }
100
100
  return IWebResponse.success(data).to_compatible();
101
101
  }
102
+ /** @deprecated Use get_parts_by_lifecycle */
103
+ export const get_parts_by_status = get_parts_by_lifecycle;
102
104
  /**
103
105
  * Creates a new part with validation
104
106
  * @param client - Supabase client instance
@@ -154,17 +156,25 @@ export async function update_part(client, part_id, updatedPart) {
154
156
  return IWebResponse.success(data).to_compatible();
155
157
  }
156
158
  /**
157
- * Soft deletes a part by setting deleted_at timestamp
159
+ * Marks an active part as deleted (lifecycle)
158
160
  * @param client - Supabase client instance
159
161
  * @param part_id - ID of the part to delete
162
+ * @param options - Optional lifecycle metadata
160
163
  */
161
- export async function delete_part(client, part_id) {
162
- // Soft delete by setting deleted_at timestamp
164
+ export async function delete_part(client, part_id, options) {
163
165
  const { data, error } = await client
164
166
  .from("parts")
165
- .update({ deleted_at: new Date().toISOString() })
167
+ .update({
168
+ lifecycle: "deleted",
169
+ ...(options?.lifecycle_reason !== undefined && {
170
+ lifecycle_reason: options.lifecycle_reason,
171
+ }),
172
+ ...(options?.lifecycle_changed_by !== undefined && {
173
+ lifecycle_changed_by: options.lifecycle_changed_by,
174
+ }),
175
+ })
166
176
  .eq("id", part_id)
167
- .is("deleted_at", null)
177
+ .eq("lifecycle", "active")
168
178
  .select("*")
169
179
  .single();
170
180
  if (error) {
@@ -176,26 +186,14 @@ export async function delete_part(client, part_id) {
176
186
  return IWebResponse.success(data).to_compatible();
177
187
  }
178
188
  /**
179
- * Updates the status of a specific part
189
+ * Updates the lifecycle of a specific part
180
190
  * @param client - Supabase client instance
181
191
  * @param part_id - ID of the part to update
182
- * @param status - New status to set
192
+ * @param lifecycle - New lifecycle state
193
+ * @param options - Optional lifecycle metadata
183
194
  */
184
- export async function update_part_status(client, part_id, status) {
185
- const { data, error } = await client
186
- .from("parts")
187
- .update({ status })
188
- .eq("id", part_id)
189
- .is("deleted_at", null)
190
- .select("*")
191
- .single();
192
- if (error) {
193
- return IWebResponse.error(error.message).to_compatible();
194
- }
195
- if (!data) {
196
- return IWebResponse.error("Part not found or status update failed").to_compatible();
197
- }
198
- return IWebResponse.success(data).to_compatible();
195
+ export async function update_part_status(client, part_id, lifecycle, options) {
196
+ return update_part_lifecycle(client, part_id, lifecycle, options);
199
197
  }
200
198
  /**
201
199
  * Retrieves all active parts associated with a certificate
@@ -207,7 +205,7 @@ export async function get_parts_by_certificate_id(client, certificate_id) {
207
205
  .from("parts")
208
206
  .select("*")
209
207
  .eq("certificate_id", certificate_id)
210
- .is("deleted_at", null)
208
+ .eq("lifecycle", "active")
211
209
  .order("created_at", { ascending: false });
212
210
  if (error) {
213
211
  return IWebResponse.error(error.message).to_compatible();
@@ -230,7 +228,7 @@ export async function get_parts_by_herd_id(client, herd_id) {
230
228
  devices!parts_device_id_fkey(herd_id)
231
229
  `)
232
230
  .eq("devices.herd_id", herd_id)
233
- .is("deleted_at", null)
231
+ .eq("lifecycle", "active")
234
232
  .order("created_at", { ascending: false });
235
233
  if (error) {
236
234
  return IWebResponse.error(error.message).to_compatible();
@@ -241,17 +239,25 @@ export async function get_parts_by_herd_id(client, herd_id) {
241
239
  return IWebResponse.success(data).to_compatible();
242
240
  }
243
241
  /**
244
- * Restores a soft-deleted part by clearing deleted_at timestamp
242
+ * Restores a deleted part to active lifecycle
245
243
  * @param client - Supabase client instance
246
244
  * @param part_id - ID of the part to restore
245
+ * @param options - Optional lifecycle metadata
247
246
  */
248
- export async function restore_part(client, part_id) {
249
- // Restore soft deleted part by setting deleted_at to null
247
+ export async function restore_part(client, part_id, options) {
250
248
  const { data, error } = await client
251
249
  .from("parts")
252
- .update({ deleted_at: null })
250
+ .update({
251
+ lifecycle: "active",
252
+ ...(options?.lifecycle_reason !== undefined && {
253
+ lifecycle_reason: options.lifecycle_reason,
254
+ }),
255
+ ...(options?.lifecycle_changed_by !== undefined && {
256
+ lifecycle_changed_by: options.lifecycle_changed_by,
257
+ }),
258
+ })
253
259
  .eq("id", part_id)
254
- .not("deleted_at", "is", null)
260
+ .eq("lifecycle", "deleted")
255
261
  .select("*")
256
262
  .single();
257
263
  if (error) {
@@ -263,17 +269,16 @@ export async function restore_part(client, part_id) {
263
269
  return IWebResponse.success(data).to_compatible();
264
270
  }
265
271
  /**
266
- * Permanently deletes a soft-deleted part from database
272
+ * Permanently deletes a part that is already in deleted lifecycle
267
273
  * @param client - Supabase client instance
268
274
  * @param part_id - ID of the part to permanently delete
269
275
  */
270
276
  export async function hard_delete_part(client, part_id) {
271
- // Permanently delete the part (only use for already soft-deleted parts)
272
277
  const { data, error } = await client
273
278
  .from("parts")
274
279
  .delete()
275
280
  .eq("id", part_id)
276
- .not("deleted_at", "is", null)
281
+ .eq("lifecycle", "deleted")
277
282
  .select("*")
278
283
  .single();
279
284
  if (error) {
@@ -285,7 +290,7 @@ export async function hard_delete_part(client, part_id) {
285
290
  return IWebResponse.success(data).to_compatible();
286
291
  }
287
292
  /**
288
- * Retrieves all soft-deleted parts for a specific device
293
+ * Retrieves all deleted parts for a specific device
289
294
  * @param client - Supabase client instance
290
295
  * @param device_id - ID of the device to get deleted parts for
291
296
  */
@@ -294,8 +299,8 @@ export async function get_deleted_parts_by_device_id(client, device_id) {
294
299
  .from("parts")
295
300
  .select("*")
296
301
  .eq("device_id", device_id)
297
- .not("deleted_at", "is", null)
298
- .order("deleted_at", { ascending: false });
302
+ .eq("lifecycle", "deleted")
303
+ .order("lifecycle_changed_at", { ascending: false });
299
304
  if (error) {
300
305
  return IWebResponse.error(error.message).to_compatible();
301
306
  }
@@ -311,13 +316,12 @@ export async function get_deleted_parts_by_device_id(client, device_id) {
311
316
  * @param serial_number - Serial number to search for
312
317
  */
313
318
  export async function get_parts_by_product_and_serial(client, product_number, serial_number) {
314
- // Get part by the composite unique constraint
315
319
  const { data, error } = await client
316
320
  .from("parts")
317
321
  .select("*")
318
322
  .eq("product_number", product_number)
319
323
  .eq("serial_number", serial_number)
320
- .is("deleted_at", null)
324
+ .eq("lifecycle", "active")
321
325
  .single();
322
326
  if (error) {
323
327
  return IWebResponse.error(error.message).to_compatible();
@@ -0,0 +1,5 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { Database } from "../types/supabase";
3
+ import { IPubsubTokenMint } from "../types/pubsub_token";
4
+ import { IWebResponseCompatible } from "../types/requests";
5
+ export declare function mint_pubsub_token(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IPubsubTokenMint>>;
@@ -0,0 +1,14 @@
1
+ import { IWebResponse } from "../types/requests";
2
+ export async function mint_pubsub_token(client) {
3
+ const { data, error } = await client.functions.invoke("mint-pubsub-token", {
4
+ method: "POST",
5
+ });
6
+ if (error) {
7
+ return IWebResponse.error(error.message).to_compatible();
8
+ }
9
+ const mint = data;
10
+ if (!mint?.token) {
11
+ return IWebResponse.error("mint-pubsub-token returned no token").to_compatible();
12
+ }
13
+ return IWebResponse.success(mint).to_compatible();
14
+ }
@@ -0,0 +1,7 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { Database } from "../types/supabase";
3
+ import { IPubsubTokenMint } from "../types/pubsub_token";
4
+ import { IWebResponseCompatible } from "../types/requests";
5
+ export declare function server_mint_pubsub_token(options?: {
6
+ client?: SupabaseClient<Database>;
7
+ }): Promise<IWebResponseCompatible<IPubsubTokenMint>>;
@@ -0,0 +1,7 @@
1
+ "use server";
2
+ import { newServerClient } from "../supabase/server";
3
+ import { mint_pubsub_token } from "./pubsub_token";
4
+ export async function server_mint_pubsub_token(options) {
5
+ const client = options?.client ?? (await newServerClient());
6
+ return mint_pubsub_token(client);
7
+ }
@@ -1,7 +1,10 @@
1
- import { IUser, IUserAndRole, IUserRolePerHerd, Role } from "../types/db";
1
+ import { IUser, IUserAndRole, IUserProfileRow, IUserRolePerHerd, Role } from "../types/db";
2
2
  import { IWebResponseCompatible } from "../types/requests";
3
3
  import { SupabaseClient } from "@supabase/supabase-js";
4
+ import { Database } from "../types/supabase";
4
5
  export declare function server_get_user_roles(herd_id: number): Promise<IWebResponseCompatible<IUserRolePerHerd[]>>;
5
6
  export declare function server_get_user(): Promise<IWebResponseCompatible<IUser | null>>;
6
7
  export declare function server_get_users_with_herd_access(herd_id: number, supabaseClient?: SupabaseClient): Promise<IWebResponseCompatible<IUserAndRole[]>>;
7
8
  export declare function server_upsert_user_with_role(herd_id: number, username: string, role: Role): Promise<IWebResponseCompatible<IUserAndRole | null>>;
9
+ export type UserProfileUpdate = Database["public"]["Tables"]["users"]["Update"];
10
+ export declare function update_user_profile(client: SupabaseClient<Database>, user_id: string, patch: UserProfileUpdate): Promise<IWebResponseCompatible<IUserProfileRow | null>>;
@@ -74,3 +74,18 @@ export async function server_upsert_user_with_role(herd_id, username, role) {
74
74
  }
75
75
  return IWebResponse.success({ user, role }).to_compatible();
76
76
  }
77
+ export async function update_user_profile(client, user_id, patch) {
78
+ const { data, error } = await client
79
+ .from("users")
80
+ .update(patch)
81
+ .eq("id", user_id)
82
+ .select("*")
83
+ .single();
84
+ if (error) {
85
+ return IWebResponse.error(error.message).to_compatible();
86
+ }
87
+ if (!data) {
88
+ return IWebResponse.error("User not found or profile update failed").to_compatible();
89
+ }
90
+ return IWebResponse.success(data).to_compatible();
91
+ }