@zykeco/sync-server 0.3.0 → 0.5.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.
Files changed (43) hide show
  1. package/README.md +131 -0
  2. package/dist/db/schema.d.ts +1009 -0
  3. package/dist/db/schema.js +80 -1
  4. package/dist/db/schema.js.map +1 -1
  5. package/dist/index.js +25 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/routes/daily-stress-burden.d.ts +3 -0
  8. package/dist/routes/daily-stress-burden.js +24 -0
  9. package/dist/routes/daily-stress-burden.js.map +1 -0
  10. package/dist/routes/heart-rate-minute.d.ts +3 -0
  11. package/dist/routes/heart-rate-minute.js +24 -0
  12. package/dist/routes/heart-rate-minute.js.map +1 -0
  13. package/dist/routes/hr-zone-history.d.ts +3 -0
  14. package/dist/routes/hr-zone-history.js +24 -0
  15. package/dist/routes/hr-zone-history.js.map +1 -0
  16. package/dist/routes/user-profile.d.ts +3 -0
  17. package/dist/routes/user-profile.js +24 -0
  18. package/dist/routes/user-profile.js.map +1 -0
  19. package/dist/routes/user-timezones.d.ts +3 -0
  20. package/dist/routes/user-timezones.js +24 -0
  21. package/dist/routes/user-timezones.js.map +1 -0
  22. package/dist/routes/wipe.d.ts +6 -1
  23. package/dist/routes/wipe.js +16 -2
  24. package/dist/routes/wipe.js.map +1 -1
  25. package/dist/stores/daily-stress-burden.d.ts +3 -0
  26. package/dist/stores/daily-stress-burden.js +58 -0
  27. package/dist/stores/daily-stress-burden.js.map +1 -0
  28. package/dist/stores/heart-rate-minute.d.ts +3 -0
  29. package/dist/stores/heart-rate-minute.js +48 -0
  30. package/dist/stores/heart-rate-minute.js.map +1 -0
  31. package/dist/stores/hr-zone-history.d.ts +3 -0
  32. package/dist/stores/hr-zone-history.js +49 -0
  33. package/dist/stores/hr-zone-history.js.map +1 -0
  34. package/dist/stores/user-profile.d.ts +3 -0
  35. package/dist/stores/user-profile.js +76 -0
  36. package/dist/stores/user-profile.js.map +1 -0
  37. package/dist/stores/user-timezones.d.ts +3 -0
  38. package/dist/stores/user-timezones.js +43 -0
  39. package/dist/stores/user-timezones.js.map +1 -0
  40. package/drizzle/0001_sweet_firebird.sql +78 -0
  41. package/drizzle/meta/0001_snapshot.json +733 -0
  42. package/drizzle/meta/_journal.json +7 -0
  43. package/package.json +3 -3
