@mmerterden/multi-agent-pipeline 20.36.0 → 20.37.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/CHANGELOG.md +12 -0
- package/docs/facts.json +1 -1
- package/install/catalog-history.json +1 -1
- package/manifest.json +194 -122
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/SKILL.md +33 -154
- package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
- package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
- package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
- package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
- package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
- package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
- package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
- package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
- package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
- package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
- package/pipeline/commands/multi-agent/sync/SKILL.md +35 -164
- package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
- package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
- package/pipeline/contract/fixtures/runs-empty.json +1 -1
- package/pipeline/contract/fixtures/runs-failed.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
- package/pipeline/contract/fixtures/runs-running.json +1 -1
- package/pipeline/contract/manifest.json +5 -5
- package/pipeline/contract/types/index.d.ts +3 -2
- package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
- package/pipeline/multi-agent-refs/channels/generator.md +14 -0
- package/pipeline/multi-agent-refs/channels/preview.md +13 -0
- package/pipeline/multi-agent-refs/channels/reference.md +46 -0
- package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
- package/pipeline/multi-agent-refs/design-check/export.md +1 -0
- package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
- package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
- package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
- package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
- package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
- package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
- package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
- package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
- package/pipeline/multi-agent-refs/resume/details.md +115 -2
- package/pipeline/multi-agent-refs/review/inputs.md +33 -1
- package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
- package/pipeline/multi-agent-refs/review/panel.md +75 -0
- package/pipeline/multi-agent-refs/review/valven.md +5 -1
- package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
- package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
- package/pipeline/multi-agent-refs/stack/background.md +72 -0
- package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
- package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
- package/pipeline/multi-agent-refs/sync/office.md +10 -0
- package/pipeline/multi-agent-refs/sync/overview.md +61 -0
- package/pipeline/multi-agent-refs/sync/repo.md +91 -0
- package/pipeline/multi-agent-refs/update/details.md +83 -0
- package/pipeline/schemas/runs-index.schema.json +2 -1
- package/pipeline/scripts/contract-server.mjs +1 -1
- package/pipeline/scripts/runs-index.mjs +34 -7
- package/pipeline/scripts/status-overview.mjs +401 -0
- package/pipeline/skills/.skill-manifest.json +63 -63
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +7 -70
- package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
- package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +10 -72
- package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +73 -217
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
- package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
- package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
- package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
- package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
- package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
- package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
- package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
- package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
- package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
- package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
- package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
- package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
- package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
- package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
- package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
- package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
- package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
- package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
- package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
- package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
- package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
- package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
- package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
- package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
- package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
- package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
- package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
- package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
- package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
- package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
- package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
- package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
- package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
- package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
- package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
- package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
- package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
- package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
- package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
- package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
- package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
- package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
- package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
- package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
- package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
- package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# TypeScript Code Examples
|
|
2
|
+
|
|
3
|
+
Full code samples for the SKILL.md sections that link here. The rules and the decision guidance stay in SKILL.md; each heading below matches the SKILL.md section the code belongs to.
|
|
4
|
+
|
|
5
|
+
## Discriminated Unions
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// API response pattern
|
|
9
|
+
type ApiResult<T> =
|
|
10
|
+
| { status: 'success'; data: T }
|
|
11
|
+
| { status: 'error'; error: { code: string; message: string } }
|
|
12
|
+
| { status: 'loading' };
|
|
13
|
+
|
|
14
|
+
function handleResult<T>(result: ApiResult<T>): string {
|
|
15
|
+
switch (result.status) {
|
|
16
|
+
case 'success':
|
|
17
|
+
return JSON.stringify(result.data);
|
|
18
|
+
case 'error':
|
|
19
|
+
return `Error ${result.error.code}: ${result.error.message}`;
|
|
20
|
+
case 'loading':
|
|
21
|
+
return 'Loading...';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Event system
|
|
26
|
+
type AppEvent =
|
|
27
|
+
| { type: 'USER_LOGIN'; payload: { userId: string } }
|
|
28
|
+
| { type: 'USER_LOGOUT' }
|
|
29
|
+
| { type: 'ITEM_ADDED'; payload: { itemId: string; quantity: number } }
|
|
30
|
+
| { type: 'ITEM_REMOVED'; payload: { itemId: string } };
|
|
31
|
+
|
|
32
|
+
function handleEvent(event: AppEvent): void {
|
|
33
|
+
switch (event.type) {
|
|
34
|
+
case 'USER_LOGIN':
|
|
35
|
+
console.log(`User ${event.payload.userId} logged in`);
|
|
36
|
+
break;
|
|
37
|
+
case 'USER_LOGOUT':
|
|
38
|
+
console.log('User logged out');
|
|
39
|
+
break;
|
|
40
|
+
case 'ITEM_ADDED':
|
|
41
|
+
console.log(`Added ${event.payload.quantity} of ${event.payload.itemId}`);
|
|
42
|
+
break;
|
|
43
|
+
case 'ITEM_REMOVED':
|
|
44
|
+
console.log(`Removed ${event.payload.itemId}`);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Exhaustive check helper
|
|
50
|
+
function exhaustiveCheck(value: never): never {
|
|
51
|
+
throw new Error(`Unhandled value: ${JSON.stringify(value)}`);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Zod for Runtime Validation
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { z } from 'zod';
|
|
59
|
+
|
|
60
|
+
// Define schema
|
|
61
|
+
const UserSchema = z.object({
|
|
62
|
+
id: z.string().uuid(),
|
|
63
|
+
name: z.string().min(1).max(100),
|
|
64
|
+
email: z.string().email(),
|
|
65
|
+
role: z.enum(['admin', 'user', 'viewer']),
|
|
66
|
+
age: z.number().int().min(0).max(150).optional(),
|
|
67
|
+
tags: z.array(z.string()).default([]),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Infer TypeScript type from schema
|
|
71
|
+
type User = z.infer<typeof UserSchema>;
|
|
72
|
+
|
|
73
|
+
// Validate at runtime
|
|
74
|
+
function createUser(input: unknown): User {
|
|
75
|
+
return UserSchema.parse(input); // throws ZodError on failure
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Safe parse (no throw)
|
|
79
|
+
function validateUser(input: unknown): ApiResult<User> {
|
|
80
|
+
const result = UserSchema.safeParse(input);
|
|
81
|
+
if (result.success) {
|
|
82
|
+
return { status: 'success', data: result.data };
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
status: 'error',
|
|
86
|
+
error: { code: 'VALIDATION', message: result.error.message },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Schema composition
|
|
91
|
+
const CreateUserSchema = UserSchema.omit({ id: true });
|
|
92
|
+
const UpdateUserSchema = UserSchema.partial().required({ id: true });
|
|
93
|
+
|
|
94
|
+
// API request validation
|
|
95
|
+
const PaginationSchema = z.object({
|
|
96
|
+
page: z.coerce.number().int().min(1).default(1),
|
|
97
|
+
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
98
|
+
sort: z.enum(['asc', 'desc']).default('desc'),
|
|
99
|
+
});
|
|
100
|
+
```
|
|
@@ -291,26 +291,7 @@ Model loading, compute units, conversion and optimization belong to the
|
|
|
291
291
|
`DataScannerViewController` (iOS 16+) is a full-screen live camera that finds
|
|
292
292
|
text and barcodes and highlights them.
|
|
293
293
|
|
|
294
|
-
|
|
295
|
-
import UIKit
|
|
296
|
-
import VisionKit
|
|
297
|
-
|
|
298
|
-
@MainActor
|
|
299
|
-
func presentTicketScanner(from host: UIViewController, delegate: DataScannerViewControllerDelegate) {
|
|
300
|
-
guard DataScannerViewController.isSupported, DataScannerViewController.isAvailable else { return }
|
|
301
|
-
let scanner = DataScannerViewController(
|
|
302
|
-
recognizedDataTypes: [.text(languages: ["en"]), .barcode(symbologies: [.qr, .aztec])],
|
|
303
|
-
qualityLevel: .balanced,
|
|
304
|
-
recognizesMultipleItems: false,
|
|
305
|
-
isHighFrameRateTrackingEnabled: true,
|
|
306
|
-
isHighlightingEnabled: true
|
|
307
|
-
)
|
|
308
|
-
scanner.delegate = delegate
|
|
309
|
-
host.present(scanner, animated: true) {
|
|
310
|
-
try? scanner.startScanning()
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
```
|
|
294
|
+
Code: [VisionKit live scanning: DataScannerViewController](references/code-examples.md#visionkit-live-scanning-datascannerviewcontroller).
|
|
314
295
|
|
|
315
296
|
`isSupported` checks the hardware (A12 Bionic or later); `isAvailable`
|
|
316
297
|
additionally requires camera permission. In SwiftUI, wrap the controller in a
|
|
@@ -362,3 +343,4 @@ additionally requires camera permission. In SwiftUI, wrap the controller in a
|
|
|
362
343
|
- https://developer.apple.com/documentation/visionkit
|
|
363
344
|
- https://developer.apple.com/documentation/vision/recognizetextrequest
|
|
364
345
|
- https://developer.apple.com/documentation/visionkit/datascannerviewcontroller
|
|
346
|
+
- [Code examples](references/code-examples.md): open for the full code behind VisionKit live scanning: DataScannerViewController.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Vision Framework Code Examples
|
|
2
|
+
|
|
3
|
+
Full code samples for the SKILL.md sections that link here. The rules and the decision guidance stay in SKILL.md; each heading below matches the SKILL.md section the code belongs to.
|
|
4
|
+
|
|
5
|
+
## VisionKit live scanning: DataScannerViewController
|
|
6
|
+
|
|
7
|
+
```swift
|
|
8
|
+
import UIKit
|
|
9
|
+
import VisionKit
|
|
10
|
+
|
|
11
|
+
@MainActor
|
|
12
|
+
func presentTicketScanner(from host: UIViewController, delegate: DataScannerViewControllerDelegate) {
|
|
13
|
+
guard DataScannerViewController.isSupported, DataScannerViewController.isAvailable else { return }
|
|
14
|
+
let scanner = DataScannerViewController(
|
|
15
|
+
recognizedDataTypes: [.text(languages: ["en"]), .barcode(symbologies: [.qr, .aztec])],
|
|
16
|
+
qualityLevel: .balanced,
|
|
17
|
+
recognizesMultipleItems: false,
|
|
18
|
+
isHighFrameRateTrackingEnabled: true,
|
|
19
|
+
isHighlightingEnabled: true
|
|
20
|
+
)
|
|
21
|
+
scanner.delegate = delegate
|
|
22
|
+
host.present(scanner, animated: true) {
|
|
23
|
+
try? scanner.startScanning()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
@@ -164,181 +164,15 @@ watch(
|
|
|
164
164
|
|
|
165
165
|
## Composables (useXxx Pattern)
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
// composables/useFetch.ts
|
|
169
|
-
import { ref, type Ref } from 'vue';
|
|
170
|
-
|
|
171
|
-
interface UseFetchReturn<T> {
|
|
172
|
-
data: Ref<T | null>;
|
|
173
|
-
error: Ref<string | null>;
|
|
174
|
-
isLoading: Ref<boolean>;
|
|
175
|
-
execute: () => Promise<void>;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function useFetch<T>(url: string | Ref<string>): UseFetchReturn<T> {
|
|
179
|
-
const data = ref<T | null>(null) as Ref<T | null>;
|
|
180
|
-
const error = ref<string | null>(null);
|
|
181
|
-
const isLoading = ref(false);
|
|
182
|
-
|
|
183
|
-
async function execute() {
|
|
184
|
-
isLoading.value = true;
|
|
185
|
-
error.value = null;
|
|
186
|
-
try {
|
|
187
|
-
const resolvedUrl = typeof url === 'string' ? url : url.value;
|
|
188
|
-
const response = await fetch(resolvedUrl);
|
|
189
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
190
|
-
data.value = await response.json();
|
|
191
|
-
} catch (e) {
|
|
192
|
-
error.value = e instanceof Error ? e.message : 'Unknown error';
|
|
193
|
-
} finally {
|
|
194
|
-
isLoading.value = false;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return { data, error, isLoading, execute };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// composables/useLocalStorage.ts
|
|
202
|
-
import { ref, watch, type Ref } from 'vue';
|
|
203
|
-
|
|
204
|
-
export function useLocalStorage<T>(key: string, defaultValue: T): Ref<T> {
|
|
205
|
-
const stored = localStorage.getItem(key);
|
|
206
|
-
const data = ref<T>(stored ? JSON.parse(stored) : defaultValue) as Ref<T>;
|
|
207
|
-
|
|
208
|
-
watch(data, (newValue) => {
|
|
209
|
-
localStorage.setItem(key, JSON.stringify(newValue));
|
|
210
|
-
}, { deep: true });
|
|
211
|
-
|
|
212
|
-
return data;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// composables/useDebounce.ts
|
|
216
|
-
import { ref, watch, type Ref } from 'vue';
|
|
217
|
-
|
|
218
|
-
export function useDebounce<T>(source: Ref<T>, delay = 300): Ref<T> {
|
|
219
|
-
const debounced = ref(source.value) as Ref<T>;
|
|
220
|
-
let timeout: ReturnType<typeof setTimeout>;
|
|
221
|
-
|
|
222
|
-
watch(source, (newValue) => {
|
|
223
|
-
clearTimeout(timeout);
|
|
224
|
-
timeout = setTimeout(() => {
|
|
225
|
-
debounced.value = newValue;
|
|
226
|
-
}, delay);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
return debounced;
|
|
230
|
-
}
|
|
231
|
-
```
|
|
167
|
+
Code for this section: [Vue examples](references/vue-examples.md).
|
|
232
168
|
|
|
233
169
|
## Pinia State Management
|
|
234
170
|
|
|
235
|
-
|
|
236
|
-
// stores/auth.ts
|
|
237
|
-
import { defineStore } from 'pinia';
|
|
238
|
-
import { ref, computed } from 'vue';
|
|
239
|
-
|
|
240
|
-
export const useAuthStore = defineStore('auth', () => {
|
|
241
|
-
// State
|
|
242
|
-
const user = ref<User | null>(null);
|
|
243
|
-
const token = ref<string | null>(null);
|
|
244
|
-
|
|
245
|
-
// Getters
|
|
246
|
-
const isAuthenticated = computed(() => !!token.value);
|
|
247
|
-
const displayName = computed(() => user.value?.name ?? 'Guest');
|
|
248
|
-
|
|
249
|
-
// Actions
|
|
250
|
-
async function login(credentials: { email: string; password: string }) {
|
|
251
|
-
const response = await api.post('/auth/login', credentials);
|
|
252
|
-
user.value = response.user;
|
|
253
|
-
token.value = response.token;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function logout() {
|
|
257
|
-
user.value = null;
|
|
258
|
-
token.value = null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return { user, token, isAuthenticated, displayName, login, logout };
|
|
262
|
-
}, {
|
|
263
|
-
persist: true, // pinia-plugin-persistedstate
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
// stores/cart.ts
|
|
267
|
-
export const useCartStore = defineStore('cart', () => {
|
|
268
|
-
const items = ref<CartItem[]>([]);
|
|
269
|
-
|
|
270
|
-
const totalItems = computed(() =>
|
|
271
|
-
items.value.reduce((sum, item) => sum + item.quantity, 0)
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
const totalPrice = computed(() =>
|
|
275
|
-
items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
function addItem(product: Product) {
|
|
279
|
-
const existing = items.value.find((i) => i.productId === product.id);
|
|
280
|
-
if (existing) {
|
|
281
|
-
existing.quantity++;
|
|
282
|
-
} else {
|
|
283
|
-
items.value.push({ productId: product.id, name: product.name, price: product.price, quantity: 1 });
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function removeItem(productId: string) {
|
|
288
|
-
items.value = items.value.filter((i) => i.productId !== productId);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return { items, totalItems, totalPrice, addItem, removeItem };
|
|
292
|
-
});
|
|
293
|
-
```
|
|
171
|
+
Code for this section: [Vue examples](references/vue-examples.md).
|
|
294
172
|
|
|
295
173
|
## Vue Router 4
|
|
296
174
|
|
|
297
|
-
|
|
298
|
-
// router/index.ts
|
|
299
|
-
import { createRouter, createWebHistory } from 'vue-router';
|
|
300
|
-
import type { RouteRecordRaw } from 'vue-router';
|
|
301
|
-
|
|
302
|
-
const routes: RouteRecordRaw[] = [
|
|
303
|
-
{
|
|
304
|
-
path: '/',
|
|
305
|
-
component: () => import('@/layouts/DefaultLayout.vue'),
|
|
306
|
-
children: [
|
|
307
|
-
{ path: '', name: 'home', component: () => import('@/pages/HomePage.vue') },
|
|
308
|
-
{ path: 'about', name: 'about', component: () => import('@/pages/AboutPage.vue') },
|
|
309
|
-
],
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
path: '/dashboard',
|
|
313
|
-
component: () => import('@/layouts/DashboardLayout.vue'),
|
|
314
|
-
meta: { requiresAuth: true },
|
|
315
|
-
children: [
|
|
316
|
-
{ path: '', name: 'dashboard', component: () => import('@/pages/DashboardPage.vue') },
|
|
317
|
-
{ path: 'settings', name: 'settings', component: () => import('@/pages/SettingsPage.vue') },
|
|
318
|
-
{ path: 'users/:id', name: 'user-detail', component: () => import('@/pages/UserDetailPage.vue'), props: true },
|
|
319
|
-
],
|
|
320
|
-
},
|
|
321
|
-
{ path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/pages/NotFoundPage.vue') },
|
|
322
|
-
];
|
|
323
|
-
|
|
324
|
-
const router = createRouter({
|
|
325
|
-
history: createWebHistory(),
|
|
326
|
-
routes,
|
|
327
|
-
scrollBehavior(to, from, savedPosition) {
|
|
328
|
-
return savedPosition ?? { top: 0 };
|
|
329
|
-
},
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
// Navigation guard
|
|
333
|
-
router.beforeEach((to, from) => {
|
|
334
|
-
const auth = useAuthStore();
|
|
335
|
-
if (to.meta.requiresAuth && !auth.isAuthenticated) {
|
|
336
|
-
return { name: 'login', query: { redirect: to.fullPath } };
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
export default router;
|
|
341
|
-
```
|
|
175
|
+
Code for this section: [Vue examples](references/vue-examples.md).
|
|
342
176
|
|
|
343
177
|
## Do's
|
|
344
178
|
|
|
@@ -372,3 +206,7 @@ export default router;
|
|
|
372
206
|
| Prop mutation warning | Modifying prop directly | Use `defineModel` or emit event with new value |
|
|
373
207
|
| Template ref is null | Accessed before mount | Use `onMounted` hook or optional chaining |
|
|
374
208
|
| Pinia state lost on refresh | No persistence plugin | Add `pinia-plugin-persistedstate` |
|
|
209
|
+
|
|
210
|
+
## References
|
|
211
|
+
|
|
212
|
+
- [Vue examples](references/vue-examples.md): open when writing a composable, a Pinia store or router configuration and guards.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Vue examples
|
|
2
|
+
|
|
3
|
+
<!-- toc -->
|
|
4
|
+
- [Composables (useXxx Pattern)](#composables-usexxx-pattern)
|
|
5
|
+
- [Pinia State Management](#pinia-state-management)
|
|
6
|
+
- [Vue Router 4](#vue-router-4)
|
|
7
|
+
<!-- /toc -->
|
|
8
|
+
|
|
9
|
+
Code for composables, Pinia stores and Vue Router 4. SKILL.md keeps the core concepts, script setup, reactivity and watcher rules.
|
|
10
|
+
|
|
11
|
+
## Composables (useXxx Pattern)
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// composables/useFetch.ts
|
|
15
|
+
import { ref, type Ref } from 'vue';
|
|
16
|
+
|
|
17
|
+
interface UseFetchReturn<T> {
|
|
18
|
+
data: Ref<T | null>;
|
|
19
|
+
error: Ref<string | null>;
|
|
20
|
+
isLoading: Ref<boolean>;
|
|
21
|
+
execute: () => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useFetch<T>(url: string | Ref<string>): UseFetchReturn<T> {
|
|
25
|
+
const data = ref<T | null>(null) as Ref<T | null>;
|
|
26
|
+
const error = ref<string | null>(null);
|
|
27
|
+
const isLoading = ref(false);
|
|
28
|
+
|
|
29
|
+
async function execute() {
|
|
30
|
+
isLoading.value = true;
|
|
31
|
+
error.value = null;
|
|
32
|
+
try {
|
|
33
|
+
const resolvedUrl = typeof url === 'string' ? url : url.value;
|
|
34
|
+
const response = await fetch(resolvedUrl);
|
|
35
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
36
|
+
data.value = await response.json();
|
|
37
|
+
} catch (e) {
|
|
38
|
+
error.value = e instanceof Error ? e.message : 'Unknown error';
|
|
39
|
+
} finally {
|
|
40
|
+
isLoading.value = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { data, error, isLoading, execute };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// composables/useLocalStorage.ts
|
|
48
|
+
import { ref, watch, type Ref } from 'vue';
|
|
49
|
+
|
|
50
|
+
export function useLocalStorage<T>(key: string, defaultValue: T): Ref<T> {
|
|
51
|
+
const stored = localStorage.getItem(key);
|
|
52
|
+
const data = ref<T>(stored ? JSON.parse(stored) : defaultValue) as Ref<T>;
|
|
53
|
+
|
|
54
|
+
watch(data, (newValue) => {
|
|
55
|
+
localStorage.setItem(key, JSON.stringify(newValue));
|
|
56
|
+
}, { deep: true });
|
|
57
|
+
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// composables/useDebounce.ts
|
|
62
|
+
import { ref, watch, type Ref } from 'vue';
|
|
63
|
+
|
|
64
|
+
export function useDebounce<T>(source: Ref<T>, delay = 300): Ref<T> {
|
|
65
|
+
const debounced = ref(source.value) as Ref<T>;
|
|
66
|
+
let timeout: ReturnType<typeof setTimeout>;
|
|
67
|
+
|
|
68
|
+
watch(source, (newValue) => {
|
|
69
|
+
clearTimeout(timeout);
|
|
70
|
+
timeout = setTimeout(() => {
|
|
71
|
+
debounced.value = newValue;
|
|
72
|
+
}, delay);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return debounced;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Pinia State Management
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// stores/auth.ts
|
|
83
|
+
import { defineStore } from 'pinia';
|
|
84
|
+
import { ref, computed } from 'vue';
|
|
85
|
+
|
|
86
|
+
export const useAuthStore = defineStore('auth', () => {
|
|
87
|
+
// State
|
|
88
|
+
const user = ref<User | null>(null);
|
|
89
|
+
const token = ref<string | null>(null);
|
|
90
|
+
|
|
91
|
+
// Getters
|
|
92
|
+
const isAuthenticated = computed(() => !!token.value);
|
|
93
|
+
const displayName = computed(() => user.value?.name ?? 'Guest');
|
|
94
|
+
|
|
95
|
+
// Actions
|
|
96
|
+
async function login(credentials: { email: string; password: string }) {
|
|
97
|
+
const response = await api.post('/auth/login', credentials);
|
|
98
|
+
user.value = response.user;
|
|
99
|
+
token.value = response.token;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function logout() {
|
|
103
|
+
user.value = null;
|
|
104
|
+
token.value = null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { user, token, isAuthenticated, displayName, login, logout };
|
|
108
|
+
}, {
|
|
109
|
+
persist: true, // pinia-plugin-persistedstate
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// stores/cart.ts
|
|
113
|
+
export const useCartStore = defineStore('cart', () => {
|
|
114
|
+
const items = ref<CartItem[]>([]);
|
|
115
|
+
|
|
116
|
+
const totalItems = computed(() =>
|
|
117
|
+
items.value.reduce((sum, item) => sum + item.quantity, 0)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const totalPrice = computed(() =>
|
|
121
|
+
items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
function addItem(product: Product) {
|
|
125
|
+
const existing = items.value.find((i) => i.productId === product.id);
|
|
126
|
+
if (existing) {
|
|
127
|
+
existing.quantity++;
|
|
128
|
+
} else {
|
|
129
|
+
items.value.push({ productId: product.id, name: product.name, price: product.price, quantity: 1 });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function removeItem(productId: string) {
|
|
134
|
+
items.value = items.value.filter((i) => i.productId !== productId);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { items, totalItems, totalPrice, addItem, removeItem };
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Vue Router 4
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// router/index.ts
|
|
145
|
+
import { createRouter, createWebHistory } from 'vue-router';
|
|
146
|
+
import type { RouteRecordRaw } from 'vue-router';
|
|
147
|
+
|
|
148
|
+
const routes: RouteRecordRaw[] = [
|
|
149
|
+
{
|
|
150
|
+
path: '/',
|
|
151
|
+
component: () => import('@/layouts/DefaultLayout.vue'),
|
|
152
|
+
children: [
|
|
153
|
+
{ path: '', name: 'home', component: () => import('@/pages/HomePage.vue') },
|
|
154
|
+
{ path: 'about', name: 'about', component: () => import('@/pages/AboutPage.vue') },
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
path: '/dashboard',
|
|
159
|
+
component: () => import('@/layouts/DashboardLayout.vue'),
|
|
160
|
+
meta: { requiresAuth: true },
|
|
161
|
+
children: [
|
|
162
|
+
{ path: '', name: 'dashboard', component: () => import('@/pages/DashboardPage.vue') },
|
|
163
|
+
{ path: 'settings', name: 'settings', component: () => import('@/pages/SettingsPage.vue') },
|
|
164
|
+
{ path: 'users/:id', name: 'user-detail', component: () => import('@/pages/UserDetailPage.vue'), props: true },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
{ path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/pages/NotFoundPage.vue') },
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
const router = createRouter({
|
|
171
|
+
history: createWebHistory(),
|
|
172
|
+
routes,
|
|
173
|
+
scrollBehavior(to, from, savedPosition) {
|
|
174
|
+
return savedPosition ?? { top: 0 };
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Navigation guard
|
|
179
|
+
router.beforeEach((to, from) => {
|
|
180
|
+
const auth = useAuthStore();
|
|
181
|
+
if (to.meta.requiresAuth && !auth.isAuthenticated) {
|
|
182
|
+
return { name: 'login', query: { redirect: to.fullPath } };
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
export default router;
|
|
187
|
+
```
|