@exxili/capacitor-nfc 0.0.12 → 0.0.13
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/README.md +93 -10
- package/android/src/main/kotlin/com/exxili/capacitornfc/NFCPlugin.kt +33 -4
- package/dist/esm/definitions.d.ts +40 -3
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +29 -11
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +3 -2
- package/dist/esm/web.js +2 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +29 -11
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +29 -11
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NFCPlugin/NFCPlugin.swift +35 -9
- package/ios/Sources/NFCPlugin/NFCReader.swift +310 -41
- package/ios/Sources/NFCPlugin/NFCWriter.swift +2 -2
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -63,6 +63,25 @@ In Xcode:
|
|
|
63
63
|
5. Click the `+ Capability` button.
|
|
64
64
|
6. Add **Near Field Communication Tag Reading**.
|
|
65
65
|
|
|
66
|
+
> **Advanced tag formats:** If you need ISO 7816, ISO 15693, or FeliCa access (to read raw UIDs, system codes, etc.), Apple requires additional entitlements in your provisioning profile and `Info.plist`. The plugin will fall back automatically when they are absent, but to unlock the full feature set add the relevant keys:
|
|
67
|
+
>
|
|
68
|
+
> ```xml
|
|
69
|
+
> <key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
|
|
70
|
+
> <array>
|
|
71
|
+
> <string>12FC</string>
|
|
72
|
+
> <string>0000</string>
|
|
73
|
+
> </array>
|
|
74
|
+
> <key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
|
|
75
|
+
> <array>
|
|
76
|
+
> <string>D2760000850100</string>
|
|
77
|
+
> <string>D2760000850101</string>
|
|
78
|
+
> <string>D2760001180101</string>
|
|
79
|
+
> <string>00000000000000</string>
|
|
80
|
+
> </array>
|
|
81
|
+
> ```
|
|
82
|
+
>
|
|
83
|
+
> Replace the sample identifiers with the values required for your tags. Consult Apple's CoreNFC documentation for the complete list of entitlement keys.
|
|
84
|
+
|
|
66
85
|
### 2. Add Usage Description
|
|
67
86
|
|
|
68
87
|
Add the `NFCReaderUsageDescription` key to your `Info.plist` file to explain why your app needs access to NFC.
|
|
@@ -116,14 +135,17 @@ NFC.onRead((data: NDEFMessagesTransformable) => {
|
|
|
116
135
|
console.log('First record raw bytes length:', asUint8.messages[0]?.records[0]?.payload.length);
|
|
117
136
|
|
|
118
137
|
// Access tag information (UID, tech types, etc.)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.log('
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
138
|
+
const info = asString.tagInfo;
|
|
139
|
+
if (info?.fallback) {
|
|
140
|
+
console.log('Reader fallback mode:', info.fallbackMode, 'Reason:', info.reason);
|
|
141
|
+
} else if (info) {
|
|
142
|
+
console.log('Tag UID:', info.uid);
|
|
143
|
+
console.log('Tag technologies:', info.techTypes);
|
|
144
|
+
console.log('Tag type:', info.type);
|
|
145
|
+
if (info.maxSize) {
|
|
146
|
+
console.log('Max NDEF size:', info.maxSize);
|
|
125
147
|
}
|
|
126
|
-
console.log('Is writable:',
|
|
148
|
+
console.log('Is writable:', info.isWritable);
|
|
127
149
|
}
|
|
128
150
|
});
|
|
129
151
|
|
|
@@ -157,6 +179,21 @@ const message: NDEFWriteOptions = {
|
|
|
157
179
|
],
|
|
158
180
|
};
|
|
159
181
|
|
|
182
|
+
// For complete control over binary content, use raw mode:
|
|
183
|
+
const rawMessage: NDEFWriteOptions = {
|
|
184
|
+
rawMode: true, // Bypasses automatic Text/URI formatting
|
|
185
|
+
records: [
|
|
186
|
+
{
|
|
187
|
+
type: 'T',
|
|
188
|
+
payload: 'Hello, NFC!', // Written as UTF-8 bytes without Text record prefix
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: 'custom',
|
|
192
|
+
payload: new Uint8Array([0x01, 0x02, 0x03, 0x04]), // Exact bytes written to tag
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
|
|
160
197
|
// Write NDEF message to NFC tag
|
|
161
198
|
NFC.writeNDEF(message)
|
|
162
199
|
.then(() => {
|
|
@@ -191,10 +228,21 @@ Returns if NFC is supported on the scanning device.
|
|
|
191
228
|
|
|
192
229
|
Starts the NFC scanning session on **_iOS only_**. Android devices are always in reading mode, so setting up the `nfcTag` listener is sufficient to handle tag reads on Android.
|
|
193
230
|
|
|
231
|
+
The iOS implementation now adapts automatically if the extended CoreNFC entitlements (ISO 7816, ISO 15693, FeliCa) are missing. The plugin first attempts the advanced tag reader so you can access UID/tech info. When iOS reports `Missing required entitlement`, the plugin downgrades to a compatibility mode (ISO 14443 only) and, if necessary, to the classic NDEF reader. A synthetic `nfcTag` event is emitted with `tagInfo.fallback`, `tagInfo.fallbackMode`, and `tagInfo.reason` so your UI can react immediately.
|
|
232
|
+
|
|
233
|
+
You can override the mode explicitly:
|
|
234
|
+
|
|
235
|
+
- `mode: 'auto'` (default) – advanced reader with automatic downgrade and caching.
|
|
236
|
+
- `mode: 'full'` – force a fresh attempt at the advanced reader, resetting cached fallback state.
|
|
237
|
+
- `mode: 'compat'` – skip the advanced probe and jump straight to the ISO 14443 compatibility reader.
|
|
238
|
+
- `mode: 'ndef'` – bypass tag sessions entirely and revert to the legacy NDEF-only reader.
|
|
239
|
+
|
|
240
|
+
Legacy booleans `forceFull`, `forceCompat`, and `forceNDEF` map to the options above for backwards compatibility.
|
|
241
|
+
|
|
194
242
|
**Returns**: `Promise<void>`
|
|
195
243
|
|
|
196
244
|
```typescript
|
|
197
|
-
NFC.startScan()
|
|
245
|
+
NFC.startScan({ mode: 'auto' })
|
|
198
246
|
.then(() => {
|
|
199
247
|
// Scanning started
|
|
200
248
|
})
|
|
@@ -233,6 +281,8 @@ Automatic formatting rules (to aid interoperability):
|
|
|
233
281
|
- Any other `type` + string payload: UTF-8 bytes only (no extra framing).
|
|
234
282
|
- `Uint8Array` or `number[]` payloads are treated as raw bytes and written verbatim (never altered).
|
|
235
283
|
|
|
284
|
+
**Raw Mode**: Set `rawMode: true` to bypass automatic Well Known Type formatting entirely. All string payloads will be written as UTF-8 bytes without Text ('T') or URI ('U') prefixes, giving you complete control over the binary content.
|
|
285
|
+
|
|
236
286
|
If you need full manual control of a Text or URI record, supply raw bytes (number[] / Uint8Array) and the plugin will not modify them.
|
|
237
287
|
|
|
238
288
|
If you attempt to write zero records the promise rejects with `Error("At least one NDEF record is required")`.
|
|
@@ -328,6 +378,11 @@ Options for writing an NDEF message.
|
|
|
328
378
|
```typescript
|
|
329
379
|
interface NDEFWriteOptions<T extends string | number[] | Uint8Array = string> {
|
|
330
380
|
records: NDEFRecord<T>[];
|
|
381
|
+
/**
|
|
382
|
+
* When true, bypasses automatic Well Known Type formatting (Text 'T' and URI 'U' prefixes).
|
|
383
|
+
* All payloads are written as raw bytes without additional framing.
|
|
384
|
+
*/
|
|
385
|
+
rawMode?: boolean;
|
|
331
386
|
}
|
|
332
387
|
```
|
|
333
388
|
|
|
@@ -369,12 +424,12 @@ interface TagInfo {
|
|
|
369
424
|
/**
|
|
370
425
|
* The unique identifier of the tag (UID) as a hex string
|
|
371
426
|
*/
|
|
372
|
-
uid
|
|
427
|
+
uid?: string;
|
|
373
428
|
|
|
374
429
|
/**
|
|
375
430
|
* The NFC tag technology types supported
|
|
376
431
|
*/
|
|
377
|
-
techTypes
|
|
432
|
+
techTypes?: string[];
|
|
378
433
|
|
|
379
434
|
/**
|
|
380
435
|
* The maximum size of NDEF message that can be written to this tag (if applicable)
|
|
@@ -390,6 +445,34 @@ interface TagInfo {
|
|
|
390
445
|
* The tag type (e.g., "ISO14443-4", "MifareClassic", etc.)
|
|
391
446
|
*/
|
|
392
447
|
type?: string;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Present when the plugin downgraded capabilities for compatibility.
|
|
451
|
+
*/
|
|
452
|
+
fallback?: boolean;
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Which fallback strategy is in use (`compat` or `ndef`).
|
|
456
|
+
*/
|
|
457
|
+
fallbackMode?: 'compat' | 'ndef';
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Reason metadata (e.g., `missing-entitlement`).
|
|
461
|
+
*/
|
|
462
|
+
reason?: string;
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
#### `StartScanOptions`
|
|
467
|
+
|
|
468
|
+
Optional tweaks for the iOS reader behavior.
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
interface StartScanOptions {
|
|
472
|
+
mode?: 'auto' | 'full' | 'compat' | 'ndef';
|
|
473
|
+
forceFull?: boolean;
|
|
474
|
+
forceCompat?: boolean;
|
|
475
|
+
forceNDEF?: boolean;
|
|
393
476
|
}
|
|
394
477
|
```
|
|
395
478
|
|
|
@@ -186,20 +186,49 @@ class NFCPlugin : Plugin() {
|
|
|
186
186
|
return
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
val typeBytes = type.toByteArray(Charsets.UTF_8)
|
|
190
189
|
val payloadBytes = ByteArray(payload.length())
|
|
191
190
|
for(i in 0 until payload.length()) {
|
|
192
191
|
payloadBytes[i] = payload.getInt(i).toByte()
|
|
193
192
|
}
|
|
194
193
|
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
val (tnf, typeBytes) = when {
|
|
195
|
+
type == "T" || type == "U" -> Pair(
|
|
197
196
|
NdefRecord.TNF_WELL_KNOWN,
|
|
197
|
+
type.toByteArray(Charsets.UTF_8)
|
|
198
|
+
)
|
|
199
|
+
type.contains("/") -> Pair(
|
|
200
|
+
NdefRecord.TNF_MIME_MEDIA,
|
|
201
|
+
type.toByteArray(Charsets.US_ASCII)
|
|
202
|
+
)
|
|
203
|
+
else -> Pair(
|
|
204
|
+
NdefRecord.TNF_EXTERNAL_TYPE,
|
|
205
|
+
type.toByteArray(Charsets.UTF_8)
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
val record = if (tnf == NdefRecord.TNF_MIME_MEDIA) {
|
|
210
|
+
try {
|
|
211
|
+
NdefRecord.createMime(type, payloadBytes)
|
|
212
|
+
} catch (e: IllegalArgumentException) {
|
|
213
|
+
notifyListeners(
|
|
214
|
+
"nfcError",
|
|
215
|
+
JSObject().put(
|
|
216
|
+
"error",
|
|
217
|
+
"Invalid MIME type for record"
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
NdefRecord(
|
|
224
|
+
tnf,
|
|
198
225
|
typeBytes,
|
|
199
226
|
ByteArray(0),
|
|
200
227
|
payloadBytes
|
|
201
228
|
)
|
|
202
|
-
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
ndefRecords.add(record)
|
|
203
232
|
}
|
|
204
233
|
|
|
205
234
|
val ndefMessage = NdefMessage(ndefRecords.toTypedArray())
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { PluginListenerHandle } from '@capacitor/core';
|
|
2
2
|
export type PayloadType = string | number[] | Uint8Array;
|
|
3
|
+
export interface StartScanOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Select the native reader strategy.
|
|
6
|
+
* - `auto` (default): attempt advanced tag session first, downgrade automatically on entitlement failures.
|
|
7
|
+
* - `full`: force the advanced tag session (resets any cached fallback state).
|
|
8
|
+
* - `compat`: force the compatibility tag session (ISO14443-only, avoids advanced entitlements).
|
|
9
|
+
* - `ndef`: skip tag session entirely and use the legacy NDEF reader.
|
|
10
|
+
*/
|
|
11
|
+
mode?: 'auto' | 'full' | 'compat' | 'ndef';
|
|
12
|
+
/**
|
|
13
|
+
* Backwards-compatible hints for older app code. When true, they map to `mode` selections above.
|
|
14
|
+
*/
|
|
15
|
+
forceFull?: boolean;
|
|
16
|
+
forceCompat?: boolean;
|
|
17
|
+
forceNDEF?: boolean;
|
|
18
|
+
}
|
|
3
19
|
export interface NFCPluginBasic {
|
|
4
20
|
/**
|
|
5
21
|
* Checks if NFC is supported on the device. Returns true on all iOS devices, and checks for support on Android.
|
|
@@ -7,7 +23,11 @@ export interface NFCPluginBasic {
|
|
|
7
23
|
isSupported(): Promise<{
|
|
8
24
|
supported: boolean;
|
|
9
25
|
}>;
|
|
10
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Begins listening for NFC tags.
|
|
28
|
+
* @param options Optional tuning parameters for native reader behavior.
|
|
29
|
+
*/
|
|
30
|
+
startScan(options?: StartScanOptions): Promise<void>;
|
|
11
31
|
/**
|
|
12
32
|
* Cancels an ongoing scan session (iOS only currently; no-op / rejection on Android).
|
|
13
33
|
*/
|
|
@@ -56,11 +76,11 @@ export interface TagInfo {
|
|
|
56
76
|
/**
|
|
57
77
|
* The unique identifier of the tag (UID) as a hex string
|
|
58
78
|
*/
|
|
59
|
-
uid
|
|
79
|
+
uid?: string;
|
|
60
80
|
/**
|
|
61
81
|
* The NFC tag technology types supported
|
|
62
82
|
*/
|
|
63
|
-
techTypes
|
|
83
|
+
techTypes?: string[];
|
|
64
84
|
/**
|
|
65
85
|
* The maximum size of NDEF message that can be written to this tag (if applicable)
|
|
66
86
|
*/
|
|
@@ -73,6 +93,18 @@ export interface TagInfo {
|
|
|
73
93
|
* The tag type (e.g., "ISO14443-4", "MifareClassic", etc.)
|
|
74
94
|
*/
|
|
75
95
|
type?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Truthy when the plugin downgraded reader capabilities for compatibility.
|
|
98
|
+
*/
|
|
99
|
+
fallback?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Indicates the active fallback mode (`compat` or `ndef`).
|
|
102
|
+
*/
|
|
103
|
+
fallbackMode?: 'compat' | 'ndef';
|
|
104
|
+
/**
|
|
105
|
+
* Optional reason string when fallback was applied (e.g., `missing-entitlement`).
|
|
106
|
+
*/
|
|
107
|
+
reason?: string;
|
|
76
108
|
}
|
|
77
109
|
export interface NDEFRecord<T extends PayloadType = string> {
|
|
78
110
|
/**
|
|
@@ -92,6 +124,11 @@ export interface NFCError {
|
|
|
92
124
|
}
|
|
93
125
|
export interface NDEFWriteOptions<T extends PayloadType = Uint8Array> {
|
|
94
126
|
records: NDEFRecord<T>[];
|
|
127
|
+
/**
|
|
128
|
+
* When true, bypasses automatic Well Known Type formatting (Text 'T' and URI 'U' prefixes).
|
|
129
|
+
* All payloads are written as raw bytes without additional framing.
|
|
130
|
+
*/
|
|
131
|
+
rawMode?: boolean;
|
|
95
132
|
}
|
|
96
133
|
export type NDEFMessagesTransformable = {
|
|
97
134
|
base64: () => NDEFMessages;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n// Payload from a new NFC scan is a base64 encoded string\nexport type PayloadType = string | number[] | Uint8Array;\n\nexport interface NFCPluginBasic {\n /**\n * Checks if NFC is supported on the device. Returns true on all iOS devices, and checks for support on Android.\n */\n isSupported(): Promise<{ supported: boolean }>;\n\n startScan(): Promise<void>;\n\n /**\n * Cancels an ongoing scan session (iOS only currently; no-op / rejection on Android).\n */\n cancelScan(): Promise<void>;\n\n /**\n * Writes an NDEF message to an NFC tag.\n * @param options The NDEF message to write.\n */\n writeNDEF<T extends PayloadType = number[]>(options: NDEFWriteOptions<T>): Promise<void>;\n\n /**\n * Cancels writeNDEF on Android (exits \"write mode\").\n */\n cancelWriteAndroid(): Promise<void>;\n\n /**\n * Adds a listener for NFC tag detection events.\n * @param eventName The name of the event ('nfcTag').\n * @param listenerFunc The function to call when an NFC tag is detected.\n */\n addListener(\n eventName: 'nfcTag',\n listenerFunc: (data: NDEFMessages) => void,\n ): Promise<PluginListenerHandle> & PluginListenerHandle;\n\n /**\n * Adds a listener for NFC tag write events.\n * @param eventName The name of the event ('nfcWriteSuccess').\n * @param listenerFunc The function to call when an NFC tag is written.\n */\n addListener(\n eventName: 'nfcWriteSuccess',\n listenerFunc: () => void,\n ): Promise<PluginListenerHandle> & PluginListenerHandle;\n\n /**\n * Adds a listener for NFC error events.\n * @param eventName The name of the event ('nfcError').\n * @param listenerFunc The function to call when an NFC error occurs.\n */\n addListener(\n eventName: 'nfcError',\n listenerFunc: (error: NFCError) => void,\n ): Promise<PluginListenerHandle> & PluginListenerHandle;\n\n /**\n * Removes all listeners for the specified event.\n * @param eventName The name of the event.\n */\n removeAllListeners(eventName: 'nfcTag' | 'nfcError'): Promise<void>;\n}\n\nexport interface NDEFMessages<T extends PayloadType = string> {\n messages: NDEFMessage<T>[];\n tagInfo?: TagInfo;\n}\n\nexport interface NDEFMessage<T extends PayloadType = string> {\n records: NDEFRecord<T>[];\n}\n\nexport interface TagInfo {\n /**\n * The unique identifier of the tag (UID) as a hex string\n */\n uid
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n// Payload from a new NFC scan is a base64 encoded string\nexport type PayloadType = string | number[] | Uint8Array;\n\nexport interface StartScanOptions {\n /**\n * Select the native reader strategy.\n * - `auto` (default): attempt advanced tag session first, downgrade automatically on entitlement failures.\n * - `full`: force the advanced tag session (resets any cached fallback state).\n * - `compat`: force the compatibility tag session (ISO14443-only, avoids advanced entitlements).\n * - `ndef`: skip tag session entirely and use the legacy NDEF reader.\n */\n mode?: 'auto' | 'full' | 'compat' | 'ndef';\n /**\n * Backwards-compatible hints for older app code. When true, they map to `mode` selections above.\n */\n forceFull?: boolean;\n forceCompat?: boolean;\n forceNDEF?: boolean;\n}\n\nexport interface NFCPluginBasic {\n /**\n * Checks if NFC is supported on the device. Returns true on all iOS devices, and checks for support on Android.\n */\n isSupported(): Promise<{ supported: boolean }>;\n\n /**\n * Begins listening for NFC tags.\n * @param options Optional tuning parameters for native reader behavior.\n */\n startScan(options?: StartScanOptions): Promise<void>;\n\n /**\n * Cancels an ongoing scan session (iOS only currently; no-op / rejection on Android).\n */\n cancelScan(): Promise<void>;\n\n /**\n * Writes an NDEF message to an NFC tag.\n * @param options The NDEF message to write.\n */\n writeNDEF<T extends PayloadType = number[]>(options: NDEFWriteOptions<T>): Promise<void>;\n\n /**\n * Cancels writeNDEF on Android (exits \"write mode\").\n */\n cancelWriteAndroid(): Promise<void>;\n\n /**\n * Adds a listener for NFC tag detection events.\n * @param eventName The name of the event ('nfcTag').\n * @param listenerFunc The function to call when an NFC tag is detected.\n */\n addListener(\n eventName: 'nfcTag',\n listenerFunc: (data: NDEFMessages) => void,\n ): Promise<PluginListenerHandle> & PluginListenerHandle;\n\n /**\n * Adds a listener for NFC tag write events.\n * @param eventName The name of the event ('nfcWriteSuccess').\n * @param listenerFunc The function to call when an NFC tag is written.\n */\n addListener(\n eventName: 'nfcWriteSuccess',\n listenerFunc: () => void,\n ): Promise<PluginListenerHandle> & PluginListenerHandle;\n\n /**\n * Adds a listener for NFC error events.\n * @param eventName The name of the event ('nfcError').\n * @param listenerFunc The function to call when an NFC error occurs.\n */\n addListener(\n eventName: 'nfcError',\n listenerFunc: (error: NFCError) => void,\n ): Promise<PluginListenerHandle> & PluginListenerHandle;\n\n /**\n * Removes all listeners for the specified event.\n * @param eventName The name of the event.\n */\n removeAllListeners(eventName: 'nfcTag' | 'nfcError'): Promise<void>;\n}\n\nexport interface NDEFMessages<T extends PayloadType = string> {\n messages: NDEFMessage<T>[];\n tagInfo?: TagInfo;\n}\n\nexport interface NDEFMessage<T extends PayloadType = string> {\n records: NDEFRecord<T>[];\n}\n\nexport interface TagInfo {\n /**\n * The unique identifier of the tag (UID) as a hex string\n */\n uid?: string;\n\n /**\n * The NFC tag technology types supported\n */\n techTypes?: string[];\n\n /**\n * The maximum size of NDEF message that can be written to this tag (if applicable)\n */\n maxSize?: number;\n\n /**\n * Whether the tag is writable\n */\n isWritable?: boolean;\n\n /**\n * The tag type (e.g., \"ISO14443-4\", \"MifareClassic\", etc.)\n */\n type?: string;\n\n /**\n * Truthy when the plugin downgraded reader capabilities for compatibility.\n */\n fallback?: boolean;\n\n /**\n * Indicates the active fallback mode (`compat` or `ndef`).\n */\n fallbackMode?: 'compat' | 'ndef';\n\n /**\n * Optional reason string when fallback was applied (e.g., `missing-entitlement`).\n */\n reason?: string;\n}\n\nexport interface NDEFRecord<T extends PayloadType = string> {\n /**\n * The type of the record.\n */\n type: string;\n\n /**\n * The payload of the record.\n */\n payload: T;\n}\n\nexport interface NFCError {\n /**\n * The error message.\n */\n error: string;\n}\n\nexport interface NDEFWriteOptions<T extends PayloadType = Uint8Array> {\n records: NDEFRecord<T>[];\n /**\n * When true, bypasses automatic Well Known Type formatting (Text 'T' and URI 'U' prefixes).\n * All payloads are written as raw bytes without additional framing.\n */\n rawMode?: boolean;\n}\n\nexport type NDEFMessagesTransformable = {\n base64: () => NDEFMessages;\n uint8Array: () => NDEFMessages<Uint8Array>;\n string: () => NDEFMessages;\n numberArray: () => NDEFMessages<number[]>;\n};\n\nexport type TagResultListenerFunc = (data: NDEFMessagesTransformable) => void;\n\nexport interface NFCPlugin extends Omit<NFCPluginBasic, 'writeNDEF' | 'addListener'> {\n writeNDEF: <T extends PayloadType = Uint8Array>(record?: NDEFWriteOptions<T>) => Promise<void>;\n wrapperListeners: TagResultListenerFunc[];\n /**\n * Register a read listener. Returns an unsubscribe function to remove just this listener.\n */\n onRead: (listenerFunc: TagResultListenerFunc) => () => void;\n /**\n * Register a write success listener. Returns an unsubscribe function.\n */\n onWrite: (listenerFunc: () => void) => () => void;\n /**\n * Register an error listener. Returns an unsubscribe function.\n */\n onError: (listenerFunc: (error: NFCError) => void) => () => void;\n}\n"]}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { NFCPlugin } from './definitions';
|
|
2
|
-
export * from './definitions';
|
|
1
|
+
import type { NFCPlugin } from './definitions.js';
|
|
2
|
+
export * from './definitions.js';
|
|
3
3
|
export declare const NFC: NFCPlugin;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
var _a, _b;
|
|
2
2
|
import { registerPlugin } from '@capacitor/core';
|
|
3
3
|
const NFCPlug = registerPlugin('NFC', {
|
|
4
|
-
|
|
4
|
+
// Explicit .js extension required under node16/nodenext module resolution for emitted ES modules.
|
|
5
|
+
web: () => import('./web.js').then((m) => new m.NFCWeb()),
|
|
5
6
|
});
|
|
6
|
-
export * from './definitions';
|
|
7
|
+
export * from './definitions.js';
|
|
7
8
|
export const NFC = {
|
|
8
9
|
isSupported: NFCPlug.isSupported.bind(NFCPlug),
|
|
9
|
-
startScan:
|
|
10
|
+
startScan: (options) => {
|
|
11
|
+
const normalizedOptions = {};
|
|
12
|
+
if (options) {
|
|
13
|
+
for (const [key, value] of Object.entries(options)) {
|
|
14
|
+
if (value !== undefined && value !== null) {
|
|
15
|
+
normalizedOptions[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return NFCPlug.startScan(normalizedOptions);
|
|
20
|
+
},
|
|
10
21
|
cancelScan: (_b = (_a = NFCPlug.cancelScan) === null || _a === void 0 ? void 0 : _a.bind(NFCPlug)) !== null && _b !== void 0 ? _b : (async () => {
|
|
11
22
|
/* Android no-op */
|
|
12
23
|
}),
|
|
@@ -65,20 +76,27 @@ export const NFC = {
|
|
|
65
76
|
const recordsArray = (_a = options === null || options === void 0 ? void 0 : options.records) !== null && _a !== void 0 ? _a : [];
|
|
66
77
|
if (recordsArray.length === 0)
|
|
67
78
|
throw new Error('At least one NDEF record is required');
|
|
79
|
+
const isRawMode = (options === null || options === void 0 ? void 0 : options.rawMode) === true;
|
|
68
80
|
const ndefMessage = {
|
|
69
81
|
records: recordsArray.map((record) => {
|
|
70
82
|
let payload = null;
|
|
71
83
|
if (typeof record.payload === 'string') {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
payload =
|
|
75
|
-
}
|
|
76
|
-
else if (record.type === 'U') {
|
|
77
|
-
payload = buildUriPayload(record.payload);
|
|
84
|
+
if (isRawMode) {
|
|
85
|
+
// Raw mode: write string payloads as UTF-8 bytes without any framing
|
|
86
|
+
payload = Array.from(new TextEncoder().encode(record.payload));
|
|
78
87
|
}
|
|
79
88
|
else {
|
|
80
|
-
//
|
|
81
|
-
|
|
89
|
+
// Apply spec-compliant formatting only for Well Known Text (T) & URI (U) types.
|
|
90
|
+
if (record.type === 'T') {
|
|
91
|
+
payload = buildTextPayload(record.payload);
|
|
92
|
+
}
|
|
93
|
+
else if (record.type === 'U') {
|
|
94
|
+
payload = buildUriPayload(record.payload);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Generic string: raw UTF-8 bytes (no extra framing)
|
|
98
|
+
payload = Array.from(new TextEncoder().encode(record.payload));
|
|
99
|
+
}
|
|
82
100
|
}
|
|
83
101
|
}
|
|
84
102
|
else if (Array.isArray(record.payload)) {
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type {\n NDEFMessagesTransformable,\n NDEFWriteOptions,\n NFCPlugin,\n NFCPluginBasic,\n PayloadType,\n TagResultListenerFunc,\n NFCError,\n NDEFMessages,\n} from './definitions';\n\nconst NFCPlug = registerPlugin<NFCPluginBasic>('NFC', {\n web: () => import('./web').then((m) => new m.NFCWeb()),\n});\nexport * from './definitions';\nexport const NFC: NFCPlugin = {\n isSupported: NFCPlug.isSupported.bind(NFCPlug),\n startScan: NFCPlug.startScan.bind(NFCPlug),\n cancelScan:\n NFCPlug.cancelScan?.bind(NFCPlug) ??\n (async () => {\n /* Android no-op */\n }),\n cancelWriteAndroid: NFCPlug.cancelWriteAndroid.bind(NFCPlug),\n onRead: (func: TagResultListenerFunc) => {\n NFC.wrapperListeners.push(func);\n // Return unsubscribe function\n return () => {\n NFC.wrapperListeners = NFC.wrapperListeners.filter((l) => l !== func);\n };\n },\n onWrite: (func: () => void) => {\n let handle: any;\n NFCPlug.addListener(`nfcWriteSuccess`, func).then((h) => (handle = h));\n return () => {\n try {\n handle?.remove?.();\n } catch {\n /* empty */\n }\n };\n },\n onError: (errorFn: (error: NFCError) => void) => {\n let handle: any;\n NFCPlug.addListener(`nfcError`, errorFn).then((h) => (handle = h));\n return () => {\n try {\n handle?.remove?.();\n } catch {\n /* empty */\n }\n };\n },\n removeAllListeners: (eventName: 'nfcTag' | 'nfcError') => {\n NFC.wrapperListeners = [];\n return NFCPlug.removeAllListeners(eventName);\n },\n wrapperListeners: [],\n\n async writeNDEF<T extends PayloadType = Uint8Array>(options?: NDEFWriteOptions<T>): Promise<void> {\n // Helper encoders for well-known record types (only applied to string payloads)\n const buildTextPayload = (text: string, lang = 'en'): number[] => {\n const langBytes = Array.from(new TextEncoder().encode(lang));\n const textBytes = Array.from(new TextEncoder().encode(text));\n const status = langBytes.length & 0x3f; // UTF-8 encoding, language length (<= 63)\n return [status, ...langBytes, ...textBytes];\n };\n const buildUriPayload = (uri: string, prefixCode = 0x00): number[] => {\n const uriBytes = Array.from(new TextEncoder().encode(uri));\n return [prefixCode, ...uriBytes];\n };\n\n const recordsArray = options?.records ?? [];\n if (recordsArray.length === 0) throw new Error('At least one NDEF record is required');\n\n const ndefMessage: NDEFWriteOptions<number[]> = {\n records: recordsArray.map((record) => {\n let payload: number[] | null = null;\n\n if (typeof record.payload === 'string') {\n // Apply spec-compliant formatting only for Well Known Text (T) & URI (U) types.\n if (record.type === 'T') {\n payload = buildTextPayload(record.payload);\n } else if (record.type === 'U') {\n payload = buildUriPayload(record.payload);\n } else {\n // Generic string: raw UTF-8 bytes (no extra framing)\n payload = Array.from(new TextEncoder().encode(record.payload));\n }\n } else if (Array.isArray(record.payload)) {\n // Assume already raw bytes; do NOT modify\n payload = record.payload as number[];\n } else if (record.payload instanceof Uint8Array) {\n payload = Array.from(record.payload);\n }\n\n if (!payload) throw new Error('Unsupported payload type');\n\n return { type: record.type, payload };\n }),\n };\n\n await NFCPlug.writeNDEF(ndefMessage);\n },\n};\n\n// ----- Payload transformation helpers -----\ntype DecodeSpecifier = 'b64' | 'string' | 'uint8Array' | 'numberArray';\ntype decodedType<T extends DecodeSpecifier> = NDEFMessages<\n T extends 'b64' ? string : T extends 'string' ? string : T extends 'uint8Array' ? Uint8Array : number[]\n>;\n\n// Decode a base64 string into a Uint8Array (browser-safe). Existing code used atob already.\nconst decodeBase64ToBytes = (base64Payload: string): Uint8Array => {\n const bin = atob(base64Payload);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n};\n\n// Parse NFC Forum \"Text\" (Well Known 'T') records according to spec.\nconst decodeTextRecord = (bytes: Uint8Array): string => {\n if (bytes.length === 0) return '';\n const status = bytes[0];\n const isUTF16 = (status & 0x80) !== 0; // Bit 7 indicates encoding\n const langLength = status & 0x3f; // Bits 0-5 language code length\n if (1 + langLength > bytes.length) return ''; // Corrupt\n const textBytes = bytes.slice(1 + langLength);\n try {\n const decoder = new TextDecoder(isUTF16 ? 'utf-16' : 'utf-8');\n return decoder.decode(textBytes);\n } catch {\n // Fallback: naive ASCII\n return Array.from(textBytes)\n .map((b) => String.fromCharCode(b))\n .join('');\n }\n};\n\n// Basic URI prefix table for Well Known 'U' records (optional convenience)\nconst URI_PREFIX: string[] = [\n '',\n 'http://www.',\n 'https://www.',\n 'http://',\n 'https://',\n 'tel:',\n 'mailto:',\n 'ftp://anonymous:anonymous@',\n 'ftp://ftp.',\n 'ftps://',\n 'sftp://',\n 'smb://',\n 'nfs://',\n 'ftp://',\n 'dav://',\n 'news:',\n 'telnet://',\n 'imap:',\n 'rtsp://',\n 'urn:',\n 'pop:',\n 'sip:',\n 'sips:',\n 'tftp:',\n 'btspp://',\n 'btl2cap://',\n 'btgoep://',\n 'tcpobex://',\n 'irdaobex://',\n 'file://',\n 'urn:epc:id:',\n 'urn:epc:tag:',\n 'urn:epc:pat:',\n 'urn:epc:raw:',\n 'urn:epc:',\n 'urn:nfc:',\n];\n\nconst decodeUriRecord = (bytes: Uint8Array): string => {\n if (bytes.length === 0) return '';\n const prefixIndex = bytes[0];\n const prefix = URI_PREFIX[prefixIndex] || '';\n const remainder = bytes.slice(1);\n try {\n return prefix + new TextDecoder('utf-8').decode(remainder);\n } catch {\n return (\n prefix +\n Array.from(remainder)\n .map((b) => String.fromCharCode(b))\n .join('')\n );\n }\n};\n\nconst toStringPayload = (recordType: string, bytes: Uint8Array): string => {\n // Well Known Text\n if (recordType === 'T') return decodeTextRecord(bytes);\n // Well Known URI\n if (recordType === 'U') return decodeUriRecord(bytes);\n // Default: attempt UTF-8 decode\n try {\n return new TextDecoder('utf-8').decode(bytes);\n } catch {\n return Array.from(bytes)\n .map((c) => String.fromCharCode(c))\n .join('');\n }\n};\n\nconst mapPayloadTo = <T extends DecodeSpecifier>(type: T, data: NDEFMessages): decodedType<T> => {\n return {\n messages: data.messages.map((message) => ({\n records: message.records.map((record) => {\n const bytes = decodeBase64ToBytes(record.payload as unknown as string);\n let payload: any;\n switch (type) {\n case 'b64':\n payload = record.payload; // original base64 string\n break;\n case 'uint8Array':\n payload = bytes;\n break;\n case 'numberArray':\n payload = Array.from(bytes);\n break;\n case 'string':\n payload = toStringPayload(record.type, bytes);\n break;\n default:\n payload = record.payload;\n }\n return { type: record.type, payload };\n }),\n })),\n tagInfo: data.tagInfo, // Include tag information\n } as decodedType<T>;\n};\n\nNFCPlug.addListener(`nfcTag`, (data) => {\n const wrappedData: NDEFMessagesTransformable = {\n base64() {\n return mapPayloadTo('b64', data);\n },\n string() {\n return mapPayloadTo('string', data);\n },\n uint8Array() {\n return mapPayloadTo('uint8Array', data);\n },\n numberArray() {\n return mapPayloadTo('numberArray', data);\n },\n };\n\n for (const listener of NFC.wrapperListeners) {\n listener(wrappedData);\n }\n});\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type {\n NDEFMessagesTransformable,\n NDEFWriteOptions,\n NFCPlugin,\n NFCPluginBasic,\n StartScanOptions,\n PayloadType,\n TagResultListenerFunc,\n NFCError,\n NDEFMessages,\n} from './definitions.js';\n\nconst NFCPlug = registerPlugin<NFCPluginBasic>('NFC', {\n // Explicit .js extension required under node16/nodenext module resolution for emitted ES modules.\n web: () => import('./web.js').then((m) => new m.NFCWeb()),\n});\nexport * from './definitions.js';\nexport const NFC: NFCPlugin = {\n isSupported: NFCPlug.isSupported.bind(NFCPlug),\n startScan: (options?: StartScanOptions) => {\n const normalizedOptions: Record<string, unknown> = {};\n if (options) {\n for (const [key, value] of Object.entries(options)) {\n if (value !== undefined && value !== null) {\n normalizedOptions[key] = value;\n }\n }\n }\n return NFCPlug.startScan(normalizedOptions);\n },\n cancelScan:\n NFCPlug.cancelScan?.bind(NFCPlug) ??\n (async () => {\n /* Android no-op */\n }),\n cancelWriteAndroid: NFCPlug.cancelWriteAndroid.bind(NFCPlug),\n onRead: (func: TagResultListenerFunc) => {\n NFC.wrapperListeners.push(func);\n // Return unsubscribe function\n return () => {\n NFC.wrapperListeners = NFC.wrapperListeners.filter((l: TagResultListenerFunc) => l !== func);\n };\n },\n onWrite: (func: () => void) => {\n let handle: any;\n NFCPlug.addListener(`nfcWriteSuccess`, func).then((h: any) => (handle = h));\n return () => {\n try {\n handle?.remove?.();\n } catch {\n /* empty */\n }\n };\n },\n onError: (errorFn: (error: NFCError) => void) => {\n let handle: any;\n NFCPlug.addListener(`nfcError`, errorFn).then((h: any) => (handle = h));\n return () => {\n try {\n handle?.remove?.();\n } catch {\n /* empty */\n }\n };\n },\n removeAllListeners: (eventName: 'nfcTag' | 'nfcError') => {\n NFC.wrapperListeners = [];\n return NFCPlug.removeAllListeners(eventName);\n },\n wrapperListeners: [],\n\n async writeNDEF<T extends PayloadType = Uint8Array>(options?: NDEFWriteOptions<T>): Promise<void> {\n // Helper encoders for well-known record types (only applied to string payloads)\n const buildTextPayload = (text: string, lang = 'en'): number[] => {\n const langBytes = Array.from(new TextEncoder().encode(lang));\n const textBytes = Array.from(new TextEncoder().encode(text));\n const status = langBytes.length & 0x3f; // UTF-8 encoding, language length (<= 63)\n return [status, ...langBytes, ...textBytes];\n };\n const buildUriPayload = (uri: string, prefixCode = 0x00): number[] => {\n const uriBytes = Array.from(new TextEncoder().encode(uri));\n return [prefixCode, ...uriBytes];\n };\n\n const recordsArray = options?.records ?? [];\n if (recordsArray.length === 0) throw new Error('At least one NDEF record is required');\n\n const isRawMode = options?.rawMode === true;\n\n const ndefMessage: NDEFWriteOptions<number[]> = {\n records: recordsArray.map((record: any) => {\n let payload: number[] | null = null;\n\n if (typeof record.payload === 'string') {\n if (isRawMode) {\n // Raw mode: write string payloads as UTF-8 bytes without any framing\n payload = Array.from(new TextEncoder().encode(record.payload));\n } else {\n // Apply spec-compliant formatting only for Well Known Text (T) & URI (U) types.\n if (record.type === 'T') {\n payload = buildTextPayload(record.payload);\n } else if (record.type === 'U') {\n payload = buildUriPayload(record.payload);\n } else {\n // Generic string: raw UTF-8 bytes (no extra framing)\n payload = Array.from(new TextEncoder().encode(record.payload));\n }\n }\n } else if (Array.isArray(record.payload)) {\n // Assume already raw bytes; do NOT modify\n payload = record.payload as number[];\n } else if (record.payload instanceof Uint8Array) {\n payload = Array.from(record.payload);\n }\n\n if (!payload) throw new Error('Unsupported payload type');\n\n return { type: record.type, payload };\n }),\n };\n\n await NFCPlug.writeNDEF(ndefMessage);\n },\n};\n\n// ----- Payload transformation helpers -----\ntype DecodeSpecifier = 'b64' | 'string' | 'uint8Array' | 'numberArray';\ntype decodedType<T extends DecodeSpecifier> = NDEFMessages<\n T extends 'b64' ? string : T extends 'string' ? string : T extends 'uint8Array' ? Uint8Array : number[]\n>;\n\n// Decode a base64 string into a Uint8Array (browser-safe). Existing code used atob already.\nconst decodeBase64ToBytes = (base64Payload: string): Uint8Array => {\n const bin = atob(base64Payload);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n};\n\n// Parse NFC Forum \"Text\" (Well Known 'T') records according to spec.\nconst decodeTextRecord = (bytes: Uint8Array): string => {\n if (bytes.length === 0) return '';\n const status = bytes[0];\n const isUTF16 = (status & 0x80) !== 0; // Bit 7 indicates encoding\n const langLength = status & 0x3f; // Bits 0-5 language code length\n if (1 + langLength > bytes.length) return ''; // Corrupt\n const textBytes = bytes.slice(1 + langLength);\n try {\n const decoder = new TextDecoder(isUTF16 ? 'utf-16' : 'utf-8');\n return decoder.decode(textBytes);\n } catch {\n // Fallback: naive ASCII\n return Array.from(textBytes)\n .map((b) => String.fromCharCode(b))\n .join('');\n }\n};\n\n// Basic URI prefix table for Well Known 'U' records (optional convenience)\nconst URI_PREFIX: string[] = [\n '',\n 'http://www.',\n 'https://www.',\n 'http://',\n 'https://',\n 'tel:',\n 'mailto:',\n 'ftp://anonymous:anonymous@',\n 'ftp://ftp.',\n 'ftps://',\n 'sftp://',\n 'smb://',\n 'nfs://',\n 'ftp://',\n 'dav://',\n 'news:',\n 'telnet://',\n 'imap:',\n 'rtsp://',\n 'urn:',\n 'pop:',\n 'sip:',\n 'sips:',\n 'tftp:',\n 'btspp://',\n 'btl2cap://',\n 'btgoep://',\n 'tcpobex://',\n 'irdaobex://',\n 'file://',\n 'urn:epc:id:',\n 'urn:epc:tag:',\n 'urn:epc:pat:',\n 'urn:epc:raw:',\n 'urn:epc:',\n 'urn:nfc:',\n];\n\nconst decodeUriRecord = (bytes: Uint8Array): string => {\n if (bytes.length === 0) return '';\n const prefixIndex = bytes[0];\n const prefix = URI_PREFIX[prefixIndex] || '';\n const remainder = bytes.slice(1);\n try {\n return prefix + new TextDecoder('utf-8').decode(remainder);\n } catch {\n return (\n prefix +\n Array.from(remainder)\n .map((b) => String.fromCharCode(b))\n .join('')\n );\n }\n};\n\nconst toStringPayload = (recordType: string, bytes: Uint8Array): string => {\n // Well Known Text\n if (recordType === 'T') return decodeTextRecord(bytes);\n // Well Known URI\n if (recordType === 'U') return decodeUriRecord(bytes);\n // Default: attempt UTF-8 decode\n try {\n return new TextDecoder('utf-8').decode(bytes);\n } catch {\n return Array.from(bytes)\n .map((c) => String.fromCharCode(c))\n .join('');\n }\n};\n\nconst mapPayloadTo = <T extends DecodeSpecifier>(type: T, data: NDEFMessages): decodedType<T> => {\n return {\n messages: data.messages.map((message: any) => ({\n records: message.records.map((record: any) => {\n const bytes = decodeBase64ToBytes(record.payload as unknown as string);\n let payload: any;\n switch (type) {\n case 'b64':\n payload = record.payload; // original base64 string\n break;\n case 'uint8Array':\n payload = bytes;\n break;\n case 'numberArray':\n payload = Array.from(bytes);\n break;\n case 'string':\n payload = toStringPayload(record.type, bytes);\n break;\n default:\n payload = record.payload;\n }\n return { type: record.type, payload };\n }),\n })),\n tagInfo: data.tagInfo, // Include tag information\n } as decodedType<T>;\n};\n\nNFCPlug.addListener(`nfcTag`, (data: any) => {\n const wrappedData: NDEFMessagesTransformable = {\n base64() {\n return mapPayloadTo('b64', data);\n },\n string() {\n return mapPayloadTo('string', data);\n },\n uint8Array() {\n return mapPayloadTo('uint8Array', data);\n },\n numberArray() {\n return mapPayloadTo('numberArray', data);\n },\n };\n\n for (const listener of NFC.wrapperListeners) {\n listener(wrappedData);\n }\n});\n"]}
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import type { StartScanOptions } from './definitions.js';
|
|
2
3
|
export declare class NFCWeb extends WebPlugin {
|
|
3
4
|
isSupported(): Promise<{
|
|
4
5
|
supported: boolean;
|
|
5
6
|
}>;
|
|
6
|
-
startScan(): Promise<void>;
|
|
7
|
+
startScan(_options?: StartScanOptions): Promise<void>;
|
|
7
8
|
cancelScan(): Promise<void>;
|
|
8
9
|
cancelWriteAndroid(): Promise<void>;
|
|
9
|
-
writeNDEF(): Promise<void>;
|
|
10
|
+
writeNDEF(_options?: any): Promise<void>;
|
|
10
11
|
}
|
package/dist/esm/web.js
CHANGED
|
@@ -3,7 +3,7 @@ export class NFCWeb extends WebPlugin {
|
|
|
3
3
|
async isSupported() {
|
|
4
4
|
return { supported: false };
|
|
5
5
|
}
|
|
6
|
-
async startScan() {
|
|
6
|
+
async startScan(_options) {
|
|
7
7
|
throw new Error('NFC is not supported on web');
|
|
8
8
|
}
|
|
9
9
|
async cancelScan() {
|
|
@@ -12,7 +12,7 @@ export class NFCWeb extends WebPlugin {
|
|
|
12
12
|
async cancelWriteAndroid() {
|
|
13
13
|
throw new Error('NFC is not supported on web');
|
|
14
14
|
}
|
|
15
|
-
async writeNDEF() {
|
|
15
|
+
async writeNDEF(_options) {
|
|
16
16
|
throw new Error('NFC is not supported on web');
|
|
17
17
|
}
|
|
18
18
|
}
|
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,MAAO,SAAQ,SAAS;IACnC,KAAK,CAAC,WAAW;QACf,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAA2B;QACzC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAc;QAC5B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type { StartScanOptions } from './definitions.js';\n\nexport class NFCWeb extends WebPlugin {\n async isSupported(): Promise<{ supported: boolean }> {\n return { supported: false };\n }\n\n async startScan(_options?: StartScanOptions): Promise<void> {\n throw new Error('NFC is not supported on web');\n }\n\n async cancelScan(): Promise<void> {\n throw new Error('NFC is not supported on web');\n }\n\n async cancelWriteAndroid(): Promise<void> {\n throw new Error('NFC is not supported on web');\n }\n\n async writeNDEF(_options?: any): Promise<void> {\n throw new Error('NFC is not supported on web');\n }\n}\n"]}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -6,11 +6,22 @@ var core = require('@capacitor/core');
|
|
|
6
6
|
|
|
7
7
|
var _a, _b;
|
|
8
8
|
const NFCPlug = core.registerPlugin('NFC', {
|
|
9
|
+
// Explicit .js extension required under node16/nodenext module resolution for emitted ES modules.
|
|
9
10
|
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.NFCWeb()),
|
|
10
11
|
});
|
|
11
12
|
const NFC = {
|
|
12
13
|
isSupported: NFCPlug.isSupported.bind(NFCPlug),
|
|
13
|
-
startScan:
|
|
14
|
+
startScan: (options) => {
|
|
15
|
+
const normalizedOptions = {};
|
|
16
|
+
if (options) {
|
|
17
|
+
for (const [key, value] of Object.entries(options)) {
|
|
18
|
+
if (value !== undefined && value !== null) {
|
|
19
|
+
normalizedOptions[key] = value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return NFCPlug.startScan(normalizedOptions);
|
|
24
|
+
},
|
|
14
25
|
cancelScan: (_b = (_a = NFCPlug.cancelScan) === null || _a === void 0 ? void 0 : _a.bind(NFCPlug)) !== null && _b !== void 0 ? _b : (async () => {
|
|
15
26
|
/* Android no-op */
|
|
16
27
|
}),
|
|
@@ -69,20 +80,27 @@ const NFC = {
|
|
|
69
80
|
const recordsArray = (_a = options === null || options === void 0 ? void 0 : options.records) !== null && _a !== void 0 ? _a : [];
|
|
70
81
|
if (recordsArray.length === 0)
|
|
71
82
|
throw new Error('At least one NDEF record is required');
|
|
83
|
+
const isRawMode = (options === null || options === void 0 ? void 0 : options.rawMode) === true;
|
|
72
84
|
const ndefMessage = {
|
|
73
85
|
records: recordsArray.map((record) => {
|
|
74
86
|
let payload = null;
|
|
75
87
|
if (typeof record.payload === 'string') {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
payload =
|
|
79
|
-
}
|
|
80
|
-
else if (record.type === 'U') {
|
|
81
|
-
payload = buildUriPayload(record.payload);
|
|
88
|
+
if (isRawMode) {
|
|
89
|
+
// Raw mode: write string payloads as UTF-8 bytes without any framing
|
|
90
|
+
payload = Array.from(new TextEncoder().encode(record.payload));
|
|
82
91
|
}
|
|
83
92
|
else {
|
|
84
|
-
//
|
|
85
|
-
|
|
93
|
+
// Apply spec-compliant formatting only for Well Known Text (T) & URI (U) types.
|
|
94
|
+
if (record.type === 'T') {
|
|
95
|
+
payload = buildTextPayload(record.payload);
|
|
96
|
+
}
|
|
97
|
+
else if (record.type === 'U') {
|
|
98
|
+
payload = buildUriPayload(record.payload);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Generic string: raw UTF-8 bytes (no extra framing)
|
|
102
|
+
payload = Array.from(new TextEncoder().encode(record.payload));
|
|
103
|
+
}
|
|
86
104
|
}
|
|
87
105
|
}
|
|
88
106
|
else if (Array.isArray(record.payload)) {
|
|
@@ -253,7 +271,7 @@ class NFCWeb extends core.WebPlugin {
|
|
|
253
271
|
async isSupported() {
|
|
254
272
|
return { supported: false };
|
|
255
273
|
}
|
|
256
|
-
async startScan() {
|
|
274
|
+
async startScan(_options) {
|
|
257
275
|
throw new Error('NFC is not supported on web');
|
|
258
276
|
}
|
|
259
277
|
async cancelScan() {
|
|
@@ -262,7 +280,7 @@ class NFCWeb extends core.WebPlugin {
|
|
|
262
280
|
async cancelWriteAndroid() {
|
|
263
281
|
throw new Error('NFC is not supported on web');
|
|
264
282
|
}
|
|
265
|
-
async writeNDEF() {
|
|
283
|
+
async writeNDEF(_options) {
|
|
266
284
|
throw new Error('NFC is not supported on web');
|
|
267
285
|
}
|
|
268
286
|
}
|