@molecule/api-wearable-oura 1.0.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/LICENSE ADDED
@@ -0,0 +1,115 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work.
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object
40
+ form, that is based on (or derived from) the Work and for which the
41
+ editorial revisions, annotations, elaborations, or other modifications
42
+ represent, as a whole, an original work of authorship.
43
+
44
+ "Contribution" shall mean any work of authorship, including the
45
+ original version of the Work and any modifications or additions
46
+ to that Work, that is intentionally submitted to the Licensor for
47
+ inclusion in the Work by the copyright owner or by an individual or
48
+ Legal Entity authorized to submit on behalf of the copyright owner.
49
+
50
+ "Contributor" shall mean Licensor and any individual or Legal Entity
51
+ on behalf of whom a Contribution has been received by the Licensor and
52
+ subsequently incorporated within the Work.
53
+
54
+ 2. Grant of Copyright License. Subject to the terms and conditions of
55
+ this License, each Contributor hereby grants to You a perpetual,
56
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
57
+ copyright license to reproduce, prepare Derivative Works of,
58
+ publicly display, publicly perform, sublicense, and distribute the
59
+ Work and such Derivative Works in Source or Object form.
60
+
61
+ 3. Grant of Patent License. Subject to the terms and conditions of
62
+ this License, each Contributor hereby grants to You a perpetual,
63
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
64
+ patent license to make, have made, use, offer to sell, sell, import,
65
+ and otherwise transfer the Work.
66
+
67
+ 4. Redistribution. You may reproduce and distribute copies of the
68
+ Work or Derivative Works thereof in any medium, with or without
69
+ modifications, and in Source or Object form, provided that You
70
+ meet the following conditions:
71
+
72
+ (a) You must give any other recipients of the Work or
73
+ Derivative Works a copy of this License; and
74
+
75
+ (b) You must cause any modified files to carry prominent notices
76
+ stating that You changed the files; and
77
+
78
+ (c) You must retain, in the Source form of any Derivative Works
79
+ that You distribute, all copyright, patent, trademark, and
80
+ attribution notices from the Source form of the Work,
81
+ excluding those notices that do not pertain to any part of
82
+ the Derivative Works; and
83
+
84
+ (d) If the Work includes a "NOTICE" text file as part of its
85
+ distribution, then any Derivative Works that You distribute must
86
+ include a readable copy of the attribution notices contained
87
+ within such NOTICE file.
88
+
89
+ 5. Submission of Contributions.
90
+
91
+ 6. Trademarks. This License does not grant permission to use the trade
92
+ names, trademarks, service marks, or product names of the Licensor.
93
+
94
+ 7. Disclaimer of Warranty. Unless required by applicable law or
95
+ agreed to in writing, Licensor provides the Work on an "AS IS" BASIS,
96
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
97
+
98
+ 8. Limitation of Liability. In no event and under no legal theory shall
99
+ any Contributor be liable to You for damages.
100
+
101
+ 9. Accepting Warranty or Additional Liability.
102
+
103
+ Copyright 2026 Molecule Dev, Inc.
104
+
105
+ Licensed under the Apache License, Version 2.0 (the "License");
106
+ you may not use this file except in compliance with the License.
107
+ You may obtain a copy of the License at
108
+
109
+ http://www.apache.org/licenses/LICENSE-2.0
110
+
111
+ Unless required by applicable law or agreed to in writing, software
112
+ distributed under the License is distributed on an "AS IS" BASIS,
113
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
114
+ See the License for the specific language governing permissions and
115
+ limitations under the License.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=browser-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-guard.d.ts","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAA"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Browser guard — `@molecule/api-wearable-oura` is SERVER-ONLY.
3
+ *
4
+ * Generated by scripts/gen-browser-guards.mjs (workspace root) — edit THAT, not this.
5
+ * Evaluating a server package in a browser bundle is always an import-graph mistake
6
+ * (node APIs, secrets); without this guard it surfaces as a cryptic downstream crash
7
+ * ("Buffer is not defined") far from the culprit. Throwing here names the package and
8
+ * the fix at the exact moment the client bundle evaluates it. jsdom tests and SSR are
9
+ * unaffected: the throw requires browser globals AND the absence of a node runtime.
10
+ */
11
+ const g = globalThis;
12
+ if (g.window !== undefined && g.document !== undefined && !g.process?.versions?.node) {
13
+ throw new Error('@molecule/api-wearable-oura is SERVER-ONLY: it was bundled into browser/client code. Import it only ' +
14
+ 'from server code (a server route/function or your API), or dynamic-import it inside ' +
15
+ 'the server handler — never from components or shared client modules, and never ' +
16
+ 'polyfill Buffer/process to silence this.');
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=browser-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-guard.js","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,GAAG,UAIT,CAAA;AACD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrF,MAAM,IAAI,KAAK,CACb,sGAAsG;QACpG,sFAAsF;QACtF,iFAAiF;QACjF,0CAA0C,CAC7C,CAAA;AACH,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Oura Cloud API v2 bond for `@molecule/api-wearable`.
3
+ *
4
+ * Implements daily activity, sleep, and heart-rate ingestion against the
5
+ * Oura Cloud API v2 using OAuth 2.0 with refresh-token rotation. Wires
6
+ * under the `wearable` named-multi-provider category as `'oura'`.
7
+ *
8
+ * Oura does not track body weight, so {@link createProvider} returns
9
+ * `[]` from `getWeight()` — pair Oura with another wearable bond
10
+ * (e.g. `@molecule/api-wearable-fitbit` or `-withings`) when weight
11
+ * data is required.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { setProvider } from '@molecule/api-wearable'
16
+ * import { createProvider, PROVIDER_NAME } from '@molecule/api-wearable-oura'
17
+ *
18
+ * const oura = createProvider({
19
+ * redirectUri: 'https://app.example.com/auth/oura/callback',
20
+ * credentialsStore: myCredentialsStore,
21
+ * })
22
+ *
23
+ * setProvider(PROVIDER_NAME, oura)
24
+ * ```
25
+ *
26
+ * @module
27
+ */
28
+ export * from './browser-guard.js';
29
+ export * from './provider.js';
30
+ export * from './secrets.js';
31
+ export * from './types.js';
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Oura Cloud API v2 bond for `@molecule/api-wearable`.
3
+ *
4
+ * Implements daily activity, sleep, and heart-rate ingestion against the
5
+ * Oura Cloud API v2 using OAuth 2.0 with refresh-token rotation. Wires
6
+ * under the `wearable` named-multi-provider category as `'oura'`.
7
+ *
8
+ * Oura does not track body weight, so {@link createProvider} returns
9
+ * `[]` from `getWeight()` — pair Oura with another wearable bond
10
+ * (e.g. `@molecule/api-wearable-fitbit` or `-withings`) when weight
11
+ * data is required.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { setProvider } from '@molecule/api-wearable'
16
+ * import { createProvider, PROVIDER_NAME } from '@molecule/api-wearable-oura'
17
+ *
18
+ * const oura = createProvider({
19
+ * redirectUri: 'https://app.example.com/auth/oura/callback',
20
+ * credentialsStore: myCredentialsStore,
21
+ * })
22
+ *
23
+ * setProvider(PROVIDER_NAME, oura)
24
+ * ```
25
+ *
26
+ * @module
27
+ */
28
+ export * from './browser-guard.js';
29
+ export * from './provider.js';
30
+ export * from './secrets.js';
31
+ export * from './types.js';
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Oura Cloud API v2 provider implementation.
3
+ *
4
+ * Wraps the Oura Cloud API v2 behind the
5
+ * {@link import('@molecule/api-wearable').WearableProvider} contract.
6
+ * Each call accepts a host-app `userId`, looks up the persisted
7
+ * {@link import('@molecule/api-wearable').UserConnection} via the bond's
8
+ * {@link import('@molecule/api-wearable').WearableCredentialsStore}, and
9
+ * transparently refreshes the access token on `401` (or proactively when
10
+ * `expiresAt` is in the past). Refreshed tokens are written back to the
11
+ * store before any operation returns.
12
+ *
13
+ * Security invariants (enforced by tests):
14
+ * - Access tokens, refresh tokens, `Authorization` / `Basic` headers,
15
+ * and OAuth `error_description` payloads are NEVER thrown, logged,
16
+ * or surfaced in error messages.
17
+ * - `disconnect` always removes the local record, even if the upstream
18
+ * revoke call fails — leaving a record around is worse than a
19
+ * stranded provider-side token.
20
+ * - `getWeight` returns `[]` — Oura does not track body weight. The
21
+ * bond preserves the `WearableProvider` contract by returning an
22
+ * empty array rather than a non-deterministic error.
23
+ *
24
+ * @module
25
+ */
26
+ import './secrets.js';
27
+ import type { DailyActivity, HeartRateSummary, SleepSession, SleepStage, SleepStageSegment, WearableDate } from '@molecule/api-wearable';
28
+ import type { OuraProvider, OuraProviderOptions } from './types.js';
29
+ /** Stable provider key used in the credentials store and bond name. */
30
+ export declare const PROVIDER_NAME = "oura";
31
+ interface OuraDailyActivityEntry {
32
+ day: string;
33
+ steps?: number;
34
+ equivalent_walking_distance?: number;
35
+ total_calories?: number;
36
+ active_calories?: number;
37
+ high_activity_time?: number;
38
+ medium_activity_time?: number;
39
+ low_activity_time?: number;
40
+ inactivity_alerts?: number;
41
+ resting_time?: number;
42
+ meters_to_target?: number;
43
+ }
44
+ interface OuraSleepEntry {
45
+ id: string;
46
+ day: string;
47
+ bedtime_start: string;
48
+ bedtime_end: string;
49
+ /** Sleep type — `long_sleep` is the user's main nightly sleep. */
50
+ type?: string;
51
+ total_sleep_duration?: number;
52
+ time_in_bed?: number;
53
+ awake_time?: number;
54
+ light_sleep_duration?: number;
55
+ deep_sleep_duration?: number;
56
+ rem_sleep_duration?: number;
57
+ restless_periods?: number;
58
+ efficiency?: number;
59
+ /**
60
+ * Hypnogram string: each character is one 5-minute segment, with
61
+ * `1=deep`, `2=light`, `3=rem`, `4=awake`.
62
+ */
63
+ sleep_phase_5_min?: string;
64
+ }
65
+ interface OuraSleepResponse {
66
+ data?: OuraSleepEntry[];
67
+ }
68
+ interface OuraHeartRateSample {
69
+ bpm: number;
70
+ source?: string;
71
+ timestamp: string;
72
+ }
73
+ interface OuraHeartRateResponse {
74
+ data?: OuraHeartRateSample[];
75
+ }
76
+ /**
77
+ * Base64url-encodes the bytes (RFC 4648 §5 with `=` padding stripped).
78
+ *
79
+ * @param bytes - The bytes to encode.
80
+ * @returns A base64url string.
81
+ */
82
+ export declare const base64UrlEncode: (bytes: Uint8Array) => string;
83
+ /**
84
+ * Translates an Oura `usercollection/daily_activity` entry into the
85
+ * normalized {@link DailyActivity} shape. Missing fields are
86
+ * zero-defaulted so handlers can sum across days without null guards.
87
+ *
88
+ * @param date - The calendar day requested.
89
+ * @param entry - Oura's daily-activity response entry, or `undefined`.
90
+ * @returns A normalized daily activity record.
91
+ */
92
+ export declare const fromOuraActivity: (date: WearableDate, entry: OuraDailyActivityEntry | undefined) => DailyActivity;
93
+ /**
94
+ * Maps an Oura `sleep_phase_5_min` digit onto our normalized
95
+ * {@link SleepStage} taxonomy. Oura uses `1=deep`, `2=light`, `3=rem`,
96
+ * `4=awake`; anything else falls through to `unknown`.
97
+ *
98
+ * @param digit - The single hypnogram character.
99
+ * @returns A normalized sleep stage.
100
+ */
101
+ export declare const mapSleepPhaseDigit: (digit: string) => SleepStage;
102
+ /**
103
+ * Decodes Oura's `sleep_phase_5_min` hypnogram into a list of contiguous
104
+ * {@link SleepStageSegment} records anchored at `bedtimeStart`.
105
+ *
106
+ * Each character represents a 5-minute window. Adjacent same-stage
107
+ * windows are coalesced into a single segment so consumers don't have
108
+ * to do the bookkeeping.
109
+ *
110
+ * @param hypnogram - The Oura hypnogram string.
111
+ * @param bedtimeStart - ISO 8601 timestamp anchoring segment 0.
112
+ * @returns Decoded sleep segments, or `undefined` when no hypnogram exists.
113
+ */
114
+ export declare const decodeOuraHypnogram: (hypnogram: string | undefined, bedtimeStart: string) => SleepStageSegment[] | undefined;
115
+ /**
116
+ * Translates an Oura `usercollection/sleep` payload into a list of
117
+ * normalized {@link SleepSession} records ordered by start ascending.
118
+ *
119
+ * Oura's `type === 'long_sleep'` corresponds to the user's main nightly
120
+ * sleep — the bond marks any matching session as `isMainSleep: true`,
121
+ * with naps and short sessions as `false`.
122
+ *
123
+ * @param date - Calendar day requested.
124
+ * @param raw - Oura's sleep response payload.
125
+ * @returns Normalized sleep sessions.
126
+ */
127
+ export declare const fromOuraSleep: (date: WearableDate, raw: OuraSleepResponse) => SleepSession[];
128
+ /**
129
+ * Aggregates an Oura `usercollection/heartrate` payload into the
130
+ * normalized {@link HeartRateSummary} shape.
131
+ *
132
+ * Oura returns per-sample BPM readings rather than a daily resting-HR
133
+ * value or zone breakdown; the bond derives a coarse resting-HR estimate
134
+ * from the minimum sample of the day. Zone data is not reported by this
135
+ * endpoint, so `zones` is left undefined.
136
+ *
137
+ * @param date - Calendar day the response was requested for.
138
+ * @param raw - Oura's heart-rate response payload.
139
+ * @returns A normalized heart-rate summary.
140
+ */
141
+ export declare const fromOuraHeartRate: (date: WearableDate, raw: OuraHeartRateResponse) => HeartRateSummary;
142
+ /**
143
+ * Creates an Oura wearable provider.
144
+ *
145
+ * @param options - Required: `redirectUri` + `credentialsStore`. Falls
146
+ * back to `OAUTH_OURA_CLIENT_ID` / `OAUTH_OURA_CLIENT_SECRET` env
147
+ * vars when `clientId` / `clientSecret` are omitted.
148
+ * @returns An Oura-flavored {@link OuraProvider}.
149
+ */
150
+ export declare const createProvider: (options: OuraProviderOptions) => OuraProvider;
151
+ export {};
152
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAOH,OAAO,cAAc,CAAA;AAKrB,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,iBAAiB,EAIjB,YAAY,EAGb,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAsB,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEvF,uEAAuE;AACvE,eAAO,MAAM,aAAa,SAAS,CAAA;AA0BnC,UAAU,sBAAsB;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAMD,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,UAAU,iBAAiB;IACzB,IAAI,CAAC,EAAE,cAAc,EAAE,CAAA;CACxB;AAED,UAAU,mBAAmB;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,qBAAqB;IAC7B,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAA;CAC7B;AAwCD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,UAAU,KAAG,MAMnD,CAAA;AAcD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,YAAY,EAClB,OAAO,sBAAsB,GAAG,SAAS,KACxC,aAoBF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,KAAG,UAalD,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAC9B,WAAW,MAAM,GAAG,SAAS,EAC7B,cAAc,MAAM,KACnB,iBAAiB,EAAE,GAAG,SAyBxB,CAAA;AAqBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,YAAY,EAAE,KAAK,iBAAiB,KAAG,YAAY,EAsBtF,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,YAAY,EAClB,KAAK,qBAAqB,KACzB,gBAcF,CAAA;AAMD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,mBAAmB,KAAG,YAqU7D,CAAA"}
@@ -0,0 +1,556 @@
1
+ /**
2
+ * Oura Cloud API v2 provider implementation.
3
+ *
4
+ * Wraps the Oura Cloud API v2 behind the
5
+ * {@link import('@molecule/api-wearable').WearableProvider} contract.
6
+ * Each call accepts a host-app `userId`, looks up the persisted
7
+ * {@link import('@molecule/api-wearable').UserConnection} via the bond's
8
+ * {@link import('@molecule/api-wearable').WearableCredentialsStore}, and
9
+ * transparently refreshes the access token on `401` (or proactively when
10
+ * `expiresAt` is in the past). Refreshed tokens are written back to the
11
+ * store before any operation returns.
12
+ *
13
+ * Security invariants (enforced by tests):
14
+ * - Access tokens, refresh tokens, `Authorization` / `Basic` headers,
15
+ * and OAuth `error_description` payloads are NEVER thrown, logged,
16
+ * or surfaced in error messages.
17
+ * - `disconnect` always removes the local record, even if the upstream
18
+ * revoke call fails — leaving a record around is worse than a
19
+ * stranded provider-side token.
20
+ * - `getWeight` returns `[]` — Oura does not track body weight. The
21
+ * bond preserves the `WearableProvider` contract by returning an
22
+ * empty array rather than a non-deterministic error.
23
+ *
24
+ * @module
25
+ */
26
+ import { randomBytes as nodeRandomBytes } from 'node:crypto';
27
+ // Side-effect import: registers this bond's secret definitions so the
28
+ // runtime registry is populated even when provider.js is imported directly
29
+ // (not through the package barrel).
30
+ import './secrets.js';
31
+ import { getLogger } from '@molecule/api-bond';
32
+ import { get, post } from '@molecule/api-http';
33
+ /** Stable provider key used in the credentials store and bond name. */
34
+ export const PROVIDER_NAME = 'oura';
35
+ const DEFAULT_API_BASE_URL = 'https://api.ouraring.com/v2';
36
+ const DEFAULT_AUTHORIZE_URL = 'https://cloud.ouraring.com/oauth/authorize';
37
+ const DEFAULT_TOKEN_URL = 'https://api.ouraring.com/oauth/token';
38
+ const DEFAULT_REVOKE_URL = 'https://api.ouraring.com/oauth/revoke';
39
+ const DEFAULT_TIMEOUT_MS = 15_000;
40
+ const DEFAULT_SCOPES = ['daily', 'heartrate', 'personal', 'session'];
41
+ // Oura's `sleep_phase_5_min` hypnogram uses 5-minute segments per
42
+ // character. RFC link: https://cloud.ouraring.com/v2/docs#operation/sleep_route_v2
43
+ const SLEEP_PHASE_SEGMENT_SECONDS = 300;
44
+ /* -------------------------------------------------------------------------- *
45
+ * Error sanitization.
46
+ * -------------------------------------------------------------------------- */
47
+ /**
48
+ * Sanitizes anything thrown by `@molecule/api-http` (or downstream
49
+ * fetch errors) into a token-free Error. The original error's `message`
50
+ * may contain `Authorization: Bearer …` headers or echoed
51
+ * `error_description` strings that include the access/refresh token.
52
+ *
53
+ * @param scope - Short label of the calling operation (e.g. `"connect"`).
54
+ * @param error - The original error.
55
+ * @returns A new Error safe to throw and log.
56
+ */
57
+ const sanitizeError = (scope, error) => {
58
+ const httpError = error;
59
+ const status = httpError?.response?.status;
60
+ const code = httpError?.code;
61
+ const parts = [`Oura ${scope} failed`];
62
+ if (status !== undefined)
63
+ parts.push(`status=${status}`);
64
+ if (code)
65
+ parts.push(`code=${code}`);
66
+ return new Error(parts.join(' '));
67
+ };
68
+ /**
69
+ * Whether an HTTP error indicates an expired/invalid access token.
70
+ *
71
+ * @param error - The original error.
72
+ * @returns True when the response status is 401.
73
+ */
74
+ const isUnauthorized = (error) => {
75
+ return error?.response?.status === 401;
76
+ };
77
+ /* -------------------------------------------------------------------------- *
78
+ * Encoding helpers.
79
+ * -------------------------------------------------------------------------- */
80
+ /**
81
+ * Base64url-encodes the bytes (RFC 4648 §5 with `=` padding stripped).
82
+ *
83
+ * @param bytes - The bytes to encode.
84
+ * @returns A base64url string.
85
+ */
86
+ export const base64UrlEncode = (bytes) => {
87
+ return Buffer.from(bytes)
88
+ .toString('base64')
89
+ .replace(/\+/g, '-')
90
+ .replace(/\//g, '_')
91
+ .replace(/=+$/, '');
92
+ };
93
+ /* -------------------------------------------------------------------------- *
94
+ * Normalization helpers.
95
+ * -------------------------------------------------------------------------- */
96
+ const totalActiveMinutes = (entry) => {
97
+ // Oura returns activity-time fields in seconds.
98
+ const high = entry.high_activity_time ?? 0;
99
+ const medium = entry.medium_activity_time ?? 0;
100
+ const low = entry.low_activity_time ?? 0;
101
+ return Math.round((high + medium + low) / 60);
102
+ };
103
+ /**
104
+ * Translates an Oura `usercollection/daily_activity` entry into the
105
+ * normalized {@link DailyActivity} shape. Missing fields are
106
+ * zero-defaulted so handlers can sum across days without null guards.
107
+ *
108
+ * @param date - The calendar day requested.
109
+ * @param entry - Oura's daily-activity response entry, or `undefined`.
110
+ * @returns A normalized daily activity record.
111
+ */
112
+ export const fromOuraActivity = (date, entry) => {
113
+ if (!entry) {
114
+ return {
115
+ date,
116
+ steps: 0,
117
+ distanceMeters: 0,
118
+ caloriesOut: 0,
119
+ activeMinutes: 0,
120
+ };
121
+ }
122
+ return {
123
+ date: entry.day || date,
124
+ steps: entry.steps ?? 0,
125
+ // `equivalent_walking_distance` is reported in meters.
126
+ distanceMeters: entry.equivalent_walking_distance ?? 0,
127
+ // `total_calories` is the most-comparable Fitbit `caloriesOut` analog
128
+ // (active + BMR equivalent).
129
+ caloriesOut: entry.total_calories ?? 0,
130
+ activeMinutes: totalActiveMinutes(entry),
131
+ };
132
+ };
133
+ /**
134
+ * Maps an Oura `sleep_phase_5_min` digit onto our normalized
135
+ * {@link SleepStage} taxonomy. Oura uses `1=deep`, `2=light`, `3=rem`,
136
+ * `4=awake`; anything else falls through to `unknown`.
137
+ *
138
+ * @param digit - The single hypnogram character.
139
+ * @returns A normalized sleep stage.
140
+ */
141
+ export const mapSleepPhaseDigit = (digit) => {
142
+ switch (digit) {
143
+ case '1':
144
+ return 'deep';
145
+ case '2':
146
+ return 'light';
147
+ case '3':
148
+ return 'rem';
149
+ case '4':
150
+ return 'awake';
151
+ default:
152
+ return 'unknown';
153
+ }
154
+ };
155
+ /**
156
+ * Decodes Oura's `sleep_phase_5_min` hypnogram into a list of contiguous
157
+ * {@link SleepStageSegment} records anchored at `bedtimeStart`.
158
+ *
159
+ * Each character represents a 5-minute window. Adjacent same-stage
160
+ * windows are coalesced into a single segment so consumers don't have
161
+ * to do the bookkeeping.
162
+ *
163
+ * @param hypnogram - The Oura hypnogram string.
164
+ * @param bedtimeStart - ISO 8601 timestamp anchoring segment 0.
165
+ * @returns Decoded sleep segments, or `undefined` when no hypnogram exists.
166
+ */
167
+ export const decodeOuraHypnogram = (hypnogram, bedtimeStart) => {
168
+ if (!hypnogram || hypnogram.length === 0)
169
+ return undefined;
170
+ const anchor = new Date(bedtimeStart).getTime();
171
+ if (Number.isNaN(anchor))
172
+ return undefined;
173
+ const segments = [];
174
+ let runStage = null;
175
+ let runStartIndex = 0;
176
+ for (let i = 0; i <= hypnogram.length; i += 1) {
177
+ const stage = i < hypnogram.length ? mapSleepPhaseDigit(hypnogram[i]) : null;
178
+ if (stage !== runStage) {
179
+ if (runStage !== null) {
180
+ const startMs = anchor + runStartIndex * SLEEP_PHASE_SEGMENT_SECONDS * 1000;
181
+ const endMs = anchor + i * SLEEP_PHASE_SEGMENT_SECONDS * 1000;
182
+ segments.push({
183
+ stage: runStage,
184
+ start: new Date(startMs).toISOString(),
185
+ end: new Date(endMs).toISOString(),
186
+ durationSeconds: (i - runStartIndex) * SLEEP_PHASE_SEGMENT_SECONDS,
187
+ });
188
+ }
189
+ runStage = stage;
190
+ runStartIndex = i;
191
+ }
192
+ }
193
+ return segments;
194
+ };
195
+ const stageSummaryFromOura = (entry) => {
196
+ const out = {};
197
+ // Oura returns stage durations in seconds.
198
+ if (typeof entry.awake_time === 'number') {
199
+ out.awakeMinutes = Math.round(entry.awake_time / 60);
200
+ }
201
+ if (typeof entry.light_sleep_duration === 'number') {
202
+ out.lightMinutes = Math.round(entry.light_sleep_duration / 60);
203
+ }
204
+ if (typeof entry.deep_sleep_duration === 'number') {
205
+ out.deepMinutes = Math.round(entry.deep_sleep_duration / 60);
206
+ }
207
+ if (typeof entry.rem_sleep_duration === 'number') {
208
+ out.remMinutes = Math.round(entry.rem_sleep_duration / 60);
209
+ }
210
+ if (Object.keys(out).length === 0)
211
+ return undefined;
212
+ return out;
213
+ };
214
+ /**
215
+ * Translates an Oura `usercollection/sleep` payload into a list of
216
+ * normalized {@link SleepSession} records ordered by start ascending.
217
+ *
218
+ * Oura's `type === 'long_sleep'` corresponds to the user's main nightly
219
+ * sleep — the bond marks any matching session as `isMainSleep: true`,
220
+ * with naps and short sessions as `false`.
221
+ *
222
+ * @param date - Calendar day requested.
223
+ * @param raw - Oura's sleep response payload.
224
+ * @returns Normalized sleep sessions.
225
+ */
226
+ export const fromOuraSleep = (date, raw) => {
227
+ const entries = raw.data ?? [];
228
+ return entries
229
+ .map((entry) => {
230
+ const totalSleepSeconds = entry.total_sleep_duration ?? 0;
231
+ const timeInBedSeconds = entry.time_in_bed ?? totalSleepSeconds;
232
+ const segments = decodeOuraHypnogram(entry.sleep_phase_5_min, entry.bedtime_start);
233
+ return {
234
+ id: String(entry.id),
235
+ date: entry.day || date,
236
+ start: entry.bedtime_start,
237
+ end: entry.bedtime_end,
238
+ timeInBedMinutes: Math.round(timeInBedSeconds / 60),
239
+ timeAsleepMinutes: Math.round(totalSleepSeconds / 60),
240
+ // Oura returns efficiency as 0-100 already.
241
+ efficiency: entry.efficiency,
242
+ isMainSleep: entry.type === 'long_sleep',
243
+ stageSummary: stageSummaryFromOura(entry),
244
+ segments,
245
+ };
246
+ })
247
+ .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
248
+ };
249
+ /**
250
+ * Aggregates an Oura `usercollection/heartrate` payload into the
251
+ * normalized {@link HeartRateSummary} shape.
252
+ *
253
+ * Oura returns per-sample BPM readings rather than a daily resting-HR
254
+ * value or zone breakdown; the bond derives a coarse resting-HR estimate
255
+ * from the minimum sample of the day. Zone data is not reported by this
256
+ * endpoint, so `zones` is left undefined.
257
+ *
258
+ * @param date - Calendar day the response was requested for.
259
+ * @param raw - Oura's heart-rate response payload.
260
+ * @returns A normalized heart-rate summary.
261
+ */
262
+ export const fromOuraHeartRate = (date, raw) => {
263
+ const samples = raw.data ?? [];
264
+ if (samples.length === 0) {
265
+ return { date, restingHeartRate: undefined, zones: undefined };
266
+ }
267
+ let min = Number.POSITIVE_INFINITY;
268
+ for (const s of samples) {
269
+ if (typeof s.bpm === 'number' && s.bpm > 0 && s.bpm < min)
270
+ min = s.bpm;
271
+ }
272
+ return {
273
+ date,
274
+ restingHeartRate: Number.isFinite(min) ? min : undefined,
275
+ zones: undefined,
276
+ };
277
+ };
278
+ /* -------------------------------------------------------------------------- *
279
+ * Provider factory.
280
+ * -------------------------------------------------------------------------- */
281
+ /**
282
+ * Creates an Oura wearable provider.
283
+ *
284
+ * @param options - Required: `redirectUri` + `credentialsStore`. Falls
285
+ * back to `OAUTH_OURA_CLIENT_ID` / `OAUTH_OURA_CLIENT_SECRET` env
286
+ * vars when `clientId` / `clientSecret` are omitted.
287
+ * @returns An Oura-flavored {@link OuraProvider}.
288
+ */
289
+ export const createProvider = (options) => {
290
+ if (!options.redirectUri) {
291
+ throw new Error('Oura bond is missing required option: redirectUri');
292
+ }
293
+ if (!options.credentialsStore) {
294
+ throw new Error('Oura bond is missing required option: credentialsStore');
295
+ }
296
+ const apiBaseUrl = options.apiBaseUrl ?? DEFAULT_API_BASE_URL;
297
+ const authorizeUrl = options.authorizeUrl ?? DEFAULT_AUTHORIZE_URL;
298
+ const tokenUrl = options.tokenUrl ?? DEFAULT_TOKEN_URL;
299
+ const revokeUrl = options.revokeUrl ?? DEFAULT_REVOKE_URL;
300
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
301
+ const scopes = options.scopes ?? DEFAULT_SCOPES;
302
+ const credentialsStore = options.credentialsStore;
303
+ const randomBytes = options.randomBytes ?? ((size) => new Uint8Array(nodeRandomBytes(size)));
304
+ const now = options.now ?? Date.now;
305
+ const logger = getLogger();
306
+ const getClientId = () => {
307
+ const id = options.clientId ?? process.env.OAUTH_OURA_CLIENT_ID;
308
+ if (!id) {
309
+ throw new Error('Oura bond is missing OAUTH_OURA_CLIENT_ID (or clientId option).');
310
+ }
311
+ return id;
312
+ };
313
+ const getClientSecret = () => {
314
+ const secret = options.clientSecret ?? process.env.OAUTH_OURA_CLIENT_SECRET;
315
+ if (!secret) {
316
+ throw new Error('Oura bond is missing OAUTH_OURA_CLIENT_SECRET (or clientSecret option).');
317
+ }
318
+ return secret;
319
+ };
320
+ /**
321
+ * Builds Authorization headers for the OAuth token endpoint. Oura
322
+ * accepts client_secret_basic (Basic auth) for both authorization-code
323
+ * and refresh-token grants.
324
+ *
325
+ * @returns Headers for the token call.
326
+ */
327
+ const tokenAuthHeaders = () => {
328
+ const clientId = getClientId();
329
+ const clientSecret = getClientSecret();
330
+ const basic = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
331
+ return {
332
+ 'content-type': 'application/x-www-form-urlencoded',
333
+ accept: 'application/json',
334
+ authorization: `Basic ${basic}`,
335
+ };
336
+ };
337
+ const formEncode = (params) => Object.entries(params)
338
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
339
+ .join('&');
340
+ const callTokenEndpoint = async (params, scope) => {
341
+ const headers = tokenAuthHeaders();
342
+ const body = formEncode(params);
343
+ try {
344
+ const response = await post(tokenUrl, body, {
345
+ headers,
346
+ timeout: timeoutMs,
347
+ });
348
+ const data = response.data;
349
+ if (!data?.access_token || !data?.refresh_token) {
350
+ // Do NOT include any field of `data` in the error — Oura may
351
+ // echo tokens back in `error_description` on some failures.
352
+ throw new Error(`Oura ${scope} failed: malformed token response`);
353
+ }
354
+ return data;
355
+ }
356
+ catch (error) {
357
+ if (error instanceof Error && error.message.startsWith(`Oura ${scope} failed:`)) {
358
+ throw error;
359
+ }
360
+ throw sanitizeError(scope, error);
361
+ }
362
+ };
363
+ const refreshAccessToken = async (connection) => {
364
+ const tokenResponse = await callTokenEndpoint({
365
+ grant_type: 'refresh_token',
366
+ refresh_token: connection.refreshToken,
367
+ }, 'token refresh');
368
+ const refreshed = {
369
+ ...connection,
370
+ accessToken: tokenResponse.access_token,
371
+ refreshToken: tokenResponse.refresh_token,
372
+ expiresAt: typeof tokenResponse.expires_in === 'number'
373
+ ? now() + tokenResponse.expires_in * 1000
374
+ : undefined,
375
+ scopes: tokenResponse.scope
376
+ ? tokenResponse.scope.split(' ').filter(Boolean)
377
+ : connection.scopes,
378
+ };
379
+ await credentialsStore.write(PROVIDER_NAME, refreshed);
380
+ return refreshed;
381
+ };
382
+ const loadConnection = async (userId) => {
383
+ const connection = await credentialsStore.read(userId, PROVIDER_NAME);
384
+ if (!connection) {
385
+ throw new Error(`Oura connection not found for user '${userId}'`);
386
+ }
387
+ return connection;
388
+ };
389
+ /**
390
+ * Calls a function with the user's access token, refreshing the token
391
+ * once on a 401 response (or proactively when `expiresAt` is in the past).
392
+ *
393
+ * @param scope - Calling-operation label for sanitized error messages.
394
+ * @param userId - Host-app user id.
395
+ * @param call - Function performing the HTTP request given an access token.
396
+ * @returns The HTTP response body.
397
+ */
398
+ const withAuth = async (scope, userId, call) => {
399
+ let connection = await loadConnection(userId);
400
+ let refreshed = false;
401
+ if (typeof connection.expiresAt === 'number' && connection.expiresAt <= now()) {
402
+ connection = await refreshAccessToken(connection);
403
+ refreshed = true;
404
+ }
405
+ try {
406
+ const response = await call(connection.accessToken);
407
+ return response.data;
408
+ }
409
+ catch (error) {
410
+ if (!isUnauthorized(error) || refreshed) {
411
+ logger.error(`Oura ${scope} failed`, sanitizeError(scope, error));
412
+ throw sanitizeError(scope, error);
413
+ }
414
+ try {
415
+ connection = await refreshAccessToken(connection);
416
+ const response = await call(connection.accessToken);
417
+ return response.data;
418
+ }
419
+ catch (retryError) {
420
+ if (retryError instanceof Error && retryError.message.startsWith('Oura bond is missing ')) {
421
+ logger.error(`Oura ${scope} failed`, retryError);
422
+ throw retryError;
423
+ }
424
+ logger.error(`Oura ${scope} failed`, sanitizeError(scope, retryError));
425
+ throw sanitizeError(scope, retryError);
426
+ }
427
+ }
428
+ };
429
+ const authedRequestOptions = (accessToken, extra) => ({
430
+ ...extra,
431
+ headers: {
432
+ accept: 'application/json',
433
+ ...(extra?.headers ?? {}),
434
+ authorization: `Bearer ${accessToken}`,
435
+ },
436
+ timeout: extra?.timeout ?? timeoutMs,
437
+ });
438
+ const buildAuthorizeUrl = (state) => {
439
+ const params = {
440
+ client_id: getClientId(),
441
+ response_type: 'code',
442
+ redirect_uri: options.redirectUri,
443
+ scope: scopes.join(' '),
444
+ state,
445
+ };
446
+ const qs = formEncode(params);
447
+ return authorizeUrl.includes('?') ? `${authorizeUrl}&${qs}` : `${authorizeUrl}?${qs}`;
448
+ };
449
+ const startAuthorize = () => {
450
+ const state = base64UrlEncode(randomBytes(16));
451
+ return { url: buildAuthorizeUrl(state), state };
452
+ };
453
+ /**
454
+ * Performs the authorization-code → token exchange and persists the
455
+ * resulting connection.
456
+ *
457
+ * @param userId - Host-app user id.
458
+ * @param code - OAuth authorization code from the redirect callback.
459
+ * @returns The newly-persisted user connection.
460
+ */
461
+ const connectInternal = async (userId, code) => {
462
+ const tokenResponse = await callTokenEndpoint({
463
+ grant_type: 'authorization_code',
464
+ code,
465
+ redirect_uri: options.redirectUri,
466
+ }, 'connect');
467
+ const connection = {
468
+ userId,
469
+ // Oura's token endpoint does not return a stable user id; the host
470
+ // can fetch `usercollection/personal_info` separately if it needs
471
+ // one. For now we leave `providerAccountId` empty.
472
+ providerAccountId: '',
473
+ accessToken: tokenResponse.access_token,
474
+ refreshToken: tokenResponse.refresh_token,
475
+ expiresAt: typeof tokenResponse.expires_in === 'number'
476
+ ? now() + tokenResponse.expires_in * 1000
477
+ : undefined,
478
+ scopes: tokenResponse.scope ? tokenResponse.scope.split(' ').filter(Boolean) : undefined,
479
+ connectedAt: now(),
480
+ };
481
+ await credentialsStore.write(PROVIDER_NAME, connection);
482
+ return connection;
483
+ };
484
+ return {
485
+ providerName: PROVIDER_NAME,
486
+ startAuthorize,
487
+ async connect(userId, code) {
488
+ return connectInternal(userId, code);
489
+ },
490
+ async refreshConnection(userId) {
491
+ const connection = await loadConnection(userId);
492
+ try {
493
+ return await refreshAccessToken(connection);
494
+ }
495
+ catch (error) {
496
+ if (error instanceof Error && error.message.startsWith('Oura ')) {
497
+ throw error;
498
+ }
499
+ throw sanitizeError('refresh', error);
500
+ }
501
+ },
502
+ async disconnect(userId) {
503
+ let connection = null;
504
+ try {
505
+ connection = await credentialsStore.read(userId, PROVIDER_NAME);
506
+ }
507
+ catch (error) {
508
+ logger.error('Oura disconnect read failed', sanitizeError('disconnect', error));
509
+ }
510
+ if (connection) {
511
+ try {
512
+ // Oura's revoke endpoint accepts the access token in the body.
513
+ const headers = tokenAuthHeaders();
514
+ const body = formEncode({ access_token: connection.accessToken });
515
+ await post(revokeUrl, body, { headers, timeout: timeoutMs });
516
+ }
517
+ catch (error) {
518
+ // Best-effort revoke — the local record still must be removed
519
+ // below. Sanitize so the access token never leaks.
520
+ logger.error('Oura revoke failed', sanitizeError('disconnect', error));
521
+ }
522
+ }
523
+ await credentialsStore.remove(userId, PROVIDER_NAME);
524
+ },
525
+ async getDailyActivity(userId, date) {
526
+ const data = await withAuth('getDailyActivity', userId, (accessToken) => get(`${apiBaseUrl}/usercollection/daily_activity?start_date=${encodeURIComponent(date)}&end_date=${encodeURIComponent(date)}`, authedRequestOptions(accessToken)));
527
+ const entry = (data.data ?? []).find((d) => d.day === date) ?? data.data?.[0];
528
+ return fromOuraActivity(date, entry);
529
+ },
530
+ async getDailySleep(userId, date) {
531
+ const data = await withAuth('getDailySleep', userId, (accessToken) => get(`${apiBaseUrl}/usercollection/sleep?start_date=${encodeURIComponent(date)}&end_date=${encodeURIComponent(date)}`, authedRequestOptions(accessToken)));
532
+ return fromOuraSleep(date, data);
533
+ },
534
+ async getDailyHeartRate(userId, date) {
535
+ // Oura's heart-rate endpoint takes ISO-8601 datetimes, not calendar
536
+ // days. We bucket the requested calendar day in UTC; hosts that need
537
+ // local-day buckets can wire their own range.
538
+ const startDatetime = `${date}T00:00:00+00:00`;
539
+ const endDatetime = `${date}T23:59:59+00:00`;
540
+ const data = await withAuth('getDailyHeartRate', userId, (accessToken) => get(`${apiBaseUrl}/usercollection/heartrate?start_datetime=${encodeURIComponent(startDatetime)}&end_datetime=${encodeURIComponent(endDatetime)}`, authedRequestOptions(accessToken)));
541
+ return fromOuraHeartRate(date, data);
542
+ },
543
+ async getWeight(_userId, _range) {
544
+ // Oura does NOT track body weight — there is no equivalent of
545
+ // Fitbit's `body/log/weight` endpoint. Returning an empty array
546
+ // preserves the WearableProvider contract; consumers can detect
547
+ // the absence by checking `result.length === 0` and falling back
548
+ // to a different bond (e.g. `@molecule/api-wearable-fitbit` or
549
+ // `-withings`) for weight readings.
550
+ void _userId;
551
+ void _range;
552
+ return [];
553
+ },
554
+ };
555
+ };
556
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Oura wearable bond secret definitions — self-registered at import time so the
3
+ * runtime secrets registry (`@molecule/api-secrets`) can drive boot-time
4
+ * configuration reports and actionable "not configured" errors.
5
+ *
6
+ * Content is derived MECHANICALLY from this package's mlcl registry secrets
7
+ * entry (label/instructions/setupUrl/example) via the fleet formula, so
8
+ * packages sharing a key register byte-identical definitions and
9
+ * registration order never matters.
10
+ *
11
+ * @module
12
+ */
13
+ import type { SecretDefinition } from '@molecule/api-secrets';
14
+ /** Secret definitions required by the Oura wearable bond. */
15
+ export declare const wearableOuraSecretDefinitions: SecretDefinition[];
16
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,6DAA6D;AAC7D,eAAO,MAAM,6BAA6B,EAAE,gBAAgB,EAc3D,CAAA"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Oura wearable bond secret definitions — self-registered at import time so the
3
+ * runtime secrets registry (`@molecule/api-secrets`) can drive boot-time
4
+ * configuration reports and actionable "not configured" errors.
5
+ *
6
+ * Content is derived MECHANICALLY from this package's mlcl registry secrets
7
+ * entry (label/instructions/setupUrl/example) via the fleet formula, so
8
+ * packages sharing a key register byte-identical definitions and
9
+ * registration order never matters.
10
+ *
11
+ * @module
12
+ */
13
+ import { registerSecrets } from '@molecule/api-secrets';
14
+ /** Secret definitions required by the Oura wearable bond. */
15
+ export const wearableOuraSecretDefinitions = [
16
+ {
17
+ key: 'OAUTH_OURA_CLIENT_ID',
18
+ description: 'Oura OAuth client ID — Create an OAuth application in the Oura Cloud developer portal.',
19
+ helpUrl: 'https://cloud.ouraring.com/oauth/applications',
20
+ required: true,
21
+ },
22
+ {
23
+ key: 'OAUTH_OURA_CLIENT_SECRET',
24
+ description: 'Oura client secret — Shown when creating the OAuth application in Oura Cloud.',
25
+ helpUrl: 'https://cloud.ouraring.com/oauth/applications',
26
+ required: true,
27
+ },
28
+ ];
29
+ registerSecrets(wearableOuraSecretDefinitions);
30
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,6DAA6D;AAC7D,MAAM,CAAC,MAAM,6BAA6B,GAAuB;IAC/D;QACE,GAAG,EAAE,sBAAsB;QAC3B,WAAW,EACT,wFAAwF;QAC1F,OAAO,EAAE,+CAA+C;QACxD,QAAQ,EAAE,IAAI;KACf;IACD;QACE,GAAG,EAAE,0BAA0B;QAC/B,WAAW,EAAE,+EAA+E;QAC5F,OAAO,EAAE,+CAA+C;QACxD,QAAQ,EAAE,IAAI;KACf;CACF,CAAA;AAED,eAAe,CAAC,6BAA6B,CAAC,CAAA"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Oura Ring wearable bond types for molecule.dev.
3
+ *
4
+ * @module
5
+ */
6
+ import type { WearableCredentialsStore, WearableProvider as WearableProviderType } from '@molecule/api-wearable';
7
+ export type { DailyActivity, HeartRateSummary, HeartRateZone, SleepSession, SleepStage, SleepStageSegment, SleepStageSummary, UserConnection, WearableCredentialsStore, WearableDate, WearableDateRange, WearableProvider, WeightEntry, } from '@molecule/api-wearable';
8
+ declare global {
9
+ namespace NodeJS {
10
+ /**
11
+ * Process Env interface.
12
+ */
13
+ interface ProcessEnv {
14
+ /**
15
+ * The Oura OAuth client ID. Required to mint and refresh access
16
+ * tokens.
17
+ *
18
+ * @see https://cloud.ouraring.com/docs/authentication
19
+ */
20
+ OAUTH_OURA_CLIENT_ID?: string;
21
+ /**
22
+ * The Oura OAuth client secret. Required for the
23
+ * `authorization_code` and `refresh_token` grant flows.
24
+ *
25
+ * @see https://cloud.ouraring.com/docs/authentication
26
+ */
27
+ OAUTH_OURA_CLIENT_SECRET?: string;
28
+ }
29
+ }
30
+ }
31
+ /**
32
+ * Configuration options for {@link createProvider}.
33
+ */
34
+ export interface OuraProviderOptions {
35
+ /**
36
+ * OAuth client id. Defaults to `process.env.OAUTH_OURA_CLIENT_ID`.
37
+ */
38
+ clientId?: string;
39
+ /**
40
+ * OAuth client secret. Defaults to
41
+ * `process.env.OAUTH_OURA_CLIENT_SECRET`.
42
+ */
43
+ clientSecret?: string;
44
+ /**
45
+ * OAuth redirect URI registered with the Oura application.
46
+ */
47
+ redirectUri: string;
48
+ /**
49
+ * Persists user connections (access + refresh tokens). Required.
50
+ */
51
+ credentialsStore: WearableCredentialsStore;
52
+ /**
53
+ * OAuth scopes to request when starting authorization. Defaults to the
54
+ * union needed for daily activity, sleep, heart-rate, and personal
55
+ * info reads.
56
+ */
57
+ scopes?: readonly string[];
58
+ /**
59
+ * Override the Oura Cloud API base URL. Defaults to
60
+ * `https://api.ouraring.com/v2`.
61
+ */
62
+ apiBaseUrl?: string;
63
+ /**
64
+ * Override the Oura OAuth authorize endpoint. Defaults to
65
+ * `https://cloud.ouraring.com/oauth/authorize`.
66
+ */
67
+ authorizeUrl?: string;
68
+ /**
69
+ * Override the Oura OAuth token endpoint. Defaults to
70
+ * `https://api.ouraring.com/oauth/token`.
71
+ */
72
+ tokenUrl?: string;
73
+ /**
74
+ * Override the Oura OAuth token-revoke endpoint. Defaults to
75
+ * `https://api.ouraring.com/oauth/revoke`.
76
+ */
77
+ revokeUrl?: string;
78
+ /**
79
+ * Request timeout in milliseconds. Defaults to `15_000`.
80
+ */
81
+ timeoutMs?: number;
82
+ /**
83
+ * Optional override for the random-bytes generator used for the OAuth
84
+ * `state` parameter. Tests inject a deterministic generator.
85
+ */
86
+ randomBytes?: (size: number) => Uint8Array;
87
+ /**
88
+ * Optional clock override, primarily for tests. Defaults to `Date.now`.
89
+ */
90
+ now?: () => number;
91
+ }
92
+ /**
93
+ * Result of {@link OuraProvider.startAuthorize} — the URL the host
94
+ * should redirect the user to plus the `state` value to round-trip.
95
+ */
96
+ export interface OuraAuthorizeStart {
97
+ /** Oura OAuth authorize URL. */
98
+ url: string;
99
+ /** Opaque `state` value the host MUST verify on the callback. */
100
+ state: string;
101
+ }
102
+ /**
103
+ * Public surface returned by {@link createProvider}. Extends the
104
+ * stack-neutral {@link import('@molecule/api-wearable').WearableProvider}
105
+ * with an Oura-flavored `startAuthorize()` helper for hosts that want
106
+ * the bond to build the authorize URL.
107
+ */
108
+ export interface OuraProvider extends WearableProviderType {
109
+ /**
110
+ * Builds the Oura authorize URL with a random `state` value. The host
111
+ * is responsible for round-tripping `state` through the OAuth callback
112
+ * and verifying it before calling {@link OuraProvider.connect}.
113
+ *
114
+ * @returns The URL to redirect to and the `state` value.
115
+ */
116
+ startAuthorize(): OuraAuthorizeStart;
117
+ }
118
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,IAAI,oBAAoB,EACzC,MAAM,wBAAwB,CAAA;AAE/B,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,GACZ,MAAM,wBAAwB,CAAA;AAE/B,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,MAAM,CAAC;QACf;;WAEG;QACH,UAAiB,UAAU;YACzB;;;;;eAKG;YACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;YAE7B;;;;;eAKG;YACH,wBAAwB,CAAC,EAAE,MAAM,CAAA;SAClC;KACF;CACF;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,gBAAgB,EAAE,wBAAwB,CAAA;IAC1C;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,CAAA;IAC1C;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAa,SAAQ,oBAAoB;IACxD;;;;;;OAMG;IACH,cAAc,IAAI,kBAAkB,CAAA;CACrC"}
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Oura Ring wearable bond types for molecule.dev.
3
+ *
4
+ * @module
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@molecule/api-wearable-oura",
3
+ "version": "1.0.0",
4
+ "description": "Oura Ring Cloud API v2 bond for molecule.dev — daily activity, sleep (REM/deep/light), and heart-rate ingestion via OAuth 2.0",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "keywords": [
23
+ "molecule",
24
+ "wearable",
25
+ "oura",
26
+ "ring",
27
+ "sleep",
28
+ "readiness",
29
+ "heart-rate",
30
+ "oauth"
31
+ ],
32
+ "license": "Apache-2.0",
33
+ "peerDependencies": {
34
+ "@molecule/api-bond": "^1.0.0",
35
+ "@molecule/api-http": "^1.0.0",
36
+ "@molecule/api-secrets": "^1.0.0",
37
+ "@molecule/api-wearable": "^1.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@molecule/api-bond": "1.0.0",
41
+ "@molecule/api-http": "1.0.0",
42
+ "@molecule/api-wearable": "1.0.0",
43
+ "@types/node": "26.1.2",
44
+ "typescript": "6.0.3",
45
+ "vitest": "4.1.10"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/molecule-dev/molecule.git",
50
+ "directory": "packages/api/bonds/wearable/oura"
51
+ },
52
+ "homepage": "https://github.com/molecule-dev/molecule/tree/main/packages/api/bonds/wearable/oura",
53
+ "bugs": "https://github.com/molecule-dev/molecule/issues",
54
+ "publishConfig": {
55
+ "access": "public"
56
+ }
57
+ }