@@ -0,0 +1,76 @@
1
+ import { inArray, sql } from 'drizzle-orm';
2
+ import { userProfile } from '../db/schema.js';
3
+ export function createUserProfileStore(db) {
4
+ return {
5
+ async upsertBatch(rows, _serverNowMs) {
6
+ if (rows.length === 0)
7
+ return [];
8
+ const values = rows.map((r) => ({
9
+ id: r.id,
10
+ name: r.name,
11
+ birthDate: r.birthDate,
12
+ height: r.height,
13
+ weight: r.weight,
14
+ biologicalGender: r.biologicalGender,
15
+ maxHeartRate: r.maxHeartRate,
16
+ maxHeartRateManually: r.maxHeartRateManually,
17
+ restingHeartRate: r.restingHeartRate,
18
+ vo2Max: r.vo2Max,
19
+ heartRateZonesManually: r.heartRateZonesManually,
20
+ hrZone1: r.hrZone1,
21
+ hrZone2: r.hrZone2,
22
+ hrZone3: r.hrZone3,
23
+ hrZone4: r.hrZone4,
24
+ hrZone5: r.hrZone5,
25
+ trainingBackground: r.trainingBackground,
26
+ typicalDay: r.typicalDay,
27
+ sleepGoalMinutes: r.sleepGoalMinutes,
28
+ dataRetentionLevel: r.dataRetentionLevel,
29
+ createdAt: r.createdAt,
30
+ updatedAt: r.updatedAt,
31
+ }));
32
+ await db
33
+ .insert(userProfile)
34
+ .values(values)
35
+ .onConflictDoUpdate({
36
+ target: userProfile.id,
37
+ set: {
38
+ name: sql `excluded.name`,
39
+ birthDate: sql `excluded.birth_date`,
40
+ height: sql `excluded.height`,
41
+ weight: sql `excluded.weight`,
42
+ biologicalGender: sql `excluded.biological_gender`,
43
+ maxHeartRate: sql `excluded.max_heart_rate`,
44
+ maxHeartRateManually: sql `excluded.max_heart_rate_manually`,
45
+ restingHeartRate: sql `excluded.resting_heart_rate`,
46
+ vo2Max: sql `excluded.vo2_max`,
47
+ heartRateZonesManually: sql `excluded.heart_rate_zones_manually`,
48
+ hrZone1: sql `excluded.hr_zone_1`,
49
+ hrZone2: sql `excluded.hr_zone_2`,
50
+ hrZone3: sql `excluded.hr_zone_3`,
51
+ hrZone4: sql `excluded.hr_zone_4`,
52
+ hrZone5: sql `excluded.hr_zone_5`,
53
+ trainingBackground: sql `excluded.training_background`,
54
+ typicalDay: sql `excluded.typical_day`,
55
+ sleepGoalMinutes: sql `excluded.sleep_goal_minutes`,
56
+ dataRetentionLevel: sql `excluded.data_retention_level`,
57
+ createdAt: sql `excluded.created_at`,
58
+ updatedAt: sql `excluded.updated_at`,
59
+ },
60
+ where: sql `excluded.updated_at > ${userProfile.updatedAt}`,
61
+ });
62
+ return rows.map((r) => r.id);
63
+ },
64
+ async deleteBatch(ids) {
65
+ if (ids.length === 0)
66
+ return [];
67
+ await db.delete(userProfile).where(inArray(userProfile.id, [...ids]));
68
+ return [...ids];
69
+ },
70
+ async wipeAll() {
71
+ const result = await db.delete(userProfile);
72
+ return result.rowsAffected;
73
+ },
74
+ };
75
+ }
76
+ //# sourceMappingURL=user-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-profile.js","sourceRoot":"","sources":["../../src/stores/user-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { UserTimezonesStore } from '@zykeco/server-core';
2
+ import type { Db } from '../db/client.js';
3
+ export declare function createUserTimezonesStore(db: Db): UserTimezonesStore;
@@ -0,0 +1,43 @@
1
+ import { inArray, sql } from 'drizzle-orm';
2
+ import { userTimezones } from '../db/schema.js';
3
+ export function createUserTimezonesStore(db) {
4
+ return {
5
+ async upsertBatch(rows, _serverNowMs) {
6
+ if (rows.length === 0)
7
+ return [];
8
+ const values = rows.map((r) => ({
9
+ id: r.id,
10
+ timezone: r.timezone,
11
+ type: r.type,
12
+ utcOffsetMinutes: r.utcOffsetMinutes,
13
+ effectiveFrom: r.effectiveFrom,
14
+ }));
15
+ await db
16
+ .insert(userTimezones)
17
+ .values(values)
18
+ .onConflictDoUpdate({
19
+ target: userTimezones.id,
20
+ set: {
21
+ timezone: sql `excluded.timezone`,
22
+ type: sql `excluded.type`,
23
+ utcOffsetMinutes: sql `excluded.utc_offset_minutes`,
24
+ effectiveFrom: sql `excluded.effective_from`,
25
+ },
26
+ // LWW on effectiveFrom (table is append-only; no updatedAt)
27
+ where: sql `excluded.effective_from > ${userTimezones.effectiveFrom}`,
28
+ });
29
+ return rows.map((r) => r.id);
30
+ },
31
+ async deleteBatch(ids) {
32
+ if (ids.length === 0)
33
+ return [];
34
+ await db.delete(userTimezones).where(inArray(userTimezones.id, [...ids]));
35
+ return [...ids];
36
+ },
37
+ async wipeAll() {
38
+ const result = await db.delete(userTimezones);
39
+ return result.rowsAffected;
40
+ },
41
+ };
42
+ }
43
+ //# sourceMappingURL=user-timezones.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-timezones.js","sourceRoot":"","sources":["../../src/stores/user-timezones.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,UAAU,wBAAwB,CAAC,EAAM;IAC7C,OAAO;QACL,KAAK,CAAC,WAAW,CAAC,IAAkC,EAAE,YAAoB;YACxE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9B,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,gBAAgB,EAAE,CAAC,CAAC,gBAAgB;gBACpC,aAAa,EAAE,CAAC,CAAC,aAAa;aAC/B,CAAC,CAAC,CAAC;YACJ,MAAM,EAAE;iBACL,MAAM,CAAC,aAAa,CAAC;iBACrB,MAAM,CAAC,MAAM,CAAC;iBACd,kBAAkB,CAAC;gBAClB,MAAM,EAAE,aAAa,CAAC,EAAE;gBACxB,GAAG,EAAE;oBACH,QAAQ,EAAE,GAAG,CAAA,mBAAmB;oBAChC,IAAI,EAAE,GAAG,CAAA,eAAe;oBACxB,gBAAgB,EAAE,GAAG,CAAA,6BAA6B;oBAClD,aAAa,EAAE,GAAG,CAAA,yBAAyB;iBAC5C;gBACD,4DAA4D;gBAC5D,KAAK,EAAE,GAAG,CAAA,6BAA6B,aAAa,CAAC,aAAa,EAAE;aACrE,CAAC,CAAC;YACL,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,GAAsB;YACtC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YAChC,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YAC1E,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,KAAK,CAAC,OAAO;YACX,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAC9C,OAAO,MAAM,CAAC,YAAY,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ CREATE TABLE `daily_stress_burden` (
2
+ `id` integer PRIMARY KEY NOT NULL,
3
+ `iso_date` text NOT NULL,
4
+ `timezone` text NOT NULL,
5
+ `intervals_relaxed` integer NOT NULL,
6
+ `intervals_balanced` integer NOT NULL,
7
+ `intervals_elevated` integer NOT NULL,
8
+ `intervals_high` integer NOT NULL,
9
+ `intervals_scored` integer NOT NULL,
10
+ `mean_score` real NOT NULL,
11
+ `peak_score` real NOT NULL,
12
+ `peak_score_utc` integer NOT NULL,
13
+ `stress_burden` real NOT NULL,
14
+ `updated_at` integer NOT NULL
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE UNIQUE INDEX `dsb_unique_date_idx` ON `daily_stress_burden` (`iso_date`);--> statement-breakpoint
18
+ CREATE TABLE `heart_rate_minute` (
19
+ `id` integer PRIMARY KEY NOT NULL,
20
+ `minute_start_utc` integer NOT NULL,
21
+ `bpm_mean` real NOT NULL,
22
+ `bpm_min` integer NOT NULL,
23
+ `bpm_max` integer NOT NULL,
24
+ `sample_count` integer NOT NULL,
25
+ `device_id` integer NOT NULL,
26
+ `updated_at` integer NOT NULL
27
+ );
28
+ --> statement-breakpoint
29
+ CREATE INDEX `hrm_minute_idx` ON `heart_rate_minute` (`minute_start_utc`);--> statement-breakpoint
30
+ CREATE UNIQUE INDEX `hrm_unique_minute_per_device_idx` ON `heart_rate_minute` (`minute_start_utc`,`device_id`);--> statement-breakpoint
31
+ CREATE INDEX `hrm_device_updated_idx` ON `heart_rate_minute` (`device_id`,`updated_at`);--> statement-breakpoint
32
+ CREATE TABLE `hr_zone_history` (
33
+ `id` integer PRIMARY KEY NOT NULL,
34
+ `effective_from_iso_date` text NOT NULL,
35
+ `zone1` integer NOT NULL,
36
+ `zone2` integer NOT NULL,
37
+ `zone3` integer NOT NULL,
38
+ `zone4` integer NOT NULL,
39
+ `zone5` integer NOT NULL,
40
+ `created_at` integer NOT NULL
41
+ );
42
+ --> statement-breakpoint
43
+ CREATE UNIQUE INDEX `hrzh_unique_effective_idx` ON `hr_zone_history` (`effective_from_iso_date`);--> statement-breakpoint
44
+ CREATE TABLE `user_profile` (
45
+ `id` integer PRIMARY KEY NOT NULL,
46
+ `name` text NOT NULL,
47
+ `birth_date` text NOT NULL,
48
+ `height` real NOT NULL,
49
+ `weight` real NOT NULL,
50
+ `biological_gender` text NOT NULL,
51
+ `max_heart_rate` integer,
52
+ `max_heart_rate_manually` integer,
53
+ `resting_heart_rate` integer,
54
+ `vo2_max` real,
55
+ `heart_rate_zones_manually` integer,
56
+ `hr_zone_1` integer,
57
+ `hr_zone_2` integer,
58
+ `hr_zone_3` integer,
59
+ `hr_zone_4` integer,
60
+ `hr_zone_5` integer,
61
+ `training_background` text,
62
+ `typical_day` text,
63
+ `sleep_goal_minutes` integer,
64
+ `data_retention_level` text,
65
+ `created_at` integer NOT NULL,
66
+ `updated_at` integer NOT NULL,
67
+ CONSTRAINT "user_profile_singleton" CHECK("user_profile"."id" = 1)
68
+ );
69
+ --> statement-breakpoint
70
+ CREATE TABLE `user_timezones` (
71
+ `id` integer PRIMARY KEY NOT NULL,
72
+ `timezone` text NOT NULL,
73
+ `type` text NOT NULL,
74
+ `utc_offset_minutes` integer NOT NULL,
75
+ `effective_from` integer NOT NULL
76
+ );
77
+ --> statement-breakpoint
78
+ CREATE INDEX `user_tz_effective_from_idx` ON `user_timezones` (`effective_from`);