@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 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
- if (asString.tagInfo) {
120
- console.log('Tag UID:', asString.tagInfo.uid);
121
- console.log('Tag technologies:', asString.tagInfo.techTypes);
122
- console.log('Tag type:', asString.tagInfo.type);
123
- if (asString.tagInfo.maxSize) {
124
- console.log('Max NDEF size:', asString.tagInfo.maxSize);
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:', asString.tagInfo.isWritable);
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: string;
427
+ uid?: string;
373
428
 
374
429
  /**
375
430
  * The NFC tag technology types supported
376
431
  */
377
- techTypes: string[];
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
- ndefRecords.add(
196
- NdefRecord(
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
- startScan(): Promise<void>;
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: string;
79
+ uid?: string;
60
80
  /**
61
81
  * The NFC tag technology types supported
62
82
  */
63
- techTypes: string[];
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: 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\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\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"]}
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"]}
@@ -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
- web: () => import('./web').then((m) => new m.NFCWeb()),
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: NFCPlug.startScan.bind(NFCPlug),
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
- // Apply spec-compliant formatting only for Well Known Text (T) & URI (U) types.
73
- if (record.type === 'T') {
74
- payload = buildTextPayload(record.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
- // Generic string: raw UTF-8 bytes (no extra framing)
81
- payload = Array.from(new TextEncoder().encode(record.payload));
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)) {
@@ -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":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAcjD,MAAM,OAAO,GAAG,cAAc,CAAiB,KAAK,EAAE;IACpD,kGAAkG;IAClG,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;CAC1D,CAAC,CAAC;AACH,cAAc,kBAAkB,CAAC;AACjC,MAAM,CAAC,MAAM,GAAG,GAAc;IAC5B,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,OAA0B,EAAE,EAAE;QACxC,MAAM,iBAAiB,GAA4B,EAAE,CAAC;QACtD,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBAC1C,iBAAiB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACjC,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC9C,CAAC;IACD,UAAU,EACR,MAAA,MAAA,OAAO,CAAC,UAAU,0CAAE,IAAI,CAAC,OAAO,CAAC,mCACjC,CAAC,KAAK,IAAI,EAAE;QACV,mBAAmB;IACrB,CAAC,CAAC;IACJ,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;IAC5D,MAAM,EAAE,CAAC,IAA2B,EAAE,EAAE;QACtC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,8BAA8B;QAC9B,OAAO,GAAG,EAAE;YACV,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAwB,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QAC/F,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC,IAAgB,EAAE,EAAE;QAC5B,IAAI,MAAW,CAAC;QAChB,OAAO,CAAC,WAAW,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC5E,OAAO,GAAG,EAAE;;YACV,IAAI,CAAC;gBACH,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,sDAAI,CAAC;YACrB,CAAC;YAAC,WAAM,CAAC;gBACP,WAAW;YACb,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC,OAAkC,EAAE,EAAE;QAC9C,IAAI,MAAW,CAAC;QAChB,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO,GAAG,EAAE;;YACV,IAAI,CAAC;gBACH,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,sDAAI,CAAC;YACrB,CAAC;YAAC,WAAM,CAAC;gBACP,WAAW;YACb,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IACD,kBAAkB,EAAE,CAAC,SAAgC,EAAE,EAAE;QACvD,GAAG,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC;IACD,gBAAgB,EAAE,EAAE;IAEpB,KAAK,CAAC,SAAS,CAAqC,OAA6B;;QAC/E,gFAAgF;QAChF,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAE,IAAI,GAAG,IAAI,EAAY,EAAE;YAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,0CAA0C;YAClF,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,SAAS,CAAC,CAAC;QAC9C,CAAC,CAAC;QACF,MAAM,eAAe,GAAG,CAAC,GAAW,EAAE,UAAU,GAAG,IAAI,EAAY,EAAE;YACnE,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3D,OAAO,CAAC,UAAU,EAAE,GAAG,QAAQ,CAAC,CAAC;QACnC,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,mCAAI,EAAE,CAAC;QAC5C,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAEvF,MAAM,SAAS,GAAG,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,MAAK,IAAI,CAAC;QAE5C,MAAM,WAAW,GAA+B;YAC9C,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,MAAW,EAAE,EAAE;gBACxC,IAAI,OAAO,GAAoB,IAAI,CAAC;gBAEpC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACvC,IAAI,SAAS,EAAE,CAAC;wBACd,qEAAqE;wBACrE,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;oBACjE,CAAC;yBAAM,CAAC;wBACN,gFAAgF;wBAChF,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;4BACxB,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBAC7C,CAAC;6BAAM,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;4BAC/B,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBAC5C,CAAC;6BAAM,CAAC;4BACN,qDAAqD;4BACrD,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;wBACjE,CAAC;oBACH,CAAC;gBACH,CAAC;qBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;oBACzC,0CAA0C;oBAC1C,OAAO,GAAG,MAAM,CAAC,OAAmB,CAAC;gBACvC,CAAC;qBAAM,IAAI,MAAM,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC;oBAChD,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACvC,CAAC;gBAED,IAAI,CAAC,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBAE1D,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;YACxC,CAAC,CAAC;SACH,CAAC;QAEF,MAAM,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACvC,CAAC;CACF,CAAC;AAQF,4FAA4F;AAC5F,MAAM,mBAAmB,GAAG,CAAC,aAAqB,EAAc,EAAE;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,qEAAqE;AACrE,MAAM,gBAAgB,GAAG,CAAC,KAAiB,EAAU,EAAE;IACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,2BAA2B;IAClE,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC,gCAAgC;IAClE,IAAI,CAAC,GAAG,UAAU,GAAG,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC,CAAC,UAAU;IACxD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IAAC,WAAM,CAAC;QACP,wBAAwB;QACxB,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;aACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;aAClC,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,UAAU,GAAa;IAC3B,EAAE;IACF,aAAa;IACb,cAAc;IACd,SAAS;IACT,UAAU;IACV,MAAM;IACN,SAAS;IACT,4BAA4B;IAC5B,YAAY;IACZ,SAAS;IACT,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,WAAW;IACX,OAAO;IACP,SAAS;IACT,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,UAAU;IACV,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,aAAa;IACb,SAAS;IACT,aAAa;IACb,cAAc;IACd,cAAc;IACd,cAAc;IACd,UAAU;IACV,UAAU;CACX,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,KAAiB,EAAU,EAAE;IACpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7D,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,CACL,MAAM;YACN,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;iBAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;iBAClC,IAAI,CAAC,EAAE,CAAC,CACZ,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,UAAkB,EAAE,KAAiB,EAAU,EAAE;IACxE,kBAAkB;IAClB,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACvD,iBAAiB;IACjB,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IACtD,gCAAgC;IAChC,IAAI,CAAC;QACH,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;aACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;aAClC,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAA4B,IAAO,EAAE,IAAkB,EAAkB,EAAE;IAC9F,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAY,EAAE,EAAE,CAAC,CAAC;YAC7C,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAW,EAAE,EAAE;gBAC3C,MAAM,KAAK,GAAG,mBAAmB,CAAC,MAAM,CAAC,OAA4B,CAAC,CAAC;gBACvE,IAAI,OAAY,CAAC;gBACjB,QAAQ,IAAI,EAAE,CAAC;oBACb,KAAK,KAAK;wBACR,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,yBAAyB;wBACnD,MAAM;oBACR,KAAK,YAAY;wBACf,OAAO,GAAG,KAAK,CAAC;wBAChB,MAAM;oBACR,KAAK,aAAa;wBAChB,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBAC5B,MAAM;oBACR,KAAK,QAAQ;wBACX,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;wBAC9C,MAAM;oBACR;wBACE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;gBAC7B,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;YACxC,CAAC,CAAC;SACH,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,0BAA0B;KAChC,CAAC;AACtB,CAAC,CAAC;AAEF,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,IAAS,EAAE,EAAE;IAC1C,MAAM,WAAW,GAA8B;QAC7C,MAAM;YACJ,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,MAAM;YACJ,OAAO,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,UAAU;YACR,OAAO,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,WAAW;YACT,OAAO,YAAY,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC;KACF,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAC5C,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxB,CAAC;AACH,CAAC,CAAC,CAAC","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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,OAAO,MAAO,SAAQ,SAAS;IACnC,KAAK,CAAC,WAAW;QACf,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,SAAS;QACb,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;QACb,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nexport class NFCWeb extends WebPlugin {\n async isSupported(): Promise<{ supported: boolean }> {\n return { supported: false };\n }\n\n async startScan(): 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(): Promise<void> {\n throw new Error('NFC is not supported on web');\n }\n}\n"]}
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"]}
@@ -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: NFCPlug.startScan.bind(NFCPlug),
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
- // Apply spec-compliant formatting only for Well Known Text (T) & URI (U) types.
77
- if (record.type === 'T') {
78
- payload = buildTextPayload(record.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
- // Generic string: raw UTF-8 bytes (no extra framing)
85
- payload = Array.from(new TextEncoder().encode(record.payload));
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
  }