@skyporch/daykeeper-react-native 0.1.0 → 0.2.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/CHANGELOG.md +121 -0
- package/COMPATIBILITY.md +91 -5
- package/LICENSE-APACHE-2.0 +201 -0
- package/NOTICE +11 -0
- package/README.md +104 -6
- package/dist/chunk-BAQHY3R2.js +500 -0
- package/dist/chunk-BAQHY3R2.js.map +1 -0
- package/dist/index.cjs +205 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -7
- package/dist/index.d.ts +68 -7
- package/dist/index.js +6 -346
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +548 -0
- package/dist/native.cjs.map +1 -0
- package/dist/native.d.cts +18 -0
- package/dist/native.d.ts +18 -0
- package/dist/native.js +36 -0
- package/dist/native.js.map +1 -0
- package/package.json +16 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,126 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-09-21
|
|
4
|
+
|
|
5
|
+
This version introduces the native transport break described below. The
|
|
6
|
+
pre-release candidate was renumbered from `0.1.1` to `0.2.0`; `0.1.1` was never
|
|
7
|
+
released, so every change below is relative to the published `0.1.0`.
|
|
8
|
+
|
|
9
|
+
### Security
|
|
10
|
+
|
|
11
|
+
`0.1.0` is no longer supported. Upgrade to `0.2.0`.
|
|
12
|
+
|
|
13
|
+
- `0.1.0` set no redirect policy. On React Native's default XHR-backed Fetch a
|
|
14
|
+
redirected request, including its bearer token, could be followed to another
|
|
15
|
+
endpoint. `0.2.0` sends `redirect: "error"`, `credentials: "omit"` and
|
|
16
|
+
`cache: "no-store"` on every request, and the native export refuses to run
|
|
17
|
+
without an explicitly supplied compliant Fetch transport.
|
|
18
|
+
- `0.1.0` replayed every request once after an HTTP 401, including writes, so
|
|
19
|
+
a message or conversation could be created twice. `0.2.0` never replays a
|
|
20
|
+
write; only reads refresh credentials and retry once.
|
|
21
|
+
|
|
22
|
+
### Breaking
|
|
23
|
+
|
|
24
|
+
- **Native transport must be configured explicitly.** The native export no
|
|
25
|
+
longer falls back to React Native's XHR-based Fetch. Callers must supply a
|
|
26
|
+
`fetch` implementation that rejects redirects and omits ambient cookies; on
|
|
27
|
+
the validated Expo 57 runtime, `fetch` from `expo/fetch`. A client
|
|
28
|
+
constructed without one now fails before credentials are requested.
|
|
29
|
+
- **Server error codes must look like codes.** `0.1.0` copied any string
|
|
30
|
+
`error` value into `DaykeeperReactNativeApiError.code`. `0.2.0` passes a value
|
|
31
|
+
through unchanged only when it matches `^[a-z][a-z0-9_]{2,63}$`, including
|
|
32
|
+
codes newer than the installed SDK; anything else, such as a free-form English
|
|
33
|
+
sentence, becomes `daykeeper_request_failed`. The vocabulary stays open, so
|
|
34
|
+
handle unknown codes safely. (An unreleased candidate briefly used a 23-entry
|
|
35
|
+
allowlist; it never shipped.)
|
|
36
|
+
- **Writes are never replayed after HTTP 401.** A write rejected with 401 now
|
|
37
|
+
throws `DaykeeperReactNativeApiError` with status 401 and `retryable: false`.
|
|
38
|
+
The token provider receives `forceRefresh: true` only for a read. Refresh the
|
|
39
|
+
session and let the customer send again.
|
|
40
|
+
- **Token provider failures are wrapped.** An error thrown by `getAccessToken`
|
|
41
|
+
now surfaces as `DaykeeperReactNativeTransportError` with code
|
|
42
|
+
`TOKEN_PROVIDER_ERROR` and `retryable: false`, not as the original error.
|
|
43
|
+
Apps that inspected their own provider errors must track that state
|
|
44
|
+
themselves.
|
|
45
|
+
- **Write errors are never retryable, and uncertain outcomes are explicit.**
|
|
46
|
+
Every write error now has `retryable: false` (`0.1.0` marked 408, 429 and 5xx
|
|
47
|
+
retryable). A write that failed after dispatch, or returned 408 or 5xx, also
|
|
48
|
+
has `outcomeUnknown: true`. Reconcile server state before a deliberate new
|
|
49
|
+
write.
|
|
50
|
+
|
|
51
|
+
This package consumes the **customer** contract only, which remains 0.1.0. The
|
|
52
|
+
newly required `Idempotency-Key` header on flow mutations, and the `200`
|
|
53
|
+
returned alongside `201` for a replayed mutation, are **management** contract
|
|
54
|
+
0.2.0 changes and do not apply here. The breaking changes above are this
|
|
55
|
+
package's own. See [`COMPATIBILITY.md`](COMPATIBILITY.md).
|
|
56
|
+
|
|
57
|
+
### Changes
|
|
58
|
+
|
|
59
|
+
- Preserve the API-only inbox contract: widget identity and anonymous-thread
|
|
60
|
+
claim return `409 widget_unavailable` before contacting the provider.
|
|
61
|
+
- Preserve every server error code whose shape is a code. Codes matching
|
|
62
|
+
`^[a-z][a-z0-9_]{2,63}$` pass through unchanged; only values that fail that
|
|
63
|
+
shape collapse to `daykeeper_request_failed`. The previous 23-entry
|
|
64
|
+
allowlist collapsed live gateway codes the consuming app switches on, such
|
|
65
|
+
as `support_gateway_request_failed`, `widget_token_required`,
|
|
66
|
+
`invalid_tenant` and `conversation_not_found`. Message projection is
|
|
67
|
+
unchanged: no raw body text enters the message, stack or serialization.
|
|
68
|
+
- Decide on the HTTP status before parsing an error body. An HTML error page
|
|
69
|
+
from a proxy or CDN no longer discards the status as `INVALID_RESPONSE`, and
|
|
70
|
+
a non-JSON 401 still authorizes the single credential refresh on reads.
|
|
71
|
+
- Accept `http://[::1]` and other bracketed IPv6 loopback base URLs for local
|
|
72
|
+
development. The loopback check compared against an unbracketed `::1` and
|
|
73
|
+
never matched.
|
|
74
|
+
- Expose `DaykeeperReactNativeApiError.nextAction`, projected through a closed
|
|
75
|
+
allowlist of `review_usage`, `review_setup` and `refresh_conversation` and
|
|
76
|
+
included in `toJSON` when present. Unrecognized values become `undefined`.
|
|
77
|
+
The contract's `message` field remains unread.
|
|
78
|
+
- Verify the vendored contract's provenance in `check:generated`. The new
|
|
79
|
+
`scripts/check-generated.mjs` recomputes the SHA-256 and Git blob id of
|
|
80
|
+
`openapi/customer.yaml`, requires both plus the upstream commit to appear in
|
|
81
|
+
`openapi/SOURCE.md`, and regenerates types into a temporary directory, so a
|
|
82
|
+
contract edit can no longer pass by regenerating alongside it.
|
|
83
|
+
- Vendor the customer contract from immutable `daykeeper-openapi` tag
|
|
84
|
+
`v1.1.0` (commit `c9a0175`), where `CustomerError` is
|
|
85
|
+
`additionalProperties: true` and the `error` code is documented as extensible.
|
|
86
|
+
|
|
87
|
+
- Set `cache: "no-store"` on every dispatch. Native exports also send
|
|
88
|
+
`Cache-Control: no-cache, no-store` for transports that ignore Fetch cache mode.
|
|
89
|
+
Keep browser cache headers under standard Fetch control to avoid changing CORS.
|
|
90
|
+
- Add seeded-cache account-switch, revoked-credential, freshness and no-new-storage
|
|
91
|
+
probes against real HTTP, with mandatory native positive cache controls.
|
|
92
|
+
|
|
93
|
+
- Set `redirect: "error"` and `credentials: "omit"` on every SDK request,
|
|
94
|
+
including credential refresh. Node/web standard Fetch remains the default.
|
|
95
|
+
- Verify native and Node ESM/CJS exports, declarations and installed-package
|
|
96
|
+
policy behavior independently. Android/release certification remains open.
|
|
97
|
+
|
|
98
|
+
The following changes were developed in the candidate previously numbered
|
|
99
|
+
`0.1.1` and ship as part of `0.2.0`; `0.1.0` remains the published baseline
|
|
100
|
+
until release approval.
|
|
101
|
+
|
|
102
|
+
- Never replay writes after authentication failure; classify uncertain write
|
|
103
|
+
outcomes explicitly and make all write errors non-retryable.
|
|
104
|
+
- Honor first-read HTTP 401 `retryable: false` before credential refresh, within
|
|
105
|
+
the original deadline and response size limit.
|
|
106
|
+
- Keep raw error bodies out of messages, stacks and serialization. Treat
|
|
107
|
+
failed native body reads as transport failures.
|
|
108
|
+
- Respect explicit server retry advice for customer API errors, including
|
|
109
|
+
non-retryable quota ceilings; preserve older status-only read classification.
|
|
110
|
+
- Align vendored OpenAPI license metadata and record the exact source commit
|
|
111
|
+
and checksum; generated types and runtime behavior are unchanged.
|
|
112
|
+
- Classify credential-provider failures as non-retryable `TOKEN_PROVIDER_ERROR`
|
|
113
|
+
without exposing raw provider errors; timeout and cancellation stay distinct.
|
|
114
|
+
- Bound credential acquisition, authentication refresh, transport, and buffered
|
|
115
|
+
or streaming response reads by one cancellable request deadline.
|
|
116
|
+
- Prevent late dispatch, release failed response bodies without waiting for
|
|
117
|
+
cleanup, and sanitize credential-provider failures.
|
|
118
|
+
- Make public SDK and generated contract documentation
|
|
119
|
+
infrastructure-provider neutral.
|
|
120
|
+
- Ship a `NOTICE` file recording the Apache-2.0 contract the generated types
|
|
121
|
+
come from, with the full Apache-2.0 text as `LICENSE-APACHE-2.0`, and list
|
|
122
|
+
supported versions and a private reporting channel in `SECURITY.md`.
|
|
123
|
+
|
|
3
124
|
## 0.1.0
|
|
4
125
|
|
|
5
126
|
- Add typed customer identity, conversation, message, unread, and seen APIs.
|
package/COMPATIBILITY.md
CHANGED
|
@@ -1,17 +1,52 @@
|
|
|
1
1
|
# React Native compatibility
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Version matrix
|
|
4
|
+
|
|
5
|
+
| Package version | Contract consumed | Contract version | Contract tag | Contract commit |
|
|
6
|
+
| ------------------- | ------------------------------------- | ---------------- | ----------------- | ------------------------------------------ |
|
|
7
|
+
| `0.2.0` | customer (`openapi/customer.yaml`) | 0.1.0 | `v1.1.0` | `c9a0175d0053f1a2d57c9329f6d3a36ec6acdb71` |
|
|
8
|
+
| `0.1.0` (published) | customer (`openapi/customer.yaml`) | 0.1.0 | `v1.0.0` | `35f5bd45fe0c6a6901766543bff90dae6838b965` |
|
|
9
|
+
| any | management (`openapi/daykeeper.yaml`) | 0.2.0 | not consumed here | not consumed here |
|
|
10
|
+
|
|
11
|
+
This package consumes only the **customer** contract. The customer contract
|
|
12
|
+
snapshot is pinned to immutable repository tag `v1.1.0`; its OpenAPI `info.version`
|
|
13
|
+
is `0.1.0`. It has no `Idempotency-Key`
|
|
14
|
+
header. The now-required
|
|
15
|
+
`Idempotency-Key` header on flow mutations, and the `200`-alongside-`201`
|
|
16
|
+
replay response, are **management** contract 0.2.0 changes and do **not** apply
|
|
17
|
+
to this package. The management contract is consumed by `daykeeper-node`,
|
|
18
|
+
`daykeeper-mcp` and `daykeeper-cli`, not here. The 0.2.0 break in this package
|
|
19
|
+
is its own: the native transport policy change and error-code passthrough
|
|
20
|
+
described below.
|
|
21
|
+
|
|
22
|
+
Every release records the exact customer contract tag and commit used; see
|
|
23
|
+
[`openapi/SOURCE.md`](openapi/SOURCE.md) and [`RELEASING.md`](RELEASING.md).
|
|
24
|
+
|
|
25
|
+
## Runtime
|
|
26
|
+
|
|
27
|
+
The initial validation target is Expo 57 with React Native 0.86 and Hermes. The
|
|
4
28
|
package declares React Native 0.79 or newer because Metro enables package
|
|
5
29
|
exports by default from that line onward. Releases must be smoke-tested through
|
|
6
30
|
Metro on both iOS and Android before this matrix is expanded.
|
|
7
31
|
|
|
8
|
-
The client
|
|
9
|
-
`Headers`, `URL`, and `AbortController`. It handles both standards-based
|
|
32
|
+
The client uses an explicitly configured native Fetch transport plus the
|
|
33
|
+
web-compatible globals `Headers`, `URL`, and `AbortController`. It handles both standards-based
|
|
10
34
|
streaming responses and React Native's buffered `Response.text()` response. A
|
|
11
35
|
custom `fetch` implementation must preserve those request and response
|
|
12
|
-
semantics
|
|
36
|
+
semantics and enforce `redirect: "error"` plus `credentials: "omit"` before
|
|
37
|
+
forwarding anything. It must also honor `Cache-Control: no-cache, no-store`;
|
|
38
|
+
the native export adds this because Expo 57 ignores Fetch's `cache` option.
|
|
39
|
+
All exports set `cache: "no-store"`. Native entry types require `fetch`; runtime validation
|
|
40
|
+
rejects an omitted transport before credentials are requested. Standard
|
|
41
|
+
Node/web Fetch remains the non-native default. Expo is not an SDK dependency.
|
|
42
|
+
|
|
43
|
+
Writes never automatically replay, including after 401. Read credential refresh
|
|
44
|
+
honors explicit non-retryable advice before a second dispatch. An uncertain
|
|
45
|
+
write result is marked `outcomeUnknown: true` and `retryable: false`; reconcile
|
|
46
|
+
server state before a deliberate retry. This is not server-side idempotency.
|
|
47
|
+
See the recovery contract in the README.
|
|
13
48
|
|
|
14
|
-
|
|
49
|
+
The SDK is headless and ships no native module. It does not yet provide a
|
|
15
50
|
messenger UI, push-notification registration, attachment selection/upload,
|
|
16
51
|
deep-link routing, persistent offline queues, or background delivery. Consumers
|
|
17
52
|
own those surfaces until they are added here behind reviewed native interfaces.
|
|
@@ -19,3 +54,54 @@ own those surfaces until they are added here behind reviewed native interfaces.
|
|
|
19
54
|
Tokens must be short-lived and customer-scoped. Keep them in memory where
|
|
20
55
|
possible; if persistence is unavoidable, use an OS-backed secret store and
|
|
21
56
|
never AsyncStorage.
|
|
57
|
+
|
|
58
|
+
## Error codes: open vocabulary
|
|
59
|
+
|
|
60
|
+
The gateway's error vocabulary is open and grows without an SDK release, so
|
|
61
|
+
the SDK does not keep a list of known codes. Any `error` value shaped like a
|
|
62
|
+
code -- ASCII lowercase snake_case, 3 to 64 characters, matching
|
|
63
|
+
`^[a-z][a-z0-9_]{2,63}$` -- reaches the caller as
|
|
64
|
+
`DaykeeperReactNativeApiError.code` unchanged, including codes newer than the
|
|
65
|
+
installed SDK. Anything else, including the free-form English messages the
|
|
66
|
+
gateway returns for some 4xx failures, collapses to
|
|
67
|
+
`daykeeper_request_failed`. The error message is always the code alone: the
|
|
68
|
+
contract's `message` and `nextAction` fields are never projected into the
|
|
69
|
+
message, stack or serialization. Consuming apps must still handle an unknown
|
|
70
|
+
code safely, and must not treat `daykeeper_request_failed` as a specific
|
|
71
|
+
condition.
|
|
72
|
+
|
|
73
|
+
`nextAction` is the exception and stays a closed vocabulary of exactly
|
|
74
|
+
`review_usage`, `review_setup` and `refresh_conversation`, typed as
|
|
75
|
+
`DaykeeperReactNativeNextAction`. A code is only ever compared, so an unknown
|
|
76
|
+
one is inert; a next action is an instruction the app acts on, so an
|
|
77
|
+
unrecognized value is dropped to `undefined` and omitted from serialization
|
|
78
|
+
rather than surfaced. The contract's `message` field is still never read.
|
|
79
|
+
|
|
80
|
+
## Native network boundary: release gate
|
|
81
|
+
|
|
82
|
+
Candidate 0.2.0 sets strict redirect/cookie policy on every dispatch and refuses
|
|
83
|
+
implicit native transport selection. Supply explicit Expo57 `expo/fetch` on the
|
|
84
|
+
validated host or a custom compliant native implementation. A caller-provided
|
|
85
|
+
function remains trusted to implement those semantics; the SDK cannot repair
|
|
86
|
+
an XHR wrapper that ignores them. Checking `response.url` afterward cannot
|
|
87
|
+
prevent an already-forwarded request.
|
|
88
|
+
|
|
89
|
+
The synthetic two-listener fixture in [`smoke/boundary`](smoke/boundary/README.md)
|
|
90
|
+
measures arrivals, partial bodies, cookies and authorization without retaining
|
|
91
|
+
credential or message values. Direct positive controls must pass before a
|
|
92
|
+
rejected redirect is credited as a boundary. All four writes and one read are
|
|
93
|
+
tested against 301, 302, 303, 307 and 308, within and across origins.
|
|
94
|
+
|
|
95
|
+
On iOS 26.5 / React Native 0.86.2 / Hermes, the XHR-backed Fetch implementation
|
|
96
|
+
followed redirects despite `redirect: "error"`. Explicit `credentials: "omit"`
|
|
97
|
+
did omit a seeded cookie in this runtime. Explicit Expo 57 `expo/fetch` with
|
|
98
|
+
both options rejected all 50 redirects before receiver arrival and omitted the
|
|
99
|
+
cookie; its five direct API controls succeeded. These are observations for that
|
|
100
|
+
specific runtime, not a supported transport migration or Android certification.
|
|
101
|
+
|
|
102
|
+
Before release, verify each consumer's actual transport, rerun packed-package
|
|
103
|
+
tests on iOS and Android plus release builds, and certify standalone bare-native
|
|
104
|
+
transport coverage. Do not silently require Expo or claim that passing Fetch
|
|
105
|
+
flags fixes XHR redirect behavior. Exact gateway
|
|
106
|
+
URLs must not rely on canonicalization redirects. Do not broaden native HTTP
|
|
107
|
+
exceptions for this fixture or use it on signed-in devices.
|
|
@@ -0,0 +1,201 @@
|
|
|
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
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 SkyPorch
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Daykeeper React Native SDK
|
|
2
|
+
Copyright (c) 2026 SkyPorch
|
|
3
|
+
|
|
4
|
+
Licensed under the MIT License. See the LICENSE file.
|
|
5
|
+
|
|
6
|
+
The TypeScript types in this package are generated from the Daykeeper customer
|
|
7
|
+
OpenAPI description, copied from SkyPorch/daykeeper-openapi and licensed under
|
|
8
|
+
the Apache License, Version 2.0. A copy of that license, taken verbatim from
|
|
9
|
+
SkyPorch/daykeeper-openapi, ships in this package as `LICENSE-APACHE-2.0`.
|
|
10
|
+
The source repository keeps that description under `openapi/customer.yaml` and
|
|
11
|
+
records the exact tag and commit in `openapi/SOURCE.md`.
|
package/README.md
CHANGED
|
@@ -1,28 +1,45 @@
|
|
|
1
1
|
# `@skyporch/daykeeper-react-native`
|
|
2
2
|
|
|
3
3
|
The official headless React Native client for customer-facing Daykeeper support
|
|
4
|
-
experiences.
|
|
4
|
+
experiences. Its types come from the Daykeeper customer API contract; its transport is
|
|
5
5
|
designed for Expo and bare React Native applications on iOS and Android.
|
|
6
6
|
|
|
7
7
|
This package contains no native module and no embedded support UI. It provides
|
|
8
8
|
typed identity, conversation, message, unread, seen, and anonymous-claim APIs.
|
|
9
9
|
Push notifications, attachments, deep links, and native UI can be added here
|
|
10
10
|
without coupling consuming applications to Daykeeper's private platform or to
|
|
11
|
-
|
|
11
|
+
any infrastructure provider.
|
|
12
|
+
|
|
13
|
+
API-only inbox gateways support customer conversations, messages, unread state,
|
|
14
|
+
and seen markers. They do not provide widget identity or anonymous-conversation
|
|
15
|
+
claim operations: those calls return `409` with `widget_unavailable` before the
|
|
16
|
+
conversation provider is contacted. Use a widget-enabled gateway for those
|
|
17
|
+
operations.
|
|
18
|
+
|
|
19
|
+
This README describes the unreleased `0.2.0` candidate. Production certification
|
|
20
|
+
is still open; a successful Metro build or Node test does not establish native
|
|
21
|
+
parity. See [`COMPATIBILITY.md`](COMPATIBILITY.md) before a release or upgrade.
|
|
12
22
|
|
|
13
23
|
## Install
|
|
14
24
|
|
|
25
|
+
After the candidate is approved and published:
|
|
26
|
+
|
|
15
27
|
```sh
|
|
16
|
-
npm install @skyporch/daykeeper-react-native
|
|
28
|
+
npm install @skyporch/daykeeper-react-native@0.2.0
|
|
17
29
|
```
|
|
18
30
|
|
|
31
|
+
Before publication, test with the reviewed packed tarball. An unversioned npm
|
|
32
|
+
install may select the older published SDK, which does not enforce this policy.
|
|
33
|
+
|
|
19
34
|
## Use
|
|
20
35
|
|
|
21
36
|
```ts
|
|
22
37
|
import { createDaykeeperReactNativeClient } from "@skyporch/daykeeper-react-native";
|
|
38
|
+
import { fetch as expoFetch } from "expo/fetch";
|
|
23
39
|
|
|
24
40
|
const daykeeper = createDaykeeperReactNativeClient({
|
|
25
41
|
baseUrl: "https://support.example.com/support-api",
|
|
42
|
+
fetch: expoFetch,
|
|
26
43
|
getAccessToken: async ({ forceRefresh }) => {
|
|
27
44
|
// Exchange the signed-in app session for a short-lived, customer-scoped
|
|
28
45
|
// Daykeeper token using your own backend. Bypass any local token cache when
|
|
@@ -34,9 +51,29 @@ const daykeeper = createDaykeeperReactNativeClient({
|
|
|
34
51
|
const { conversations } = await daykeeper.listConversations();
|
|
35
52
|
```
|
|
36
53
|
|
|
54
|
+
The native export requires an explicit Fetch implementation. This example uses
|
|
55
|
+
the host's existing Expo 57 installation; the SDK does not install Expo. Bare
|
|
56
|
+
React Native applications may supply their own native Fetch implementation,
|
|
57
|
+
but it must reject redirects before following them, omit ambient cookies,
|
|
58
|
+
honor request Cache-Control directives, and support cancellation. The XHR-backed React Native global is not compliant;
|
|
59
|
+
wrapping it or setting Fetch flags does not repair its redirect behavior. Do not
|
|
60
|
+
pass it as the native transport. Omitting `fetch` fails before token acquisition.
|
|
61
|
+
|
|
62
|
+
Every request sets `redirect: "error"`, `credentials: "omit"`, and `cache: "no-store"`.
|
|
63
|
+
The native export also sends `Cache-Control: no-cache, no-store`, because the
|
|
64
|
+
validated Expo runtime ignores the standard Fetch cache option. This revalidates
|
|
65
|
+
existing cache entries and prevents new storage, including across account
|
|
66
|
+
changes. Node/web uses standard Fetch cache semantics without adding a custom
|
|
67
|
+
browser request header. Keep the API's response `Cache-Control: no-store` too.
|
|
68
|
+
A custom Fetch
|
|
69
|
+
function is a caller-owned trust boundary: the SDK cannot prevent an injected
|
|
70
|
+
implementation from ignoring those options. Use an exact HTTPS gateway URL,
|
|
71
|
+
not one that redirects. Node/web exports can use standard global Fetch.
|
|
72
|
+
|
|
37
73
|
The token provider runs for every request so the consuming app can rotate
|
|
38
|
-
short-lived credentials.
|
|
39
|
-
provider for one forced refresh and retries
|
|
74
|
+
short-lived credentials. For a GET returning HTTP 401 without an explicit
|
|
75
|
+
`retryable: false`, the SDK asks the provider for one forced refresh and retries
|
|
76
|
+
the read once. Writes never refresh and replay automatically. Keep customer tokens
|
|
40
77
|
in memory where possible. Never place them in URLs, analytics, crash reports,
|
|
41
78
|
or application logs.
|
|
42
79
|
|
|
@@ -51,8 +88,69 @@ or application logs.
|
|
|
51
88
|
See [`COMPATIBILITY.md`](COMPATIBILITY.md) for the supported runtime contract
|
|
52
89
|
and release certification matrix.
|
|
53
90
|
|
|
91
|
+
## Deadlines and cancellation
|
|
92
|
+
|
|
93
|
+
`timeoutMs` defaults to 30 seconds (allowed range: 1–60 seconds). One budget
|
|
94
|
+
covers token acquisition, both authentication attempts, and streaming or
|
|
95
|
+
buffered response reads. Token providers receive an optional `signal` alongside
|
|
96
|
+
`forceRefresh`; pass it to your backend exchange to cancel that work too.
|
|
97
|
+
|
|
98
|
+
Every customer API method accepts `{ signal }` request options. Use a caller
|
|
99
|
+
`AbortController` when a screen unmounts or the signed-in identity changes.
|
|
100
|
+
Pre-aborted calls do not invoke credentials or send a request. A late token
|
|
101
|
+
cannot start a request after cancellation or timeout.
|
|
102
|
+
|
|
103
|
+
Errors preserve `REQUEST_ABORTED` or `REQUEST_TIMEOUT` even if a credential
|
|
104
|
+
provider or native response stalls. Cancellation does not undo a message or
|
|
105
|
+
conversation the server already accepted. Reconcile history before retrying a
|
|
106
|
+
write with an uncertain outcome; network failures and timeouts are not
|
|
107
|
+
automatically replayed.
|
|
108
|
+
|
|
109
|
+
Credential-provider failures use the non-retryable `TOKEN_PROVIDER_ERROR`
|
|
110
|
+
code, distinct from network failures. Raw provider errors are not exposed.
|
|
111
|
+
Handle sign-in recovery in the token provider or the application; deadline and
|
|
112
|
+
caller cancellation still use their dedicated error codes.
|
|
113
|
+
|
|
114
|
+
## Safe recovery
|
|
115
|
+
|
|
116
|
+
All four writes (`createConversation`, `sendMessage`, `markConversationSeen`, and
|
|
117
|
+
`claimAnonymousConversation`) dispatch at most once per call, including after
|
|
118
|
+
HTTP 401. Their errors always have `retryable: false`, even when a server hint
|
|
119
|
+
says otherwise. Refresh credentials in your app before a deliberate new write;
|
|
120
|
+
do not wrap writes in a generic retry loop.
|
|
121
|
+
|
|
122
|
+
Both exported error classes include `outcomeUnknown` in their properties and
|
|
123
|
+
`toJSON()` output. It is true when a write may have reached the server but the
|
|
124
|
+
SDK cannot confirm its result: a timeout, cancellation, transport/body failure
|
|
125
|
+
after dispatch, or HTTP 408/5xx. Preserve the draft and read conversation history
|
|
126
|
+
before offering another send. Cancellation cannot roll back an accepted write.
|
|
127
|
+
`outcomeUnknown: false` is not an exactly-once guarantee or proof of no side
|
|
128
|
+
effects. The customer API has no SDK-supported idempotency-key contract yet.
|
|
129
|
+
The SDK's dispatch bound is not a wire-level guarantee: a native HTTP stack may
|
|
130
|
+
retry internally (observed for a dropped GET on iOS). Never treat this SDK as
|
|
131
|
+
an exactly-once delivery mechanism.
|
|
132
|
+
|
|
133
|
+
For reads, `DaykeeperReactNativeApiError.retryable` respects an explicit server
|
|
134
|
+
boolean; older responses fall back to status-based classification (408, 429,
|
|
135
|
+
and 5xx). No automatic backoff or rate-limit retry is performed. Only a first
|
|
136
|
+
read 401 without an explicit false hint permits one credential refresh. A
|
|
137
|
+
stalled, oversized, cancelled, or failed 401 body does not permit that refresh;
|
|
138
|
+
a completed legacy empty/non-JSON 401 does.
|
|
139
|
+
|
|
140
|
+
A usage ceiling can return HTTP 429 with `retryable: false`. Show suitable local
|
|
141
|
+
copy for its stable code and let a workspace administrator review usage.
|
|
142
|
+
Server error codes matching `^[a-z][a-z0-9_]{2,63}$` are preserved in SDK
|
|
143
|
+
errors, including codes introduced after this release. Handle unfamiliar codes
|
|
144
|
+
with a generic recovery message. Malformed codes become
|
|
145
|
+
`daykeeper_request_failed`. Raw server messages, next-action URLs, and
|
|
146
|
+
credential/transport errors are not copied.
|
|
147
|
+
|
|
148
|
+
On logout or account change, abort outstanding calls, clear customer UI/history
|
|
149
|
+
and in-memory token caches, and ignore results belonging to the old session.
|
|
150
|
+
The headless SDK does not own the app's login state or persistent storage.
|
|
151
|
+
|
|
54
152
|
## Release status
|
|
55
153
|
|
|
56
|
-
|
|
154
|
+
Candidate `0.2.0` is generated from the customer contract recorded in
|
|
57
155
|
[`openapi/SOURCE.md`](openapi/SOURCE.md). Releases use the protected,
|
|
58
156
|
provenance-producing process in [`RELEASING.md`](RELEASING.md).
|