@cap-kit/people 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapKitPeople.podspec +20 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +1177 -0
- package/android/build.gradle +101 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/io/capkit/people/PeopleImpl.kt +1003 -0
- package/android/src/main/java/io/capkit/people/PeopleObserver.kt +80 -0
- package/android/src/main/java/io/capkit/people/PeoplePlugin.kt +766 -0
- package/android/src/main/java/io/capkit/people/config/PeopleConfig.kt +44 -0
- package/android/src/main/java/io/capkit/people/error/PeopleError.kt +90 -0
- package/android/src/main/java/io/capkit/people/error/PeopleErrorMessages.kt +39 -0
- package/android/src/main/java/io/capkit/people/logger/PeopleLogger.kt +85 -0
- package/android/src/main/java/io/capkit/people/models/ContactModels.kt +64 -0
- package/android/src/main/java/io/capkit/people/utils/PeopleUtils.kt +133 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +1449 -0
- package/dist/esm/definitions.d.ts +775 -0
- package/dist/esm/definitions.js +31 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +15 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +120 -0
- package/dist/esm/web.js +252 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs +300 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.js +303 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/PeoplePlugin/PeopleImpl.swift +463 -0
- package/ios/Sources/PeoplePlugin/PeoplePlugin.swift +627 -0
- package/ios/Sources/PeoplePlugin/PrivacyInfo.xcprivacy +13 -0
- package/ios/Sources/PeoplePlugin/Utils/PeopleUtils.swift +120 -0
- package/ios/Sources/PeoplePlugin/Version.swift +16 -0
- package/ios/Sources/PeoplePlugin/config/PeopleConfig.swift +56 -0
- package/ios/Sources/PeoplePlugin/error/PeopleError.swift +89 -0
- package/ios/Sources/PeoplePlugin/error/PeopleErrorMessages.swift +25 -0
- package/ios/Sources/PeoplePlugin/logger/PeopleLogging.swift +69 -0
- package/ios/Sources/PeoplePlugin/models/ContactModels.swift +68 -0
- package/ios/Tests/PeoplePluginTests/PeoplePluginTests.swift +10 -0
- package/package.json +119 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Contacts
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A utility struct providing helper methods for data mapping.
|
|
6
|
+
* Architectural rules:
|
|
7
|
+
* - Pure helper functions only.
|
|
8
|
+
* - No Capacitor dependencies.
|
|
9
|
+
*/
|
|
10
|
+
struct PeopleUtils {
|
|
11
|
+
|
|
12
|
+
/// Supported projection fields constant.
|
|
13
|
+
static let supportedProjections: Set<String> = [
|
|
14
|
+
"name", "organization", "birthday", "phones", "emails", "addresses", "urls", "image", "note"
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
/// Normalizes native iOS contact labels into standardized strings.
|
|
18
|
+
static func normalizeLabel(_ label: String?) -> String {
|
|
19
|
+
guard let label = label else { return "other" }
|
|
20
|
+
|
|
21
|
+
// Use the localized string from the framework to clean internal identifiers like _$!<Mobile>!$_
|
|
22
|
+
let localizedLabel = CNLabeledValue<NSString>.localizedString(forLabel: label).lowercased()
|
|
23
|
+
|
|
24
|
+
if localizedLabel.contains("mobile") || localizedLabel.contains("cell") { return "mobile" }
|
|
25
|
+
if localizedLabel.contains("home") { return "home" }
|
|
26
|
+
if localizedLabel.contains("work") { return "work" }
|
|
27
|
+
if localizedLabel.contains("main") { return "main" }
|
|
28
|
+
if localizedLabel.contains("fax") { return "fax" }
|
|
29
|
+
return "other"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Validates that all requested projection fields are supported.
|
|
33
|
+
/// - Parameter projection: The array of strings to validate.
|
|
34
|
+
/// - Throws: PeopleError.unknownType if a field is invalid.
|
|
35
|
+
static func validateProjection(_ projection: [String]) throws {
|
|
36
|
+
for field in projection {
|
|
37
|
+
if !supportedProjections.contains(field) {
|
|
38
|
+
throw PeopleError.unknownType("Unsupported projection field: \(field)")
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Maps a native CNContact to the nominal ContactData model.
|
|
44
|
+
static func mapToContactData(_ contact: CNContact, projection: [String]) -> ContactData {
|
|
45
|
+
let nameAvailable =
|
|
46
|
+
contact.isKeyAvailable(CNContactGivenNameKey) &&
|
|
47
|
+
contact.isKeyAvailable(CNContactFamilyNameKey)
|
|
48
|
+
let organizationAvailable =
|
|
49
|
+
contact.isKeyAvailable(CNContactOrganizationNameKey) &&
|
|
50
|
+
contact.isKeyAvailable(CNContactJobTitleKey) &&
|
|
51
|
+
contact.isKeyAvailable(CNContactDepartmentNameKey)
|
|
52
|
+
let birthdayAvailable = contact.isKeyAvailable(CNContactBirthdayKey)
|
|
53
|
+
let phonesAvailable = contact.isKeyAvailable(CNContactPhoneNumbersKey)
|
|
54
|
+
let emailsAvailable = contact.isKeyAvailable(CNContactEmailAddressesKey)
|
|
55
|
+
let addressesAvailable = contact.isKeyAvailable(CNContactPostalAddressesKey)
|
|
56
|
+
let urlsAvailable = contact.isKeyAvailable(CNContactUrlAddressesKey)
|
|
57
|
+
let noteAvailable = contact.isKeyAvailable(CNContactNoteKey)
|
|
58
|
+
let imageAvailable = contact.isKeyAvailable(CNContactThumbnailImageDataKey)
|
|
59
|
+
|
|
60
|
+
let displayName: String? = {
|
|
61
|
+
guard nameAvailable else { return nil }
|
|
62
|
+
let parts = [contact.givenName, contact.familyName].filter { !$0.isEmpty }
|
|
63
|
+
return parts.isEmpty ? nil : parts.joined(separator: " ")
|
|
64
|
+
}()
|
|
65
|
+
|
|
66
|
+
let organization: ContactOrganization? = {
|
|
67
|
+
guard projection.contains("organization"), organizationAvailable else { return nil }
|
|
68
|
+
return ContactOrganization(
|
|
69
|
+
company: contact.organizationName.isEmpty ? nil : contact.organizationName,
|
|
70
|
+
title: contact.jobTitle.isEmpty ? nil : contact.jobTitle,
|
|
71
|
+
department: contact.departmentName.isEmpty ? nil : contact.departmentName
|
|
72
|
+
)
|
|
73
|
+
}()
|
|
74
|
+
|
|
75
|
+
return ContactData(
|
|
76
|
+
id: contact.identifier,
|
|
77
|
+
displayName: displayName,
|
|
78
|
+
firstName: projection.contains("name") && nameAvailable ? contact.givenName : nil,
|
|
79
|
+
lastName: projection.contains("name") && nameAvailable ? contact.familyName : nil,
|
|
80
|
+
organization: organization,
|
|
81
|
+
birthday: projection.contains("birthday") && birthdayAvailable ? contact.birthday : nil,
|
|
82
|
+
phones: projection.contains("phones") && phonesAvailable ? contact.phoneNumbers.map {
|
|
83
|
+
ContactPhone(
|
|
84
|
+
label: normalizeLabel($0.label),
|
|
85
|
+
number: $0.value.stringValue
|
|
86
|
+
)
|
|
87
|
+
} : nil,
|
|
88
|
+
emails: projection.contains("emails") && emailsAvailable ? contact.emailAddresses.map {
|
|
89
|
+
ContactEmail(
|
|
90
|
+
label: normalizeLabel($0.label),
|
|
91
|
+
address: $0.value as String
|
|
92
|
+
)
|
|
93
|
+
} : nil,
|
|
94
|
+
addresses: projection.contains("addresses") && addressesAvailable ? contact.postalAddresses.map {
|
|
95
|
+
ContactAddress(
|
|
96
|
+
label: normalizeLabel($0.label),
|
|
97
|
+
formatted: CNPostalAddressFormatter.string(from: $0.value, style: .mailingAddress),
|
|
98
|
+
street: $0.value.street.isEmpty ? nil : $0.value.street,
|
|
99
|
+
city: $0.value.city.isEmpty ? nil : $0.value.city,
|
|
100
|
+
region: $0.value.state.isEmpty ? nil : $0.value.state,
|
|
101
|
+
postcode: $0.value.postalCode.isEmpty ? nil : $0.value.postalCode,
|
|
102
|
+
country: $0.value.country.isEmpty ? nil : $0.value.country
|
|
103
|
+
)
|
|
104
|
+
} : nil,
|
|
105
|
+
urls: projection.contains("urls") && urlsAvailable ? contact.urlAddresses.map { $0.value as String } : nil,
|
|
106
|
+
note: projection.contains("note") && noteAvailable ? contact.note : nil,
|
|
107
|
+
image: (projection.contains("image") && imageAvailable && contact.thumbnailImageData != nil) ? contact.thumbnailImageData?.base64EncodedString() : nil
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Maps a native CNGroup to the nominal GroupData model.
|
|
112
|
+
static func mapToGroupData(_ group: CNGroup) -> GroupData {
|
|
113
|
+
return GroupData(
|
|
114
|
+
id: group.identifier,
|
|
115
|
+
name: group.name,
|
|
116
|
+
source: "local",
|
|
117
|
+
readOnly: false
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// This file is automatically generated. Do not modify manually.
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
Container for the plugin's version information.
|
|
6
|
+
This enum provides a centralized, single source of truth for the native
|
|
7
|
+
version string, synchronized directly from the project's 'package.json'.
|
|
8
|
+
It ensures parity across JavaScript, Android, and iOS platforms.
|
|
9
|
+
*/
|
|
10
|
+
public enum PluginVersion {
|
|
11
|
+
/**
|
|
12
|
+
The semantic version string of the plugin.
|
|
13
|
+
Value synchronized from package.json: "8.0.0"
|
|
14
|
+
*/
|
|
15
|
+
public static let number = "8.0.0"
|
|
16
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Plugin configuration container for the People plugin.
|
|
6
|
+
*
|
|
7
|
+
* This struct is responsible for reading and exposing static configuration
|
|
8
|
+
* values defined under the `People` key in `capacitor.config.ts`.
|
|
9
|
+
*
|
|
10
|
+
* Architectural rules:
|
|
11
|
+
* - Read once during plugin initialization (`load()`).
|
|
12
|
+
* - Treated as immutable runtime input.
|
|
13
|
+
* - Consumed only by native code (never by JavaScript).
|
|
14
|
+
*/
|
|
15
|
+
public struct PeopleConfig {
|
|
16
|
+
|
|
17
|
+
// MARK: - Configuration Keys
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Internal structure to maintain consistent configuration keys.
|
|
21
|
+
*/
|
|
22
|
+
private struct Keys {
|
|
23
|
+
static let verboseLogging = "verboseLogging"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// MARK: - Public Properties
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Enables verbose native logging via PeopleLogger.
|
|
30
|
+
*
|
|
31
|
+
* When enabled, additional debug information is printed to the Xcode console.
|
|
32
|
+
* Default: false
|
|
33
|
+
*/
|
|
34
|
+
public let verboseLogging: Bool
|
|
35
|
+
|
|
36
|
+
// MARK: - Private Defaults
|
|
37
|
+
|
|
38
|
+
private static let defaultVerboseLogging: Bool = false
|
|
39
|
+
|
|
40
|
+
// MARK: - Initialization
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Initializes the configuration by reading values from the Capacitor bridge.
|
|
44
|
+
*
|
|
45
|
+
* - Parameter plugin: The CAPPlugin instance used to access typed configuration via `getConfig()`.
|
|
46
|
+
*/
|
|
47
|
+
init(plugin: CAPPlugin) {
|
|
48
|
+
let config = plugin.getConfig()
|
|
49
|
+
|
|
50
|
+
// Extract verboseLogging with fallback to default
|
|
51
|
+
self.verboseLogging = config.getBoolean(
|
|
52
|
+
Keys.verboseLogging,
|
|
53
|
+
Self.defaultVerboseLogging
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Native error model for the People plugin (iOS).
|
|
5
|
+
|
|
6
|
+
This enum represents all error categories that can be
|
|
7
|
+
produced by the native implementation layer.
|
|
8
|
+
|
|
9
|
+
Architectural rules:
|
|
10
|
+
- Must NOT reference Capacitor
|
|
11
|
+
- Must NOT reference JavaScript
|
|
12
|
+
- Must be throwable from the Impl layer
|
|
13
|
+
- Mapping to JS-facing error codes happens ONLY in the Plugin layer
|
|
14
|
+
*/
|
|
15
|
+
enum PeopleError: Error {
|
|
16
|
+
|
|
17
|
+
/// Feature or capability is not available on this device or configuration
|
|
18
|
+
case unavailable(String)
|
|
19
|
+
|
|
20
|
+
/// The user cancelled an interactive flow (e.g., contact picker)
|
|
21
|
+
case cancelled(String)
|
|
22
|
+
|
|
23
|
+
/// Required permission was denied or not granted
|
|
24
|
+
case permissionDenied(String)
|
|
25
|
+
|
|
26
|
+
/// Plugin failed to initialize or perform a required operation
|
|
27
|
+
case initFailed(String)
|
|
28
|
+
|
|
29
|
+
/// The input provided to the plugin method is invalid or malformed
|
|
30
|
+
case invalidInput(String)
|
|
31
|
+
|
|
32
|
+
/// Invalid or unsupported input was provided
|
|
33
|
+
case unknownType(String)
|
|
34
|
+
|
|
35
|
+
/// The requested resource does not exist (e.g., contact or group not found)
|
|
36
|
+
case notFound(String)
|
|
37
|
+
|
|
38
|
+
/// The operation conflicts with the current state (e.g., read-only group)
|
|
39
|
+
case conflict(String)
|
|
40
|
+
|
|
41
|
+
/// The operation did not complete within the expected time
|
|
42
|
+
case timeout(String)
|
|
43
|
+
|
|
44
|
+
// MARK: - Human-readable message
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
Human-readable error message.
|
|
48
|
+
|
|
49
|
+
This message is intended to be passed verbatim
|
|
50
|
+
to JavaScript via `call.reject(message, code)`.
|
|
51
|
+
*/
|
|
52
|
+
var message: String {
|
|
53
|
+
switch self {
|
|
54
|
+
case .unavailable(let message):
|
|
55
|
+
return message
|
|
56
|
+
case .cancelled(let message):
|
|
57
|
+
return message
|
|
58
|
+
case .permissionDenied(let message):
|
|
59
|
+
return message
|
|
60
|
+
case .initFailed(let message):
|
|
61
|
+
return message
|
|
62
|
+
case .invalidInput(let message):
|
|
63
|
+
return message
|
|
64
|
+
case .unknownType(let message):
|
|
65
|
+
return message
|
|
66
|
+
case .notFound(let message):
|
|
67
|
+
return message
|
|
68
|
+
case .conflict(let message):
|
|
69
|
+
return message
|
|
70
|
+
case .timeout(let message):
|
|
71
|
+
return message
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Standardized error code string for JS rejection.
|
|
76
|
+
var errorCode: String {
|
|
77
|
+
switch self {
|
|
78
|
+
case .unavailable: return "UNAVAILABLE"
|
|
79
|
+
case .cancelled: return "CANCELLED"
|
|
80
|
+
case .permissionDenied: return "PERMISSION_DENIED"
|
|
81
|
+
case .initFailed: return "INIT_FAILED"
|
|
82
|
+
case .invalidInput: return "INVALID_INPUT"
|
|
83
|
+
case .unknownType: return "UNKNOWN_TYPE"
|
|
84
|
+
case .notFound: return "NOT_FOUND"
|
|
85
|
+
case .conflict: return "CONFLICT"
|
|
86
|
+
case .timeout: return "TIMEOUT"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Canonical error messages shared across platforms.
|
|
4
|
+
/// Keep these strings identical on iOS and Android.
|
|
5
|
+
enum PeopleErrorMessages {
|
|
6
|
+
static let idRequired = "id is required"
|
|
7
|
+
static let queryRequired = "query is required"
|
|
8
|
+
static let eventNameRequired = "eventName is required"
|
|
9
|
+
|
|
10
|
+
static func unsupportedEventName(_ value: String) -> String {
|
|
11
|
+
"Unsupported eventName: \(value)"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static let missingContactsUsageDescription = "Missing NSContactsUsageDescription in Info.plist"
|
|
15
|
+
static let noContactUriReturned = "No contact URI returned"
|
|
16
|
+
static let userCancelledSelection = "User cancelled selection"
|
|
17
|
+
static let failedToParseContactFromUri = "Failed to parse contact from URI"
|
|
18
|
+
|
|
19
|
+
static func unsupportedProjectionField(_ value: String) -> String {
|
|
20
|
+
"Unsupported projection field: \(value)"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static let contactNotFound = "Contact not found"
|
|
24
|
+
static let atLeastOneWritableFieldRequired = "At least one writable contact field is required"
|
|
25
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Capacitor
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Centralized native logger for the People plugin.
|
|
5
|
+
|
|
6
|
+
Responsibilities:
|
|
7
|
+
- Provide a single logging entry point
|
|
8
|
+
- Support runtime-controlled verbose logging
|
|
9
|
+
- Keep logging behavior consistent across files
|
|
10
|
+
|
|
11
|
+
Forbidden:
|
|
12
|
+
- Controlling application logic
|
|
13
|
+
- Being queried for flow decisions
|
|
14
|
+
*/
|
|
15
|
+
enum PeopleLogger {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
Controls whether debug logs are printed.
|
|
19
|
+
|
|
20
|
+
This value MUST be set once during plugin initialization
|
|
21
|
+
based on static configuration.
|
|
22
|
+
*/
|
|
23
|
+
static var verbose: Bool = false
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
Prints a verbose / debug log message.
|
|
27
|
+
|
|
28
|
+
Debug logs are automatically silenced
|
|
29
|
+
when `verbose` is false.
|
|
30
|
+
*/
|
|
31
|
+
static func debug(_ items: Any...) {
|
|
32
|
+
guard verbose else { return }
|
|
33
|
+
log(items)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
Prints an error log message.
|
|
38
|
+
|
|
39
|
+
Error logs are always printed regardless
|
|
40
|
+
of the verbose flag.
|
|
41
|
+
*/
|
|
42
|
+
static func error(_ items: Any...) {
|
|
43
|
+
log(items)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// MARK: - Internal log printer
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
Low-level log printer with a consistent prefix.
|
|
51
|
+
|
|
52
|
+
This function MUST NOT be used outside this file.
|
|
53
|
+
*/
|
|
54
|
+
private func log(
|
|
55
|
+
_ items: [Any],
|
|
56
|
+
separator: String = " ",
|
|
57
|
+
terminator: String = "\n"
|
|
58
|
+
) {
|
|
59
|
+
CAPLog.print("⚡️ People -", terminator: separator)
|
|
60
|
+
|
|
61
|
+
for (index, item) in items.enumerated() {
|
|
62
|
+
CAPLog.print(
|
|
63
|
+
item,
|
|
64
|
+
terminator: index == items.count - 1
|
|
65
|
+
? terminator
|
|
66
|
+
: separator
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Contacts
|
|
3
|
+
|
|
4
|
+
/// Nominal model for organization data to ensure cross-platform parity.
|
|
5
|
+
public struct ContactOrganization {
|
|
6
|
+
public let company: String?
|
|
7
|
+
public let title: String?
|
|
8
|
+
public let department: String?
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/// Nominal model for a phone field.
|
|
12
|
+
public struct ContactPhone {
|
|
13
|
+
public let label: String?
|
|
14
|
+
public let number: String
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Nominal model for an email field.
|
|
18
|
+
public struct ContactEmail {
|
|
19
|
+
public let label: String?
|
|
20
|
+
public let address: String
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Nominal model for a postal address field.
|
|
24
|
+
public struct ContactAddress {
|
|
25
|
+
public let label: String?
|
|
26
|
+
public let formatted: String?
|
|
27
|
+
public let street: String?
|
|
28
|
+
public let city: String?
|
|
29
|
+
public let region: String?
|
|
30
|
+
public let postcode: String?
|
|
31
|
+
public let country: String?
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Nominal model representing contact data, independent of Capacitor.
|
|
35
|
+
/// This ensures type safety within the Implementation layer.
|
|
36
|
+
public struct ContactData {
|
|
37
|
+
public let id: String
|
|
38
|
+
public let displayName: String?
|
|
39
|
+
public let firstName: String?
|
|
40
|
+
public let lastName: String?
|
|
41
|
+
public let organization: ContactOrganization? // Updated from String? to struct
|
|
42
|
+
public let birthday: DateComponents?
|
|
43
|
+
public let phones: [ContactPhone]?
|
|
44
|
+
public let emails: [ContactEmail]?
|
|
45
|
+
public let addresses: [ContactAddress]?
|
|
46
|
+
public let urls: [String]?
|
|
47
|
+
public let note: String?
|
|
48
|
+
public let image: String? // Base64 string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Nominal model for Group data to replace raw dictionaries.
|
|
52
|
+
public struct GroupData {
|
|
53
|
+
public let id: String
|
|
54
|
+
public let name: String
|
|
55
|
+
public let source: String
|
|
56
|
+
public let readOnly: Bool
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Result model for paginated contact fetches.
|
|
60
|
+
public struct GetContactsResultData {
|
|
61
|
+
public let contacts: [ContactData]
|
|
62
|
+
public let totalCount: Int
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Result model for group listings.
|
|
66
|
+
public struct ListGroupsResultData {
|
|
67
|
+
public let groups: [GroupData]
|
|
68
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import People
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
Basic functional tests for the People plugin native implementation.
|
|
6
|
+
|
|
7
|
+
These tests validate the core behavior of the implementation
|
|
8
|
+
independently from the Capacitor bridge.
|
|
9
|
+
*/
|
|
10
|
+
class PeoplePluginTests: XCTestCase {}
|
package/package.json
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cap-kit/people",
|
|
3
|
+
"version": "8.0.0",
|
|
4
|
+
"description": "Unified, high-performance contact management for Capacitor with zero-permission picking and capability-based access.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=24.0.0",
|
|
10
|
+
"pnpm": ">=10.0.0"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/plugin.cjs",
|
|
13
|
+
"module": "dist/esm/index.js",
|
|
14
|
+
"types": "dist/esm/index.d.ts",
|
|
15
|
+
"unpkg": "dist/plugin.js",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/esm/index.d.ts",
|
|
19
|
+
"import": "./dist/esm/index.js",
|
|
20
|
+
"require": "./dist/plugin.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"android/src/main/",
|
|
26
|
+
"android/build.gradle",
|
|
27
|
+
"dist/",
|
|
28
|
+
"ios/Sources",
|
|
29
|
+
"ios/Tests",
|
|
30
|
+
"Package.swift",
|
|
31
|
+
"CapKitPeople.podspec",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"author": "CapKit Team",
|
|
36
|
+
"funding": {
|
|
37
|
+
"type": "github",
|
|
38
|
+
"url": "https://github.com/sponsors/cap-kit"
|
|
39
|
+
},
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/cap-kit/capacitor-plugins.git",
|
|
44
|
+
"directory": "packages/people"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/cap-kit/capacitor-plugins/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/cap-kit/capacitor-plugins/tree/main/packages/people#readme",
|
|
50
|
+
"keywords": [
|
|
51
|
+
"capacitor",
|
|
52
|
+
"capacitor-plugin",
|
|
53
|
+
"cap-kit",
|
|
54
|
+
"mobile",
|
|
55
|
+
"native",
|
|
56
|
+
"ios",
|
|
57
|
+
"android",
|
|
58
|
+
"contact",
|
|
59
|
+
"address-book",
|
|
60
|
+
"directory",
|
|
61
|
+
"contact-picker",
|
|
62
|
+
"privacy",
|
|
63
|
+
"performance"
|
|
64
|
+
],
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@capacitor/android": "^8.1.0",
|
|
70
|
+
"@capacitor/cli": "^8.1.0",
|
|
71
|
+
"@capacitor/core": "^8.1.0",
|
|
72
|
+
"@capacitor/docgen": "^0.3.1",
|
|
73
|
+
"@capacitor/ios": "^8.1.0",
|
|
74
|
+
"@eslint/eslintrc": "^3.3.3",
|
|
75
|
+
"@eslint/js": "^9.39.2",
|
|
76
|
+
"eslint": "^9.39.2",
|
|
77
|
+
"eslint-plugin-import": "^2.32.0",
|
|
78
|
+
"globals": "^17.3.0",
|
|
79
|
+
"prettier": "^3.8.1",
|
|
80
|
+
"prettier-plugin-java": "^2.8.1",
|
|
81
|
+
"rimraf": "^6.1.3",
|
|
82
|
+
"rollup": "^4.57.1",
|
|
83
|
+
"swiftlint": "^2.0.0",
|
|
84
|
+
"typescript": "^5.9.3",
|
|
85
|
+
"typescript-eslint": "^8.56.0"
|
|
86
|
+
},
|
|
87
|
+
"peerDependencies": {
|
|
88
|
+
"@capacitor/core": ">=8.1.0"
|
|
89
|
+
},
|
|
90
|
+
"capacitor": {
|
|
91
|
+
"ios": {
|
|
92
|
+
"src": "ios"
|
|
93
|
+
},
|
|
94
|
+
"android": {
|
|
95
|
+
"src": "android"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"scripts": {
|
|
99
|
+
"verify": "pnpm run verify:ios && pnpm run verify:android && pnpm run verify:web",
|
|
100
|
+
"verify:ios": "node ./scripts/sync-version.mjs && xcodebuild -scheme CapKitPeople -destination generic/platform=iOS",
|
|
101
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
102
|
+
"verify:web": "pnpm run build",
|
|
103
|
+
"lint:android": "cd android && ./gradlew ktlintCheck",
|
|
104
|
+
"fmt:android": "cd android && ./gradlew ktlintFormat",
|
|
105
|
+
"lint": "pnpm run eslint . && pnpm run swiftlint lint --strict || true && pnpm run lint:android || true",
|
|
106
|
+
"format:check": "prettier --check \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
107
|
+
"format": "eslint --fix . && prettier --write \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java && pnpm run swiftlint --fix --format && pnpm run fmt:android",
|
|
108
|
+
"eslint": "eslint",
|
|
109
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
110
|
+
"swiftlint": "node-swiftlint lint ios/Sources",
|
|
111
|
+
"docgen": "docgen --api PeoplePlugin --output-readme README.md --output-json dist/docs.json",
|
|
112
|
+
"build": "node ./scripts/sync-version.mjs && pnpm run clean && pnpm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
113
|
+
"clean": "rimraf ./dist",
|
|
114
|
+
"watch": "tsc --watch",
|
|
115
|
+
"test": "pnpm run verify",
|
|
116
|
+
"removePacked": "rimraf -g cap-kit-people-*.tgz",
|
|
117
|
+
"publish:locally": "pnpm run removePacked && pnpm run build && pnpm pack"
|
|
118
|
+
}
|
|
119
|
+
}
|