@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.
Files changed (42) hide show
  1. package/CapKitPeople.podspec +20 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +1177 -0
  5. package/android/build.gradle +101 -0
  6. package/android/src/main/AndroidManifest.xml +4 -0
  7. package/android/src/main/java/io/capkit/people/PeopleImpl.kt +1003 -0
  8. package/android/src/main/java/io/capkit/people/PeopleObserver.kt +80 -0
  9. package/android/src/main/java/io/capkit/people/PeoplePlugin.kt +766 -0
  10. package/android/src/main/java/io/capkit/people/config/PeopleConfig.kt +44 -0
  11. package/android/src/main/java/io/capkit/people/error/PeopleError.kt +90 -0
  12. package/android/src/main/java/io/capkit/people/error/PeopleErrorMessages.kt +39 -0
  13. package/android/src/main/java/io/capkit/people/logger/PeopleLogger.kt +85 -0
  14. package/android/src/main/java/io/capkit/people/models/ContactModels.kt +64 -0
  15. package/android/src/main/java/io/capkit/people/utils/PeopleUtils.kt +133 -0
  16. package/android/src/main/res/.gitkeep +0 -0
  17. package/dist/docs.json +1449 -0
  18. package/dist/esm/definitions.d.ts +775 -0
  19. package/dist/esm/definitions.js +31 -0
  20. package/dist/esm/definitions.js.map +1 -0
  21. package/dist/esm/index.d.ts +15 -0
  22. package/dist/esm/index.js +18 -0
  23. package/dist/esm/index.js.map +1 -0
  24. package/dist/esm/web.d.ts +120 -0
  25. package/dist/esm/web.js +252 -0
  26. package/dist/esm/web.js.map +1 -0
  27. package/dist/plugin.cjs +300 -0
  28. package/dist/plugin.cjs.map +1 -0
  29. package/dist/plugin.js +303 -0
  30. package/dist/plugin.js.map +1 -0
  31. package/ios/Sources/PeoplePlugin/PeopleImpl.swift +463 -0
  32. package/ios/Sources/PeoplePlugin/PeoplePlugin.swift +627 -0
  33. package/ios/Sources/PeoplePlugin/PrivacyInfo.xcprivacy +13 -0
  34. package/ios/Sources/PeoplePlugin/Utils/PeopleUtils.swift +120 -0
  35. package/ios/Sources/PeoplePlugin/Version.swift +16 -0
  36. package/ios/Sources/PeoplePlugin/config/PeopleConfig.swift +56 -0
  37. package/ios/Sources/PeoplePlugin/error/PeopleError.swift +89 -0
  38. package/ios/Sources/PeoplePlugin/error/PeopleErrorMessages.swift +25 -0
  39. package/ios/Sources/PeoplePlugin/logger/PeopleLogging.swift +69 -0
  40. package/ios/Sources/PeoplePlugin/models/ContactModels.swift +68 -0
  41. package/ios/Tests/PeoplePluginTests/PeoplePluginTests.swift +10 -0
  42. 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
+ }