react-native-nitro-cookies 1.0.0 → 1.0.2
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/android/AGENTS.md +62 -0
- package/android/src/main/java/com/margelo/nitro/nitrocookies/NitroCookies.kt +37 -21
- package/ios/AGENTS.md +49 -0
- package/ios/NitroCookies.swift +63 -16
- package/lib/module/AGENTS.md +50 -0
- package/lib/typescript/src/NitroCookies.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JCookie.hpp +1 -1
- package/nitrogen/generated/android/c++/JHybridNitroCookiesSpec.cpp +9 -1
- package/nitrogen/generated/android/c++/JHybridNitroCookiesSpec.hpp +2 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/Cookie.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/HybridNitroCookiesSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/nitrocookiesOnLoad.kt +1 -1
- package/nitrogen/generated/android/nitrocookies+autolinking.cmake +1 -1
- package/nitrogen/generated/android/nitrocookies+autolinking.gradle +1 -1
- package/nitrogen/generated/android/nitrocookiesOnLoad.cpp +1 -1
- package/nitrogen/generated/android/nitrocookiesOnLoad.hpp +1 -1
- package/nitrogen/generated/ios/NitroCookies+autolinking.rb +2 -2
- package/nitrogen/generated/ios/NitroCookies-Swift-Cxx-Bridge.cpp +2 -1
- package/nitrogen/generated/ios/NitroCookies-Swift-Cxx-Bridge.hpp +3 -3
- package/nitrogen/generated/ios/NitroCookies-Swift-Cxx-Umbrella.hpp +1 -1
- package/nitrogen/generated/ios/NitroCookiesAutolinking.mm +1 -1
- package/nitrogen/generated/ios/NitroCookiesAutolinking.swift +9 -8
- package/nitrogen/generated/ios/c++/HybridNitroCookiesSpecSwift.cpp +1 -1
- package/nitrogen/generated/ios/c++/HybridNitroCookiesSpecSwift.hpp +7 -1
- package/nitrogen/generated/ios/swift/Cookie.swift +59 -143
- package/nitrogen/generated/ios/swift/Func_void.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_std__vector_Cookie_.swift +1 -2
- package/nitrogen/generated/ios/swift/HybridNitroCookiesSpec.swift +3 -5
- package/nitrogen/generated/ios/swift/HybridNitroCookiesSpec_cxx.swift +9 -3
- package/nitrogen/generated/shared/c++/Cookie.hpp +34 -26
- package/nitrogen/generated/shared/c++/HybridNitroCookiesSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridNitroCookiesSpec.hpp +1 -1
- package/package.json +13 -13
- package/src/AGENTS.md +50 -0
- package/src/NitroCookies.nitro.ts +4 -2
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!-- Parent: ../AGENTS.md -->
|
|
2
|
+
<!-- Generated: 2026-02-13 | Updated: 2026-02-13 -->
|
|
3
|
+
|
|
4
|
+
# android
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
Android native implementation of the NitroCookies HybridObject in Kotlin, plus JNI/C++ adapter for Nitro Modules initialization. Uses Android's `CookieManager` (WebView-based) for all cookie operations. Includes Gradle build configuration and CMake native build.
|
|
8
|
+
|
|
9
|
+
## Key Files
|
|
10
|
+
|
|
11
|
+
| File | Description |
|
|
12
|
+
|------|-------------|
|
|
13
|
+
| `build.gradle` | Android Gradle build config (Kotlin, CMake, dependencies) |
|
|
14
|
+
| `CMakeLists.txt` | CMake config for building the C++ JNI adapter |
|
|
15
|
+
|
|
16
|
+
## Subdirectories
|
|
17
|
+
|
|
18
|
+
| Directory | Purpose |
|
|
19
|
+
|-----------|---------|
|
|
20
|
+
| `src/main/java/com/margelo/nitro/nitrocookies/` | Kotlin source files |
|
|
21
|
+
| `src/main/cpp/` | C++ JNI adapter for Nitro module loading |
|
|
22
|
+
| `.gradle/` | Gradle cache (gitignored, auto-generated) |
|
|
23
|
+
| `.cxx/` | CMake build output (gitignored, auto-generated) |
|
|
24
|
+
|
|
25
|
+
## For AI Agents
|
|
26
|
+
|
|
27
|
+
### Working In This Directory
|
|
28
|
+
- `NitroCookies.kt` is the main implementation file -- extends `HybridNitroCookiesSpec()` generated by Nitrogen
|
|
29
|
+
- `NitroCookiesPackage.kt` is the React Native package registration (loads `libnitrocookies` native library)
|
|
30
|
+
- `cpp-adapter.cpp` is the JNI entry point that calls `margelo::nitro::nitrocookies::initialize(vm)`
|
|
31
|
+
- Android uses `android.webkit.CookieManager` for all cookie operations (single storage backend, no WebKit/non-WebKit distinction)
|
|
32
|
+
- `useWebKit` parameter is accepted but ignored on Android (API compatibility with iOS)
|
|
33
|
+
- Cookie deletion works by setting an expired cookie (Android `CookieManager` limitation)
|
|
34
|
+
- `getAll()` throws `PLATFORM_UNSUPPORTED` on Android (iOS-only feature)
|
|
35
|
+
- Date formatting: converts between ISO 8601 and RFC 1123 for cookie headers
|
|
36
|
+
- Some async operations (`clearAll`, `removeSessionCookies`) require `Dispatchers.Main` because `CookieManager` callbacks run on the main thread
|
|
37
|
+
|
|
38
|
+
### Testing Requirements
|
|
39
|
+
- No unit tests in this directory -- test via the `example/` Android app
|
|
40
|
+
- Test on various Android API levels (min API 21+)
|
|
41
|
+
- Verify cookie persistence across app restarts (test `flush()`)
|
|
42
|
+
|
|
43
|
+
### Common Patterns
|
|
44
|
+
- `Promise.async { ... }` for async operations
|
|
45
|
+
- `suspendCancellableCoroutine` to bridge callback-based `CookieManager` APIs to coroutines
|
|
46
|
+
- `toRFC6265String()` serializes Cookie struct to Set-Cookie header format
|
|
47
|
+
- `createCookieData()` parses Set-Cookie header string back to Cookie struct
|
|
48
|
+
- `validateURL()` / `validateDomain()` for input validation
|
|
49
|
+
|
|
50
|
+
## Dependencies
|
|
51
|
+
|
|
52
|
+
### Internal
|
|
53
|
+
- `nitrogen/generated/android/` -- `HybridNitroCookiesSpec`, `Cookie` data class, JNI bridges
|
|
54
|
+
- `nitrogen/generated/shared/c++/` -- C++ spec and Cookie struct
|
|
55
|
+
|
|
56
|
+
### External
|
|
57
|
+
- `android.webkit.CookieManager` -- Cookie storage (WebView-based)
|
|
58
|
+
- `java.net.HttpURLConnection` -- HTTP requests for `getFromResponse()`
|
|
59
|
+
- `com.margelo.nitro.core.Promise` -- Nitro async return type
|
|
60
|
+
- `kotlinx.coroutines` -- Coroutine support for async callbacks
|
|
61
|
+
|
|
62
|
+
<!-- MANUAL: Any manually added notes below this line are preserved on regeneration -->
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
package com.margelo.nitro.nitrocookies
|
|
2
2
|
|
|
3
|
+
import android.os.Handler
|
|
4
|
+
import android.os.Looper
|
|
3
5
|
import android.webkit.CookieManager
|
|
4
6
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
5
7
|
import com.margelo.nitro.core.Promise
|
|
@@ -8,8 +10,6 @@ import java.net.URL
|
|
|
8
10
|
import java.text.SimpleDateFormat
|
|
9
11
|
import java.util.Locale
|
|
10
12
|
import java.util.TimeZone
|
|
11
|
-
import kotlin.coroutines.resume
|
|
12
|
-
import kotlin.coroutines.suspendCoroutine
|
|
13
13
|
|
|
14
14
|
/** HybridNitroCookies - Android implementation of cookie management */
|
|
15
15
|
@DoNotStrip
|
|
@@ -31,9 +31,14 @@ class NitroCookies : HybridNitroCookiesSpec() {
|
|
|
31
31
|
// Expires attribute (convert ISO 8601 to RFC 1123)
|
|
32
32
|
cookie.expires?.let { expiresISO ->
|
|
33
33
|
try {
|
|
34
|
-
val
|
|
34
|
+
val normalizedISO = if (expiresISO.endsWith("Z")) {
|
|
35
|
+
expiresISO.dropLast(1) + "+00:00"
|
|
36
|
+
} else {
|
|
37
|
+
expiresISO
|
|
38
|
+
}
|
|
39
|
+
val date = iso8601Formatter.get()!!.parse(normalizedISO)
|
|
35
40
|
date?.let {
|
|
36
|
-
val expiresRFC = rfc1123Formatter.format(it)
|
|
41
|
+
val expiresRFC = rfc1123Formatter.get()!!.format(it)
|
|
37
42
|
parts.add("Expires=$expiresRFC")
|
|
38
43
|
}
|
|
39
44
|
} catch (e: Exception) {
|
|
@@ -87,8 +92,8 @@ class NitroCookies : HybridNitroCookiesSpec() {
|
|
|
87
92
|
part.startsWith("Expires=", ignoreCase = true) -> {
|
|
88
93
|
val expiresRFC = part.substring(8).trim()
|
|
89
94
|
try {
|
|
90
|
-
val date = rfc1123Formatter.parse(expiresRFC)
|
|
91
|
-
date?.let { expires = iso8601Formatter.format(it) }
|
|
95
|
+
val date = rfc1123Formatter.get()!!.parse(expiresRFC)
|
|
96
|
+
date?.let { expires = iso8601Formatter.get()!!.format(it) }
|
|
92
97
|
} catch (e: Exception) {
|
|
93
98
|
// Invalid date, skip
|
|
94
99
|
}
|
|
@@ -130,7 +135,7 @@ class NitroCookies : HybridNitroCookiesSpec() {
|
|
|
130
135
|
// Wildcard match (.example.com matches api.example.com)
|
|
131
136
|
if (cookieDomain.startsWith(".")) {
|
|
132
137
|
val domain = cookieDomain.substring(1)
|
|
133
|
-
return urlHost.endsWith(domain) || urlHost == domain
|
|
138
|
+
return urlHost.endsWith(".$domain") || urlHost == domain
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
// Subdomain match (example.com matches api.example.com)
|
|
@@ -324,12 +329,16 @@ class NitroCookies : HybridNitroCookiesSpec() {
|
|
|
324
329
|
|
|
325
330
|
/** Clear all cookies */
|
|
326
331
|
override fun clearAll(useWebKit: Boolean?): Promise<Boolean> {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
cookieManager
|
|
332
|
+
val promise = Promise<Boolean>()
|
|
333
|
+
Handler(Looper.getMainLooper()).post {
|
|
334
|
+
try {
|
|
335
|
+
val cookieManager = CookieManager.getInstance()
|
|
336
|
+
cookieManager.removeAllCookies { removed -> promise.resolve(removed) }
|
|
337
|
+
} catch (e: Exception) {
|
|
338
|
+
promise.reject(e)
|
|
331
339
|
}
|
|
332
340
|
}
|
|
341
|
+
return promise
|
|
333
342
|
}
|
|
334
343
|
|
|
335
344
|
/** Parse and set cookies from Set-Cookie header */
|
|
@@ -354,9 +363,9 @@ class NitroCookies : HybridNitroCookiesSpec() {
|
|
|
354
363
|
/** Make HTTP request and get cookies from response */
|
|
355
364
|
override fun getFromResponse(url: String): Promise<Array<Cookie>> {
|
|
356
365
|
return Promise.async {
|
|
366
|
+
val urlObj = validateURL(url)
|
|
367
|
+
val connection = urlObj.openConnection() as HttpURLConnection
|
|
357
368
|
try {
|
|
358
|
-
val urlObj = validateURL(url)
|
|
359
|
-
val connection = urlObj.openConnection() as HttpURLConnection
|
|
360
369
|
connection.requestMethod = "GET"
|
|
361
370
|
connection.connect()
|
|
362
371
|
|
|
@@ -368,10 +377,11 @@ class NitroCookies : HybridNitroCookiesSpec() {
|
|
|
368
377
|
cookie?.let { cookies.add(it) }
|
|
369
378
|
}
|
|
370
379
|
|
|
371
|
-
connection.disconnect()
|
|
372
380
|
cookies.toTypedArray()
|
|
373
381
|
} catch (e: Exception) {
|
|
374
|
-
throw Exception("NETWORK_ERROR: ${e.message}")
|
|
382
|
+
throw Exception("NETWORK_ERROR: ${e.message}", e)
|
|
383
|
+
} finally {
|
|
384
|
+
connection.disconnect()
|
|
375
385
|
}
|
|
376
386
|
}
|
|
377
387
|
}
|
|
@@ -418,25 +428,31 @@ class NitroCookies : HybridNitroCookiesSpec() {
|
|
|
418
428
|
|
|
419
429
|
/** Remove session cookies (Android only) */
|
|
420
430
|
override fun removeSessionCookies(): Promise<Boolean> {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
cookieManager
|
|
431
|
+
val promise = Promise<Boolean>()
|
|
432
|
+
Handler(Looper.getMainLooper()).post {
|
|
433
|
+
try {
|
|
434
|
+
val cookieManager = CookieManager.getInstance()
|
|
435
|
+
cookieManager.removeSessionCookies { removed -> promise.resolve(removed) }
|
|
436
|
+
} catch (e: Exception) {
|
|
437
|
+
promise.reject(e)
|
|
425
438
|
}
|
|
426
439
|
}
|
|
440
|
+
return promise
|
|
427
441
|
}
|
|
428
442
|
|
|
429
443
|
companion object {
|
|
430
444
|
/** ISO 8601 date formatter for cookie expires (yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ) */
|
|
431
|
-
private val iso8601Formatter =
|
|
445
|
+
private val iso8601Formatter = ThreadLocal.withInitial {
|
|
432
446
|
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US).apply {
|
|
433
447
|
timeZone = TimeZone.getTimeZone("UTC")
|
|
434
448
|
}
|
|
449
|
+
}
|
|
435
450
|
|
|
436
451
|
/** RFC 1123 date formatter for Set-Cookie headers (EEE, dd MMM yyyy HH:mm:ss z) */
|
|
437
|
-
private val rfc1123Formatter =
|
|
452
|
+
private val rfc1123Formatter = ThreadLocal.withInitial {
|
|
438
453
|
SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US).apply {
|
|
439
454
|
timeZone = TimeZone.getTimeZone("GMT")
|
|
440
455
|
}
|
|
456
|
+
}
|
|
441
457
|
}
|
|
442
458
|
}
|
package/ios/AGENTS.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<!-- Parent: ../AGENTS.md -->
|
|
2
|
+
<!-- Generated: 2026-02-13 | Updated: 2026-02-13 -->
|
|
3
|
+
|
|
4
|
+
# ios
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
iOS native implementation of the NitroCookies HybridObject in Swift. Provides cookie management using `NSHTTPCookieStorage` (synchronous/default) and `WKHTTPCookieStore` (async WebKit operations). Conforms to the Nitrogen-generated `HybridNitroCookiesSpec`.
|
|
8
|
+
|
|
9
|
+
## Key Files
|
|
10
|
+
|
|
11
|
+
| File | Description |
|
|
12
|
+
|------|-------------|
|
|
13
|
+
| `NitroCookies.swift` | Full iOS implementation: `HybridNitroCookies` class extending `HybridNitroCookiesSpec` with sync + async cookie CRUD, URL validation, domain matching, WebKit support |
|
|
14
|
+
|
|
15
|
+
## For AI Agents
|
|
16
|
+
|
|
17
|
+
### Working In This Directory
|
|
18
|
+
- The class `HybridNitroCookies` extends the generated `HybridNitroCookiesSpec` (from `nitrogen/generated/ios/`)
|
|
19
|
+
- Two cookie storage backends:
|
|
20
|
+
- **NSHTTPCookieStorage** (shared): used for all sync methods and async methods when `useWebKit == false`
|
|
21
|
+
- **WKHTTPCookieStore**: used for async methods when `useWebKit == true` (iOS 11+ required)
|
|
22
|
+
- WebKit operations require `MainActor.run` to access `WKWebsiteDataStore.default().httpCookieStore` safely across iOS versions
|
|
23
|
+
- Date conversion uses `ISO8601DateFormatter` with fractional seconds for cookie `expires` fields
|
|
24
|
+
- Platform-only methods (`flush`, `removeSessionCookies`) throw `PLATFORM_UNSUPPORTED` errors on iOS
|
|
25
|
+
- Domain matching logic: exact match, wildcard (`.example.com`), and subdomain matching
|
|
26
|
+
|
|
27
|
+
### Testing Requirements
|
|
28
|
+
- No unit tests in this directory -- test via the `example/` iOS app
|
|
29
|
+
- Test both NSHTTPCookieStorage and WKHTTPCookieStore paths
|
|
30
|
+
- Verify iOS 11+ availability checks for WebKit operations
|
|
31
|
+
|
|
32
|
+
### Common Patterns
|
|
33
|
+
- `Promise.async { ... }` wraps async work into Nitro Promises
|
|
34
|
+
- `withCheckedContinuation` bridges callback-based WebKit APIs to Swift concurrency
|
|
35
|
+
- `validateURL()` and `validateDomain()` are called at the start of every operation
|
|
36
|
+
- Cookie struct conversion: `makeHTTPCookie(from:url:)` and `createCookieData(from:)`
|
|
37
|
+
|
|
38
|
+
## Dependencies
|
|
39
|
+
|
|
40
|
+
### Internal
|
|
41
|
+
- `nitrogen/generated/ios/` -- `HybridNitroCookiesSpec`, `Cookie` type bridges
|
|
42
|
+
- `nitrogen/generated/shared/c++/` -- C++ spec and Cookie struct
|
|
43
|
+
|
|
44
|
+
### External
|
|
45
|
+
- `Foundation` -- `HTTPCookieStorage`, `HTTPCookie`, `URLSession`
|
|
46
|
+
- `WebKit` -- `WKWebsiteDataStore`, `WKHTTPCookieStore`
|
|
47
|
+
- `NitroModules` -- `HybridObject` base, `Promise`
|
|
48
|
+
|
|
49
|
+
<!-- MANUAL: Any manually added notes below this line are preserved on regeneration -->
|
package/ios/NitroCookies.swift
CHANGED
|
@@ -25,6 +25,27 @@ public class HybridNitroCookies: HybridNitroCookiesSpec {
|
|
|
25
25
|
return formatter
|
|
26
26
|
}()
|
|
27
27
|
|
|
28
|
+
/// RFC 1123 date formatter for Set-Cookie Expires attribute
|
|
29
|
+
private static let rfc1123Formatter: DateFormatter = {
|
|
30
|
+
let formatter = DateFormatter()
|
|
31
|
+
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
32
|
+
formatter.timeZone = TimeZone(abbreviation: "GMT")
|
|
33
|
+
formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
|
|
34
|
+
return formatter
|
|
35
|
+
}()
|
|
36
|
+
|
|
37
|
+
/// Remove CR, LF, and NUL characters that could enable header injection
|
|
38
|
+
private static func sanitizeCookieToken(_ value: String) -> String {
|
|
39
|
+
var result = ""
|
|
40
|
+
result.reserveCapacity(value.count)
|
|
41
|
+
for scalar in value.unicodeScalars {
|
|
42
|
+
if scalar != "\r" && scalar != "\n" && scalar != "\0" {
|
|
43
|
+
result.append(String(scalar))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result
|
|
47
|
+
}
|
|
48
|
+
|
|
28
49
|
// MARK: - Helper Functions
|
|
29
50
|
|
|
30
51
|
/**
|
|
@@ -42,24 +63,49 @@ public class HybridNitroCookies: HybridNitroCookiesSpec {
|
|
|
42
63
|
.path: cookiePath
|
|
43
64
|
]
|
|
44
65
|
|
|
45
|
-
// Add expires if provided
|
|
46
66
|
if let expiresString = cookie.expires,
|
|
47
67
|
let expiresDate = Self.iso8601Formatter.date(from: expiresString) {
|
|
48
68
|
properties[.expires] = expiresDate
|
|
49
69
|
}
|
|
50
70
|
|
|
51
|
-
// Add secure flag
|
|
52
71
|
if cookie.secure == true {
|
|
53
72
|
properties[.secure] = "TRUE"
|
|
54
73
|
}
|
|
55
74
|
|
|
56
|
-
// Note: HttpOnly is handled via the isHTTPOnly property, not in properties dict
|
|
57
|
-
|
|
58
75
|
guard let httpCookie = HTTPCookie(properties: properties) else {
|
|
59
76
|
throw NSError(domain: "NitroCookies", code: 1,
|
|
60
77
|
userInfo: [NSLocalizedDescriptionKey: "Failed to create HTTPCookie"])
|
|
61
78
|
}
|
|
62
79
|
|
|
80
|
+
// HTTPCookie(properties:) ignores httpOnly — there is no property key for it.
|
|
81
|
+
// When httpOnly is requested, rebuild via Set-Cookie header parsing which
|
|
82
|
+
// correctly handles the HttpOnly attribute, while preserving the original domain.
|
|
83
|
+
if cookie.httpOnly == true {
|
|
84
|
+
let originalDomain = httpCookie.domain
|
|
85
|
+
let safeName = Self.sanitizeCookieToken(httpCookie.name)
|
|
86
|
+
let safeValue = Self.sanitizeCookieToken(httpCookie.value)
|
|
87
|
+
var parts: [String] = ["\(safeName)=\(safeValue)"]
|
|
88
|
+
parts.append("Domain=\(originalDomain)")
|
|
89
|
+
parts.append("Path=\(httpCookie.path)")
|
|
90
|
+
if let expires = httpCookie.expiresDate {
|
|
91
|
+
parts.append("Expires=\(Self.rfc1123Formatter.string(from: expires))")
|
|
92
|
+
}
|
|
93
|
+
if httpCookie.isSecure {
|
|
94
|
+
parts.append("Secure")
|
|
95
|
+
}
|
|
96
|
+
parts.append("HttpOnly")
|
|
97
|
+
|
|
98
|
+
let setCookieHeader = parts.joined(separator: "; ")
|
|
99
|
+
let headerFields = ["Set-Cookie": setCookieHeader]
|
|
100
|
+
let parsed = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
|
|
101
|
+
|
|
102
|
+
guard let httpOnlyCookie = parsed.first else {
|
|
103
|
+
throw NSError(domain: "NitroCookies", code: 1,
|
|
104
|
+
userInfo: [NSLocalizedDescriptionKey: "Failed to create HTTPCookie with HttpOnly"])
|
|
105
|
+
}
|
|
106
|
+
return httpOnlyCookie
|
|
107
|
+
}
|
|
108
|
+
|
|
63
109
|
return httpCookie
|
|
64
110
|
}
|
|
65
111
|
|
|
@@ -71,11 +117,18 @@ public class HybridNitroCookies: HybridNitroCookiesSpec {
|
|
|
71
117
|
Self.iso8601Formatter.string(from: $0)
|
|
72
118
|
}
|
|
73
119
|
|
|
120
|
+
// Strip leading dot from domain — NSHTTPCookieStorage and Set-Cookie
|
|
121
|
+
// parsing add a dot prefix per RFC 6265, but callers expect the bare domain.
|
|
122
|
+
var domain = httpCookie.domain
|
|
123
|
+
if domain.hasPrefix(".") {
|
|
124
|
+
domain = String(domain.dropFirst())
|
|
125
|
+
}
|
|
126
|
+
|
|
74
127
|
return Cookie(
|
|
75
128
|
name: httpCookie.name,
|
|
76
129
|
value: httpCookie.value,
|
|
77
130
|
path: httpCookie.path,
|
|
78
|
-
domain:
|
|
131
|
+
domain: domain,
|
|
79
132
|
version: String(httpCookie.version),
|
|
80
133
|
expires: expiresString,
|
|
81
134
|
secure: httpCookie.isSecure,
|
|
@@ -95,7 +148,7 @@ public class HybridNitroCookies: HybridNitroCookiesSpec {
|
|
|
95
148
|
// Wildcard match (.example.com matches api.example.com)
|
|
96
149
|
if cookieDomain.hasPrefix(".") {
|
|
97
150
|
let domain = String(cookieDomain.dropFirst())
|
|
98
|
-
return urlHost.hasSuffix(domain) || urlHost == domain
|
|
151
|
+
return urlHost.hasSuffix("." + domain) || urlHost == domain
|
|
99
152
|
}
|
|
100
153
|
|
|
101
154
|
// Subdomain match (example.com matches api.example.com)
|
|
@@ -367,10 +420,8 @@ public class HybridNitroCookies: HybridNitroCookiesSpec {
|
|
|
367
420
|
userInfo: [NSLocalizedDescriptionKey: "Not HTTP response"])
|
|
368
421
|
}
|
|
369
422
|
|
|
370
|
-
let
|
|
371
|
-
|
|
372
|
-
for: url
|
|
373
|
-
)
|
|
423
|
+
let headerFields = httpResponse.allHeaderFields as? [String: String] ?? [:]
|
|
424
|
+
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
|
|
374
425
|
|
|
375
426
|
return cookies.map { self.createCookieData(from: $0) }
|
|
376
427
|
}
|
|
@@ -466,9 +517,7 @@ public class HybridNitroCookies: HybridNitroCookiesSpec {
|
|
|
466
517
|
*/
|
|
467
518
|
public func flush() throws -> Promise<Void> {
|
|
468
519
|
return Promise.async {
|
|
469
|
-
|
|
470
|
-
userInfo: [NSLocalizedDescriptionKey:
|
|
471
|
-
"flush() is only available on Android"])
|
|
520
|
+
// No-op on iOS - cookies are automatically persisted
|
|
472
521
|
}
|
|
473
522
|
}
|
|
474
523
|
|
|
@@ -477,9 +526,7 @@ public class HybridNitroCookies: HybridNitroCookiesSpec {
|
|
|
477
526
|
*/
|
|
478
527
|
public func removeSessionCookies() throws -> Promise<Bool> {
|
|
479
528
|
return Promise.async {
|
|
480
|
-
|
|
481
|
-
userInfo: [NSLocalizedDescriptionKey:
|
|
482
|
-
"removeSessionCookies() is only available on Android"])
|
|
529
|
+
return false
|
|
483
530
|
}
|
|
484
531
|
}
|
|
485
532
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<!-- Parent: ../AGENTS.md -->
|
|
2
|
+
<!-- Generated: 2026-02-13 | Updated: 2026-02-13 -->
|
|
3
|
+
|
|
4
|
+
# src
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
TypeScript source code for the `react-native-nitro-cookies` npm package. Contains the public API, Nitro HybridObject interface contract, type definitions, and tests. This is the entry point consumers import from.
|
|
8
|
+
|
|
9
|
+
## Key Files
|
|
10
|
+
|
|
11
|
+
| File | Description |
|
|
12
|
+
|------|-------------|
|
|
13
|
+
| `index.tsx` | Public API -- exports `NitroCookies` object wrapping the HybridObject with sync/async methods; converts Cookie arrays to Record dictionaries |
|
|
14
|
+
| `NitroCookies.nitro.ts` | Nitro HybridObject interface -- the **contract** that drives Nitrogen code generation for C++/Swift/Kotlin bridges |
|
|
15
|
+
| `types.ts` | Type definitions: `Cookie` interface (RFC 6265), `Cookies` type alias, `CookieErrorCode` enum, `CookieError` interface |
|
|
16
|
+
|
|
17
|
+
## Subdirectories
|
|
18
|
+
|
|
19
|
+
| Directory | Purpose |
|
|
20
|
+
|-----------|---------|
|
|
21
|
+
| `__tests__/` | Jest test suite (currently placeholder) |
|
|
22
|
+
|
|
23
|
+
## For AI Agents
|
|
24
|
+
|
|
25
|
+
### Working In This Directory
|
|
26
|
+
- `NitroCookies.nitro.ts` is the **single source of truth** for the native interface. Changing it requires running `yarn nitrogen` to regenerate all C++/Swift/Kotlin bridge code
|
|
27
|
+
- `index.tsx` is the **public API layer** that wraps the raw HybridObject. It converts `Cookie[]` arrays from native into `Cookies` (Record) dictionaries for backward compatibility with `@react-native-cookies/cookies`
|
|
28
|
+
- `types.ts` defines types used both by the public API and the Nitro interface
|
|
29
|
+
- The `useWebKit` parameter defaults to `false` in the public API (via `?? false`); the Nitro interface uses `optional<bool>`
|
|
30
|
+
|
|
31
|
+
### Testing Requirements
|
|
32
|
+
- Run `yarn test` to execute Jest tests
|
|
33
|
+
- The test suite is currently a placeholder -- add real unit tests when modifying the TypeScript API
|
|
34
|
+
- Platform-specific behavior must be tested in the `example/` app on real devices/simulators
|
|
35
|
+
|
|
36
|
+
### Common Patterns
|
|
37
|
+
- Sync methods: direct JSI calls returning values immediately (no Promise)
|
|
38
|
+
- Async methods: return `Promise` from the HybridObject, public API wraps with `async`
|
|
39
|
+
- Cookie conversion: `cookiesToDictionary()` helper converts `Cookie[]` to `Record<string, Cookie>`
|
|
40
|
+
- Default exports: both named (`NitroCookies`) and default export for consumer flexibility
|
|
41
|
+
|
|
42
|
+
## Dependencies
|
|
43
|
+
|
|
44
|
+
### Internal
|
|
45
|
+
- `react-native-nitro-modules` -- `NitroModules.createHybridObject`, `HybridObject` types
|
|
46
|
+
|
|
47
|
+
### External
|
|
48
|
+
- `react-native-nitro-modules` ^0.31.4
|
|
49
|
+
|
|
50
|
+
<!-- MANUAL: Any manually added notes below this line are preserved on regeneration -->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NitroCookies.nitro.d.ts","sourceRoot":"","sources":["../../../src/NitroCookies.nitro.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAKtC;;;;;;;;;;GAUG;AACH,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"NitroCookies.nitro.d.ts","sourceRoot":"","sources":["../../../src/NitroCookies.nitro.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAKtC;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAa,SAAQ,YAAY,CAAC;IACjD,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;IAKA;;;;;;;;;OASG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAE9C;;;;;;;OAOG;IACH,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAEzD;;;;;;;OAOG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAMpD;;;;;;;OAOG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAExE;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEzD;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhD;;;;;;OAMG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9D;;;;;OAKG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE/C;;;;;;;OAOG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9E;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;OAIG;IACH,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// JHybridNitroCookiesSpec.cpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#include "JHybridNitroCookiesSpec.hpp"
|
|
@@ -17,6 +17,7 @@ namespace margelo::nitro::nitrocookies { struct Cookie; }
|
|
|
17
17
|
#include <optional>
|
|
18
18
|
#include <NitroModules/Promise.hpp>
|
|
19
19
|
#include <NitroModules/JPromise.hpp>
|
|
20
|
+
#include <NitroModules/JUnit.hpp>
|
|
20
21
|
|
|
21
22
|
namespace margelo::nitro::nitrocookies {
|
|
22
23
|
|
|
@@ -35,6 +36,13 @@ namespace margelo::nitro::nitrocookies {
|
|
|
35
36
|
return method(_javaPart);
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
bool JHybridNitroCookiesSpec::equals(const std::shared_ptr<HybridObject>& other) {
|
|
40
|
+
if (auto otherCast = std::dynamic_pointer_cast<JHybridNitroCookiesSpec>(other)) {
|
|
41
|
+
return _javaPart == otherCast->_javaPart;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
void JHybridNitroCookiesSpec::dispose() noexcept {
|
|
39
47
|
static const auto method = javaClassStatic()->getMethod<void()>("dispose");
|
|
40
48
|
method(_javaPart);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// HybridNitroCookiesSpec.hpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
@@ -40,6 +40,7 @@ namespace margelo::nitro::nitrocookies {
|
|
|
40
40
|
|
|
41
41
|
public:
|
|
42
42
|
size_t getExternalMemorySize() noexcept override;
|
|
43
|
+
bool equals(const std::shared_ptr<HybridObject>& other) override;
|
|
43
44
|
void dispose() noexcept override;
|
|
44
45
|
std::string toString() override;
|
|
45
46
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// Cookie.kt
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
package com.margelo.nitro.nitrocookies
|
|
@@ -44,7 +44,7 @@ data class Cookie(
|
|
|
44
44
|
) {
|
|
45
45
|
/* primary constructor */
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
companion object {
|
|
48
48
|
/**
|
|
49
49
|
* Constructor called from C++
|
|
50
50
|
*/
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/HybridNitroCookiesSpec.kt
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// HybridNitroCookiesSpec.kt
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
package com.margelo.nitro.nitrocookies
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/nitrocookiesOnLoad.kt
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// nitrocookiesOnLoad.kt
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
package com.margelo.nitro.nitrocookies
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# nitrocookies+autolinking.cmake
|
|
3
3
|
# This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
# https://github.com/mrousavy/nitro
|
|
5
|
-
# Copyright ©
|
|
5
|
+
# Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
#
|
|
7
7
|
|
|
8
8
|
# This is a CMake file that adds all files generated by Nitrogen
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// nitrocookies+autolinking.gradle
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
/// This is a Gradle file that adds all files generated by Nitrogen
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// nitrocookiesOnLoad.cpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#ifndef BUILDING_NITROCOOKIES_WITH_GENERATED_CMAKE_PROJECT
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# NitroCookies+autolinking.rb
|
|
3
3
|
# This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
# https://github.com/mrousavy/nitro
|
|
5
|
-
# Copyright ©
|
|
5
|
+
# Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
#
|
|
7
7
|
|
|
8
8
|
# This is a Ruby script that adds all files generated by Nitrogen
|
|
@@ -52,7 +52,7 @@ def add_nitrogen_files(spec)
|
|
|
52
52
|
spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({
|
|
53
53
|
# Use C++ 20
|
|
54
54
|
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
|
|
55
|
-
# Enables C++ <-> Swift interop (by default it's only
|
|
55
|
+
# Enables C++ <-> Swift interop (by default it's only ObjC)
|
|
56
56
|
"SWIFT_OBJC_INTEROP_MODE" => "objcxx",
|
|
57
57
|
# Enables stricter modular headers
|
|
58
58
|
"DEFINES_MODULE" => "YES",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// NitroCookies-Swift-Cxx-Bridge.cpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#include "NitroCookies-Swift-Cxx-Bridge.hpp"
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// Include C++ implementation defined types
|
|
11
11
|
#include "HybridNitroCookiesSpecSwift.hpp"
|
|
12
12
|
#include "NitroCookies-Swift-Cxx-Umbrella.hpp"
|
|
13
|
+
#include <NitroModules/NitroDefines.hpp>
|
|
13
14
|
|
|
14
15
|
namespace margelo::nitro::nitrocookies::bridge::swift {
|
|
15
16
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// NitroCookies-Swift-Cxx-Bridge.hpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
@@ -48,7 +48,7 @@ namespace margelo::nitro::nitrocookies::bridge::swift {
|
|
|
48
48
|
return optional.has_value();
|
|
49
49
|
}
|
|
50
50
|
inline std::string get_std__optional_std__string_(const std::optional<std::string>& optional) noexcept {
|
|
51
|
-
return
|
|
51
|
+
return optional.value();
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// pragma MARK: std::optional<bool>
|
|
@@ -63,7 +63,7 @@ namespace margelo::nitro::nitrocookies::bridge::swift {
|
|
|
63
63
|
return optional.has_value();
|
|
64
64
|
}
|
|
65
65
|
inline bool get_std__optional_bool_(const std::optional<bool>& optional) noexcept {
|
|
66
|
-
return
|
|
66
|
+
return optional.value();
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// pragma MARK: std::vector<Cookie>
|