@nahisaho/musubix-pattern-mcp 3.0.16 → 3.1.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/patterns/concurrency/index.d.ts +76 -0
- package/dist/patterns/concurrency/index.d.ts.map +1 -0
- package/dist/patterns/concurrency/index.js +413 -0
- package/dist/patterns/concurrency/index.js.map +1 -0
- package/dist/patterns/index.d.ts +11 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +11 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/time/index.d.ts +59 -0
- package/dist/patterns/time/index.d.ts.map +1 -0
- package/dist/patterns/time/index.js +362 -0
- package/dist/patterns/time/index.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,sBAAsB,CAAC;AAGrC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,sBAAsB,CAAC;AAGrC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,iCAAiC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,4 +16,6 @@ export * from './library/index.js';
|
|
|
16
16
|
export * from './privacy/index.js';
|
|
17
17
|
// Wake-Sleep Learning (TSK-WAKE-002)
|
|
18
18
|
export * from './learning/index.js';
|
|
19
|
+
// Concurrency Patterns (TSK-PAT-001, REQ-PAT-001)
|
|
20
|
+
export * from './patterns/concurrency/index.js';
|
|
19
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,uCAAuC;AACvC,cAAc,sBAAsB,CAAC;AAErC,wCAAwC;AACxC,cAAc,wBAAwB,CAAC;AAEvC,oCAAoC;AACpC,cAAc,oBAAoB,CAAC;AAEnC,uCAAuC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,qCAAqC;AACrC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,uCAAuC;AACvC,cAAc,sBAAsB,CAAC;AAErC,wCAAwC;AACxC,cAAc,wBAAwB,CAAC;AAEvC,oCAAoC;AACpC,cAAc,oBAAoB,CAAC;AAEnC,uCAAuC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,qCAAqC;AACrC,cAAc,qBAAqB,CAAC;AAEpC,kDAAkD;AAClD,cAAc,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency Patterns - Best Practices for Concurrent Operations
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
* @module patterns/concurrency
|
|
6
|
+
*
|
|
7
|
+
* @see REQ-PAT-001 - 同時実行パターン追加
|
|
8
|
+
* @see TSK-PAT-001 - 同時実行パターンタスク
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Concurrency pattern definition
|
|
12
|
+
*/
|
|
13
|
+
export interface ConcurrencyPattern {
|
|
14
|
+
/** Pattern ID */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Pattern name */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Short description */
|
|
19
|
+
description: string;
|
|
20
|
+
/** When to use this pattern */
|
|
21
|
+
useCase: string;
|
|
22
|
+
/** When NOT to use this pattern */
|
|
23
|
+
antiPattern?: string;
|
|
24
|
+
/** TypeScript template code */
|
|
25
|
+
template: string;
|
|
26
|
+
/** Usage example */
|
|
27
|
+
example: string;
|
|
28
|
+
/** Related patterns */
|
|
29
|
+
relatedPatterns: string[];
|
|
30
|
+
/** Tags for categorization */
|
|
31
|
+
tags: string[];
|
|
32
|
+
/** Confidence score (0-1) */
|
|
33
|
+
confidence: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* PAT-CONC-001: Optimistic Locking Pattern
|
|
37
|
+
*
|
|
38
|
+
* Use when: Multiple users may edit the same resource,
|
|
39
|
+
* but conflicts are rare.
|
|
40
|
+
*/
|
|
41
|
+
export declare const OPTIMISTIC_LOCKING: ConcurrencyPattern;
|
|
42
|
+
/**
|
|
43
|
+
* PAT-CONC-002: Pessimistic Locking Pattern
|
|
44
|
+
*
|
|
45
|
+
* Use when: Conflicts are frequent and you need to prevent them entirely.
|
|
46
|
+
*/
|
|
47
|
+
export declare const PESSIMISTIC_LOCKING: ConcurrencyPattern;
|
|
48
|
+
/**
|
|
49
|
+
* PAT-CONC-003: Hold Pattern
|
|
50
|
+
*
|
|
51
|
+
* Use when: You need to temporarily reserve resources before confirmation.
|
|
52
|
+
*/
|
|
53
|
+
export declare const HOLD_PATTERN: ConcurrencyPattern;
|
|
54
|
+
/**
|
|
55
|
+
* PAT-CONC-004: Idempotency Key Pattern
|
|
56
|
+
*
|
|
57
|
+
* Use when: Operations may be retried (network failures, user double-clicks).
|
|
58
|
+
*/
|
|
59
|
+
export declare const IDEMPOTENCY_KEY: ConcurrencyPattern;
|
|
60
|
+
/**
|
|
61
|
+
* All concurrency patterns
|
|
62
|
+
*/
|
|
63
|
+
export declare const CONCURRENCY_PATTERNS: ConcurrencyPattern[];
|
|
64
|
+
/**
|
|
65
|
+
* Get pattern by ID
|
|
66
|
+
*/
|
|
67
|
+
export declare function getConcurrencyPattern(id: string): ConcurrencyPattern | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Get patterns by tag
|
|
70
|
+
*/
|
|
71
|
+
export declare function getConcurrencyPatternsByTag(tag: string): ConcurrencyPattern[];
|
|
72
|
+
/**
|
|
73
|
+
* Get all concurrency pattern IDs
|
|
74
|
+
*/
|
|
75
|
+
export declare function getConcurrencyPatternIds(): string[];
|
|
76
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/patterns/concurrency/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,kBA6EhC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,kBAmFjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,kBA+F1B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,kBA4G7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,kBAAkB,EAKpD,CAAC;AAEF;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAEhF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAE7E;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD"}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency Patterns - Best Practices for Concurrent Operations
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
* @module patterns/concurrency
|
|
6
|
+
*
|
|
7
|
+
* @see REQ-PAT-001 - 同時実行パターン追加
|
|
8
|
+
* @see TSK-PAT-001 - 同時実行パターンタスク
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* PAT-CONC-001: Optimistic Locking Pattern
|
|
12
|
+
*
|
|
13
|
+
* Use when: Multiple users may edit the same resource,
|
|
14
|
+
* but conflicts are rare.
|
|
15
|
+
*/
|
|
16
|
+
export const OPTIMISTIC_LOCKING = {
|
|
17
|
+
id: 'PAT-CONC-001',
|
|
18
|
+
name: 'Optimistic Locking',
|
|
19
|
+
description: 'Allows concurrent reads while detecting conflicts at write time using a version field.',
|
|
20
|
+
useCase: 'Use when conflicts are rare and you want to maximize read concurrency. Good for high-read, low-write scenarios.',
|
|
21
|
+
antiPattern: 'Avoid when conflicts are frequent, as users will face many retry prompts.',
|
|
22
|
+
template: `
|
|
23
|
+
interface WithVersion {
|
|
24
|
+
version: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface OptimisticLockError extends Error {
|
|
28
|
+
code: 'OPTIMISTIC_LOCK_ERROR';
|
|
29
|
+
currentVersion: number;
|
|
30
|
+
providedVersion: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function updateWithOptimisticLock<T extends WithVersion>(
|
|
34
|
+
id: string,
|
|
35
|
+
updates: Partial<T>,
|
|
36
|
+
expectedVersion: number,
|
|
37
|
+
repository: {
|
|
38
|
+
findById(id: string): Promise<T | null>;
|
|
39
|
+
save(entity: T): Promise<T>;
|
|
40
|
+
}
|
|
41
|
+
): Promise<Result<T, OptimisticLockError>> {
|
|
42
|
+
const current = await repository.findById(id);
|
|
43
|
+
|
|
44
|
+
if (!current) {
|
|
45
|
+
return err({ code: 'NOT_FOUND' } as any);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (current.version !== expectedVersion) {
|
|
49
|
+
return err({
|
|
50
|
+
code: 'OPTIMISTIC_LOCK_ERROR',
|
|
51
|
+
message: \`Version conflict: expected \${expectedVersion}, found \${current.version}\`,
|
|
52
|
+
currentVersion: current.version,
|
|
53
|
+
providedVersion: expectedVersion,
|
|
54
|
+
} as OptimisticLockError);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const updated = {
|
|
58
|
+
...current,
|
|
59
|
+
...updates,
|
|
60
|
+
version: current.version + 1,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return ok(await repository.save(updated));
|
|
64
|
+
}
|
|
65
|
+
`.trim(),
|
|
66
|
+
example: `
|
|
67
|
+
// Entity with version field
|
|
68
|
+
interface Order extends WithVersion {
|
|
69
|
+
id: string;
|
|
70
|
+
status: OrderStatus;
|
|
71
|
+
version: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Update with optimistic lock
|
|
75
|
+
const result = await updateWithOptimisticLock(
|
|
76
|
+
orderId,
|
|
77
|
+
{ status: 'completed' },
|
|
78
|
+
order.version,
|
|
79
|
+
orderRepository
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (result.isErr() && result.error.code === 'OPTIMISTIC_LOCK_ERROR') {
|
|
83
|
+
// Handle conflict - reload and retry or notify user
|
|
84
|
+
console.log('Order was modified by another user');
|
|
85
|
+
}
|
|
86
|
+
`.trim(),
|
|
87
|
+
relatedPatterns: ['PAT-CONC-002', 'PAT-DESIGN-004'],
|
|
88
|
+
tags: ['concurrency', 'versioning', 'conflict-detection'],
|
|
89
|
+
confidence: 0.95,
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* PAT-CONC-002: Pessimistic Locking Pattern
|
|
93
|
+
*
|
|
94
|
+
* Use when: Conflicts are frequent and you need to prevent them entirely.
|
|
95
|
+
*/
|
|
96
|
+
export const PESSIMISTIC_LOCKING = {
|
|
97
|
+
id: 'PAT-CONC-002',
|
|
98
|
+
name: 'Pessimistic Locking',
|
|
99
|
+
description: 'Acquires exclusive lock before modification, preventing concurrent access.',
|
|
100
|
+
useCase: 'Use when conflicts are frequent or the cost of conflict resolution is high. Good for financial transactions.',
|
|
101
|
+
antiPattern: 'Avoid for high-concurrency read scenarios as it reduces throughput.',
|
|
102
|
+
template: `
|
|
103
|
+
interface Lock {
|
|
104
|
+
resourceId: string;
|
|
105
|
+
ownerId: string;
|
|
106
|
+
acquiredAt: Date;
|
|
107
|
+
expiresAt: Date;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface LockManager {
|
|
111
|
+
acquire(resourceId: string, ownerId: string, ttlMs: number): Promise<Lock | null>;
|
|
112
|
+
release(resourceId: string, ownerId: string): Promise<boolean>;
|
|
113
|
+
isLocked(resourceId: string): Promise<boolean>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function withPessimisticLock<T>(
|
|
117
|
+
resourceId: string,
|
|
118
|
+
ownerId: string,
|
|
119
|
+
lockManager: LockManager,
|
|
120
|
+
operation: () => Promise<T>,
|
|
121
|
+
options: { ttlMs?: number; retryCount?: number; retryDelayMs?: number } = {}
|
|
122
|
+
): Promise<Result<T, 'LOCK_FAILED' | 'OPERATION_FAILED'>> {
|
|
123
|
+
const { ttlMs = 30000, retryCount = 3, retryDelayMs = 100 } = options;
|
|
124
|
+
|
|
125
|
+
let lock: Lock | null = null;
|
|
126
|
+
let attempts = 0;
|
|
127
|
+
|
|
128
|
+
while (!lock && attempts < retryCount) {
|
|
129
|
+
lock = await lockManager.acquire(resourceId, ownerId, ttlMs);
|
|
130
|
+
if (!lock) {
|
|
131
|
+
attempts++;
|
|
132
|
+
await sleep(retryDelayMs * attempts);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!lock) {
|
|
137
|
+
return err('LOCK_FAILED');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const result = await operation();
|
|
142
|
+
return ok(result);
|
|
143
|
+
} finally {
|
|
144
|
+
await lockManager.release(resourceId, ownerId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function sleep(ms: number): Promise<void> {
|
|
149
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
150
|
+
}
|
|
151
|
+
`.trim(),
|
|
152
|
+
example: `
|
|
153
|
+
// Transfer money with pessimistic lock
|
|
154
|
+
const result = await withPessimisticLock(
|
|
155
|
+
\`account:\${fromAccountId}\`,
|
|
156
|
+
transactionId,
|
|
157
|
+
lockManager,
|
|
158
|
+
async () => {
|
|
159
|
+
const from = await accountRepo.findById(fromAccountId);
|
|
160
|
+
const to = await accountRepo.findById(toAccountId);
|
|
161
|
+
|
|
162
|
+
from.balance -= amount;
|
|
163
|
+
to.balance += amount;
|
|
164
|
+
|
|
165
|
+
await accountRepo.save(from);
|
|
166
|
+
await accountRepo.save(to);
|
|
167
|
+
|
|
168
|
+
return { from, to };
|
|
169
|
+
},
|
|
170
|
+
{ ttlMs: 5000 }
|
|
171
|
+
);
|
|
172
|
+
`.trim(),
|
|
173
|
+
relatedPatterns: ['PAT-CONC-001', 'PAT-CONC-003'],
|
|
174
|
+
tags: ['concurrency', 'locking', 'exclusive-access'],
|
|
175
|
+
confidence: 0.90,
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* PAT-CONC-003: Hold Pattern
|
|
179
|
+
*
|
|
180
|
+
* Use when: You need to temporarily reserve resources before confirmation.
|
|
181
|
+
*/
|
|
182
|
+
export const HOLD_PATTERN = {
|
|
183
|
+
id: 'PAT-CONC-003',
|
|
184
|
+
name: 'Hold Pattern',
|
|
185
|
+
description: 'Temporarily reserves a resource with automatic expiry, useful for booking/checkout flows.',
|
|
186
|
+
useCase: 'Use for e-commerce checkout, seat reservations, or any scenario where users need time to complete a multi-step process.',
|
|
187
|
+
antiPattern: 'Avoid for instant operations where reservation overhead is unnecessary.',
|
|
188
|
+
template: `
|
|
189
|
+
interface Hold<T> {
|
|
190
|
+
id: string;
|
|
191
|
+
resourceId: string;
|
|
192
|
+
resourceType: string;
|
|
193
|
+
heldBy: string;
|
|
194
|
+
heldData: T;
|
|
195
|
+
createdAt: Date;
|
|
196
|
+
expiresAt: Date;
|
|
197
|
+
status: 'active' | 'confirmed' | 'expired' | 'released';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
interface HoldManager<T> {
|
|
201
|
+
createHold(resourceId: string, data: T, ttlMs: number): Promise<Hold<T>>;
|
|
202
|
+
confirmHold(holdId: string): Promise<T | null>;
|
|
203
|
+
releaseHold(holdId: string): Promise<boolean>;
|
|
204
|
+
getHold(holdId: string): Promise<Hold<T> | null>;
|
|
205
|
+
cleanupExpired(): Promise<number>;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function createHoldManager<T>(
|
|
209
|
+
repository: HoldRepository<T>
|
|
210
|
+
): HoldManager<T> {
|
|
211
|
+
return {
|
|
212
|
+
async createHold(resourceId, data, ttlMs) {
|
|
213
|
+
const hold: Hold<T> = {
|
|
214
|
+
id: generateId(),
|
|
215
|
+
resourceId,
|
|
216
|
+
resourceType: typeof data,
|
|
217
|
+
heldBy: getCurrentUserId(),
|
|
218
|
+
heldData: data,
|
|
219
|
+
createdAt: new Date(),
|
|
220
|
+
expiresAt: new Date(Date.now() + ttlMs),
|
|
221
|
+
status: 'active',
|
|
222
|
+
};
|
|
223
|
+
return repository.save(hold);
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
async confirmHold(holdId) {
|
|
227
|
+
const hold = await repository.findById(holdId);
|
|
228
|
+
if (!hold || hold.status !== 'active') return null;
|
|
229
|
+
if (hold.expiresAt < new Date()) {
|
|
230
|
+
await repository.updateStatus(holdId, 'expired');
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
await repository.updateStatus(holdId, 'confirmed');
|
|
234
|
+
return hold.heldData;
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
async releaseHold(holdId) {
|
|
238
|
+
return repository.updateStatus(holdId, 'released');
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
async getHold(holdId) {
|
|
242
|
+
return repository.findById(holdId);
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
async cleanupExpired() {
|
|
246
|
+
return repository.markExpired(new Date());
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
`.trim(),
|
|
251
|
+
example: `
|
|
252
|
+
// E-commerce checkout flow
|
|
253
|
+
const holdManager = createHoldManager<CartItem[]>(holdRepository);
|
|
254
|
+
|
|
255
|
+
// Step 1: User adds items to cart - create hold
|
|
256
|
+
const hold = await holdManager.createHold(
|
|
257
|
+
'cart:' + cartId,
|
|
258
|
+
cartItems,
|
|
259
|
+
15 * 60 * 1000 // 15 minutes
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// Step 2: User completes payment
|
|
263
|
+
const confirmedItems = await holdManager.confirmHold(hold.id);
|
|
264
|
+
if (confirmedItems) {
|
|
265
|
+
await createOrder(confirmedItems);
|
|
266
|
+
} else {
|
|
267
|
+
// Hold expired, items may no longer be available
|
|
268
|
+
throw new Error('Checkout session expired');
|
|
269
|
+
}
|
|
270
|
+
`.trim(),
|
|
271
|
+
relatedPatterns: ['PAT-CONC-002', 'PAT-DESIGN-007'],
|
|
272
|
+
tags: ['concurrency', 'reservation', 'expiry', 'checkout'],
|
|
273
|
+
confidence: 0.90,
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* PAT-CONC-004: Idempotency Key Pattern
|
|
277
|
+
*
|
|
278
|
+
* Use when: Operations may be retried (network failures, user double-clicks).
|
|
279
|
+
*/
|
|
280
|
+
export const IDEMPOTENCY_KEY = {
|
|
281
|
+
id: 'PAT-CONC-004',
|
|
282
|
+
name: 'Idempotency Key',
|
|
283
|
+
description: 'Ensures operations are processed exactly once using a client-provided unique key.',
|
|
284
|
+
useCase: 'Use for payment processing, order creation, or any operation that should not be duplicated on retry.',
|
|
285
|
+
antiPattern: 'Not needed for naturally idempotent operations (like GET requests or updates that set absolute values).',
|
|
286
|
+
template: `
|
|
287
|
+
interface IdempotencyRecord<T> {
|
|
288
|
+
key: string;
|
|
289
|
+
requestHash: string;
|
|
290
|
+
status: 'processing' | 'completed' | 'failed';
|
|
291
|
+
result?: T;
|
|
292
|
+
error?: string;
|
|
293
|
+
createdAt: Date;
|
|
294
|
+
expiresAt: Date;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
interface IdempotencyStore<T> {
|
|
298
|
+
get(key: string): Promise<IdempotencyRecord<T> | null>;
|
|
299
|
+
create(key: string, requestHash: string, ttlMs: number): Promise<boolean>;
|
|
300
|
+
complete(key: string, result: T): Promise<void>;
|
|
301
|
+
fail(key: string, error: string): Promise<void>;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function withIdempotency<T>(
|
|
305
|
+
idempotencyKey: string,
|
|
306
|
+
requestBody: unknown,
|
|
307
|
+
store: IdempotencyStore<T>,
|
|
308
|
+
operation: () => Promise<T>,
|
|
309
|
+
options: { ttlMs?: number } = {}
|
|
310
|
+
): Promise<Result<T, 'IN_PROGRESS' | 'REQUEST_MISMATCH'>> {
|
|
311
|
+
const { ttlMs = 24 * 60 * 60 * 1000 } = options; // 24 hours default
|
|
312
|
+
const requestHash = hashRequest(requestBody);
|
|
313
|
+
|
|
314
|
+
// Check for existing record
|
|
315
|
+
const existing = await store.get(idempotencyKey);
|
|
316
|
+
|
|
317
|
+
if (existing) {
|
|
318
|
+
// Verify request body matches
|
|
319
|
+
if (existing.requestHash !== requestHash) {
|
|
320
|
+
return err('REQUEST_MISMATCH');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Return cached result if completed
|
|
324
|
+
if (existing.status === 'completed' && existing.result !== undefined) {
|
|
325
|
+
return ok(existing.result);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Still processing
|
|
329
|
+
if (existing.status === 'processing') {
|
|
330
|
+
return err('IN_PROGRESS');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Create new record
|
|
335
|
+
const created = await store.create(idempotencyKey, requestHash, ttlMs);
|
|
336
|
+
if (!created) {
|
|
337
|
+
return err('IN_PROGRESS'); // Race condition - another request got there first
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const result = await operation();
|
|
342
|
+
await store.complete(idempotencyKey, result);
|
|
343
|
+
return ok(result);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
await store.fail(idempotencyKey, String(error));
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function hashRequest(body: unknown): string {
|
|
351
|
+
return JSON.stringify(body); // Simplified - use crypto hash in production
|
|
352
|
+
}
|
|
353
|
+
`.trim(),
|
|
354
|
+
example: `
|
|
355
|
+
// Payment processing with idempotency
|
|
356
|
+
app.post('/payments', async (req, res) => {
|
|
357
|
+
const idempotencyKey = req.headers['idempotency-key'];
|
|
358
|
+
|
|
359
|
+
if (!idempotencyKey) {
|
|
360
|
+
return res.status(400).json({ error: 'Idempotency-Key header required' });
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const result = await withIdempotency(
|
|
364
|
+
idempotencyKey,
|
|
365
|
+
req.body,
|
|
366
|
+
idempotencyStore,
|
|
367
|
+
async () => {
|
|
368
|
+
return await paymentService.processPayment(req.body);
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (result.isErr()) {
|
|
373
|
+
if (result.error === 'IN_PROGRESS') {
|
|
374
|
+
return res.status(409).json({ error: 'Request in progress' });
|
|
375
|
+
}
|
|
376
|
+
return res.status(422).json({ error: 'Request body mismatch' });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return res.json(result.value);
|
|
380
|
+
});
|
|
381
|
+
`.trim(),
|
|
382
|
+
relatedPatterns: ['PAT-CONC-001', 'PAT-DESIGN-005'],
|
|
383
|
+
tags: ['concurrency', 'idempotency', 'retry-safety', 'payment'],
|
|
384
|
+
confidence: 0.95,
|
|
385
|
+
};
|
|
386
|
+
/**
|
|
387
|
+
* All concurrency patterns
|
|
388
|
+
*/
|
|
389
|
+
export const CONCURRENCY_PATTERNS = [
|
|
390
|
+
OPTIMISTIC_LOCKING,
|
|
391
|
+
PESSIMISTIC_LOCKING,
|
|
392
|
+
HOLD_PATTERN,
|
|
393
|
+
IDEMPOTENCY_KEY,
|
|
394
|
+
];
|
|
395
|
+
/**
|
|
396
|
+
* Get pattern by ID
|
|
397
|
+
*/
|
|
398
|
+
export function getConcurrencyPattern(id) {
|
|
399
|
+
return CONCURRENCY_PATTERNS.find((p) => p.id === id);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Get patterns by tag
|
|
403
|
+
*/
|
|
404
|
+
export function getConcurrencyPatternsByTag(tag) {
|
|
405
|
+
return CONCURRENCY_PATTERNS.filter((p) => p.tags.includes(tag));
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Get all concurrency pattern IDs
|
|
409
|
+
*/
|
|
410
|
+
export function getConcurrencyPatternIds() {
|
|
411
|
+
return CONCURRENCY_PATTERNS.map((p) => p.id);
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/patterns/concurrency/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/patterns/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/patterns/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time Constraint Patterns
|
|
3
|
+
*
|
|
4
|
+
* Best practice patterns for time-based operations.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
* @module patterns/time
|
|
8
|
+
*
|
|
9
|
+
* @see BP-DESIGN-007 - Expiry Time Logic
|
|
10
|
+
* @see TSK-PAT-002 - 時間制約パターン
|
|
11
|
+
*/
|
|
12
|
+
import type { DesignPattern, PatternExample } from '../../types.js';
|
|
13
|
+
/**
|
|
14
|
+
* PAT-TIME-001: Expiry Pattern
|
|
15
|
+
*
|
|
16
|
+
* Manages entities with expiration times.
|
|
17
|
+
*/
|
|
18
|
+
export declare const EXPIRY_PATTERN: DesignPattern;
|
|
19
|
+
/**
|
|
20
|
+
* PAT-TIME-002: Scheduled Event Pattern
|
|
21
|
+
*
|
|
22
|
+
* Handles events that occur at specific times.
|
|
23
|
+
*/
|
|
24
|
+
export declare const SCHEDULED_PATTERN: DesignPattern;
|
|
25
|
+
/**
|
|
26
|
+
* PAT-TIME-003: Interval Pattern
|
|
27
|
+
*
|
|
28
|
+
* Manages recurring events at fixed intervals.
|
|
29
|
+
*/
|
|
30
|
+
export declare const INTERVAL_PATTERN: DesignPattern;
|
|
31
|
+
/**
|
|
32
|
+
* PAT-TIME-004: Streak Pattern
|
|
33
|
+
*
|
|
34
|
+
* Tracks consecutive activity streaks.
|
|
35
|
+
*/
|
|
36
|
+
export declare const STREAK_PATTERN: DesignPattern;
|
|
37
|
+
/**
|
|
38
|
+
* PAT-TIME-005: Cooldown Pattern
|
|
39
|
+
*
|
|
40
|
+
* Prevents actions from being repeated too quickly.
|
|
41
|
+
*/
|
|
42
|
+
export declare const COOLDOWN_PATTERN: DesignPattern;
|
|
43
|
+
/**
|
|
44
|
+
* All time constraint patterns
|
|
45
|
+
*/
|
|
46
|
+
export declare const TIME_PATTERNS: DesignPattern[];
|
|
47
|
+
/**
|
|
48
|
+
* Get a time pattern by ID
|
|
49
|
+
*/
|
|
50
|
+
export declare function getTimePattern(id: string): DesignPattern | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Get patterns applicable to a domain
|
|
53
|
+
*/
|
|
54
|
+
export declare function getTimePatternsByDomain(domain: string): DesignPattern[];
|
|
55
|
+
/**
|
|
56
|
+
* Pattern examples for time patterns
|
|
57
|
+
*/
|
|
58
|
+
export declare const TIME_PATTERN_EXAMPLES: PatternExample[];
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/patterns/time/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,aAyC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,aA6C/B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,aAiD9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,aAyE5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,aA2D9B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,aAAa,EAMxC,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAEpE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAIvE;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,EA0CjD,CAAC"}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time Constraint Patterns
|
|
3
|
+
*
|
|
4
|
+
* Best practice patterns for time-based operations.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
* @module patterns/time
|
|
8
|
+
*
|
|
9
|
+
* @see BP-DESIGN-007 - Expiry Time Logic
|
|
10
|
+
* @see TSK-PAT-002 - 時間制約パターン
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* PAT-TIME-001: Expiry Pattern
|
|
14
|
+
*
|
|
15
|
+
* Manages entities with expiration times.
|
|
16
|
+
*/
|
|
17
|
+
export const EXPIRY_PATTERN = {
|
|
18
|
+
id: 'PAT-TIME-001',
|
|
19
|
+
name: 'Expiry Pattern',
|
|
20
|
+
category: 'temporal',
|
|
21
|
+
domain: ['all'],
|
|
22
|
+
description: 'Manages entities with explicit expiration times. Use expiresAt field to track validity.',
|
|
23
|
+
problem: 'Entities need to become invalid after a certain time (e.g., tokens, coupons, sessions).',
|
|
24
|
+
solution: 'Store expiresAt as a Date field. Provide isExpired() method that compares against current time.',
|
|
25
|
+
applicability: [
|
|
26
|
+
'Tokens and sessions',
|
|
27
|
+
'Coupons and promotions',
|
|
28
|
+
'Temporary access',
|
|
29
|
+
'Cache entries',
|
|
30
|
+
],
|
|
31
|
+
consequences: {
|
|
32
|
+
positive: [
|
|
33
|
+
'Clear expiration semantics',
|
|
34
|
+
'Easy to query expired entities',
|
|
35
|
+
'Timezone-safe with UTC',
|
|
36
|
+
],
|
|
37
|
+
negative: [
|
|
38
|
+
'Requires periodic cleanup jobs',
|
|
39
|
+
'Clock synchronization needed for distributed systems',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
implementation: `interface Expirable {
|
|
43
|
+
readonly expiresAt: Date;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isExpired(entity: Expirable, now: Date = new Date()): boolean {
|
|
47
|
+
return entity.expiresAt <= now;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function createExpiry(durationMs: number, from: Date = new Date()): Date {
|
|
51
|
+
return new Date(from.getTime() + durationMs);
|
|
52
|
+
}`,
|
|
53
|
+
relatedPatterns: ['PAT-TIME-002', 'PAT-TIME-003'],
|
|
54
|
+
confidence: 0.9,
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* PAT-TIME-002: Scheduled Event Pattern
|
|
58
|
+
*
|
|
59
|
+
* Handles events that occur at specific times.
|
|
60
|
+
*/
|
|
61
|
+
export const SCHEDULED_PATTERN = {
|
|
62
|
+
id: 'PAT-TIME-002',
|
|
63
|
+
name: 'Scheduled Event Pattern',
|
|
64
|
+
category: 'temporal',
|
|
65
|
+
domain: ['all'],
|
|
66
|
+
description: 'Manages events scheduled for future execution at specific times.',
|
|
67
|
+
problem: 'Need to execute actions at predetermined times (e.g., reminders, appointments).',
|
|
68
|
+
solution: 'Store scheduledAt timestamp. Use job queue to trigger actions when time arrives.',
|
|
69
|
+
applicability: [
|
|
70
|
+
'Appointment systems',
|
|
71
|
+
'Reminder notifications',
|
|
72
|
+
'Scheduled publishing',
|
|
73
|
+
'Delayed tasks',
|
|
74
|
+
],
|
|
75
|
+
consequences: {
|
|
76
|
+
positive: [
|
|
77
|
+
'Predictable execution timing',
|
|
78
|
+
'Supports multiple time zones',
|
|
79
|
+
'Can reschedule easily',
|
|
80
|
+
],
|
|
81
|
+
negative: [
|
|
82
|
+
'Requires reliable scheduler',
|
|
83
|
+
'Missed events need handling strategy',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
implementation: `interface Schedulable {
|
|
87
|
+
readonly scheduledAt: Date;
|
|
88
|
+
readonly status: 'pending' | 'executed' | 'cancelled';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isReady(event: Schedulable, now: Date = new Date()): boolean {
|
|
92
|
+
return event.status === 'pending' && event.scheduledAt <= now;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function reschedule(event: Schedulable, newTime: Date): Schedulable {
|
|
96
|
+
if (event.status !== 'pending') {
|
|
97
|
+
throw new Error('Cannot reschedule non-pending event');
|
|
98
|
+
}
|
|
99
|
+
return { ...event, scheduledAt: newTime };
|
|
100
|
+
}`,
|
|
101
|
+
relatedPatterns: ['PAT-TIME-001', 'PAT-TIME-003'],
|
|
102
|
+
confidence: 0.85,
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* PAT-TIME-003: Interval Pattern
|
|
106
|
+
*
|
|
107
|
+
* Manages recurring events at fixed intervals.
|
|
108
|
+
*/
|
|
109
|
+
export const INTERVAL_PATTERN = {
|
|
110
|
+
id: 'PAT-TIME-003',
|
|
111
|
+
name: 'Interval Pattern',
|
|
112
|
+
category: 'temporal',
|
|
113
|
+
domain: ['all'],
|
|
114
|
+
description: 'Manages recurring events that happen at fixed intervals.',
|
|
115
|
+
problem: 'Events need to repeat periodically (e.g., subscriptions, polling, cron jobs).',
|
|
116
|
+
solution: 'Store intervalMs and lastRunAt. Calculate nextRunAt from last execution.',
|
|
117
|
+
applicability: [
|
|
118
|
+
'Subscription billing',
|
|
119
|
+
'Recurring tasks',
|
|
120
|
+
'Health checks',
|
|
121
|
+
'Data synchronization',
|
|
122
|
+
],
|
|
123
|
+
consequences: {
|
|
124
|
+
positive: [
|
|
125
|
+
'Consistent timing',
|
|
126
|
+
'Easy to adjust frequency',
|
|
127
|
+
'Tracks execution history',
|
|
128
|
+
],
|
|
129
|
+
negative: [
|
|
130
|
+
'Drift accumulation if not anchored',
|
|
131
|
+
'Overlapping executions need handling',
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
implementation: `interface Recurring {
|
|
135
|
+
readonly intervalMs: number;
|
|
136
|
+
readonly lastRunAt: Date | null;
|
|
137
|
+
readonly anchorTime: Date;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getNextRunAt(recurring: Recurring, now: Date = new Date()): Date {
|
|
141
|
+
if (!recurring.lastRunAt) {
|
|
142
|
+
return recurring.anchorTime;
|
|
143
|
+
}
|
|
144
|
+
const elapsed = now.getTime() - recurring.anchorTime.getTime();
|
|
145
|
+
const periods = Math.floor(elapsed / recurring.intervalMs) + 1;
|
|
146
|
+
return new Date(recurring.anchorTime.getTime() + periods * recurring.intervalMs);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isDue(recurring: Recurring, now: Date = new Date()): boolean {
|
|
150
|
+
const next = getNextRunAt(recurring, now);
|
|
151
|
+
return next <= now;
|
|
152
|
+
}`,
|
|
153
|
+
relatedPatterns: ['PAT-TIME-001', 'PAT-TIME-002', 'PAT-TIME-004'],
|
|
154
|
+
confidence: 0.85,
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* PAT-TIME-004: Streak Pattern
|
|
158
|
+
*
|
|
159
|
+
* Tracks consecutive activity streaks.
|
|
160
|
+
*/
|
|
161
|
+
export const STREAK_PATTERN = {
|
|
162
|
+
id: 'PAT-TIME-004',
|
|
163
|
+
name: 'Streak Pattern',
|
|
164
|
+
category: 'temporal',
|
|
165
|
+
domain: ['fitness', 'education', 'gaming'],
|
|
166
|
+
description: 'Tracks consecutive day/activity streaks with grace period support.',
|
|
167
|
+
problem: 'Need to track user engagement through consecutive activity days (e.g., login streaks, workout streaks).',
|
|
168
|
+
solution: 'Store currentStreak, longestStreak, lastActivityAt. Update on each activity with grace period.',
|
|
169
|
+
applicability: [
|
|
170
|
+
'Gamification features',
|
|
171
|
+
'Habit tracking',
|
|
172
|
+
'User engagement metrics',
|
|
173
|
+
'Loyalty programs',
|
|
174
|
+
],
|
|
175
|
+
consequences: {
|
|
176
|
+
positive: [
|
|
177
|
+
'Motivates consistent engagement',
|
|
178
|
+
'Clear progress tracking',
|
|
179
|
+
'Supports grace periods',
|
|
180
|
+
],
|
|
181
|
+
negative: [
|
|
182
|
+
'Timezone handling complexity',
|
|
183
|
+
'Grace period edge cases',
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
implementation: `interface Streak {
|
|
187
|
+
currentStreak: number;
|
|
188
|
+
longestStreak: number;
|
|
189
|
+
lastActivityAt: Date | null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
193
|
+
|
|
194
|
+
function recordActivity(
|
|
195
|
+
streak: Streak,
|
|
196
|
+
gracePeriodMs: number = ONE_DAY_MS * 1.5,
|
|
197
|
+
now: Date = new Date()
|
|
198
|
+
): Streak {
|
|
199
|
+
if (!streak.lastActivityAt) {
|
|
200
|
+
return {
|
|
201
|
+
currentStreak: 1,
|
|
202
|
+
longestStreak: Math.max(1, streak.longestStreak),
|
|
203
|
+
lastActivityAt: now,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const elapsed = now.getTime() - streak.lastActivityAt.getTime();
|
|
208
|
+
|
|
209
|
+
if (elapsed < ONE_DAY_MS) {
|
|
210
|
+
// Same day, no change
|
|
211
|
+
return streak;
|
|
212
|
+
} else if (elapsed <= gracePeriodMs) {
|
|
213
|
+
// Within grace period, increment streak
|
|
214
|
+
const newCurrent = streak.currentStreak + 1;
|
|
215
|
+
return {
|
|
216
|
+
currentStreak: newCurrent,
|
|
217
|
+
longestStreak: Math.max(newCurrent, streak.longestStreak),
|
|
218
|
+
lastActivityAt: now,
|
|
219
|
+
};
|
|
220
|
+
} else {
|
|
221
|
+
// Streak broken
|
|
222
|
+
return {
|
|
223
|
+
currentStreak: 1,
|
|
224
|
+
longestStreak: streak.longestStreak,
|
|
225
|
+
lastActivityAt: now,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}`,
|
|
229
|
+
relatedPatterns: ['PAT-TIME-003'],
|
|
230
|
+
confidence: 0.8,
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* PAT-TIME-005: Cooldown Pattern
|
|
234
|
+
*
|
|
235
|
+
* Prevents actions from being repeated too quickly.
|
|
236
|
+
*/
|
|
237
|
+
export const COOLDOWN_PATTERN = {
|
|
238
|
+
id: 'PAT-TIME-005',
|
|
239
|
+
name: 'Cooldown Pattern',
|
|
240
|
+
category: 'temporal',
|
|
241
|
+
domain: ['all'],
|
|
242
|
+
description: 'Prevents actions from being performed too frequently with a cooldown period.',
|
|
243
|
+
problem: 'Need to rate-limit user actions (e.g., password reset, notifications, API calls).',
|
|
244
|
+
solution: 'Track lastActionAt and cooldownMs. Check if enough time has passed before allowing action.',
|
|
245
|
+
applicability: [
|
|
246
|
+
'Rate limiting',
|
|
247
|
+
'Spam prevention',
|
|
248
|
+
'Resource protection',
|
|
249
|
+
'Gaming abilities',
|
|
250
|
+
],
|
|
251
|
+
consequences: {
|
|
252
|
+
positive: [
|
|
253
|
+
'Prevents abuse',
|
|
254
|
+
'Fair resource distribution',
|
|
255
|
+
'Simple implementation',
|
|
256
|
+
],
|
|
257
|
+
negative: [
|
|
258
|
+
'May frustrate legitimate users',
|
|
259
|
+
'Clock skew issues in distributed systems',
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
implementation: `interface Cooldownable {
|
|
263
|
+
readonly lastActionAt: Date | null;
|
|
264
|
+
readonly cooldownMs: number;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function canPerformAction(
|
|
268
|
+
item: Cooldownable,
|
|
269
|
+
now: Date = new Date()
|
|
270
|
+
): { allowed: boolean; remainingMs: number } {
|
|
271
|
+
if (!item.lastActionAt) {
|
|
272
|
+
return { allowed: true, remainingMs: 0 };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const elapsed = now.getTime() - item.lastActionAt.getTime();
|
|
276
|
+
const remaining = item.cooldownMs - elapsed;
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
allowed: remaining <= 0,
|
|
280
|
+
remainingMs: Math.max(0, remaining),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function performAction(item: Cooldownable, now: Date = new Date()): Cooldownable {
|
|
285
|
+
const check = canPerformAction(item, now);
|
|
286
|
+
if (!check.allowed) {
|
|
287
|
+
throw new Error(\`Action on cooldown. Wait \${check.remainingMs}ms\`);
|
|
288
|
+
}
|
|
289
|
+
return { ...item, lastActionAt: now };
|
|
290
|
+
}`,
|
|
291
|
+
relatedPatterns: ['PAT-TIME-001', 'PAT-CONC-004'],
|
|
292
|
+
confidence: 0.85,
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* All time constraint patterns
|
|
296
|
+
*/
|
|
297
|
+
export const TIME_PATTERNS = [
|
|
298
|
+
EXPIRY_PATTERN,
|
|
299
|
+
SCHEDULED_PATTERN,
|
|
300
|
+
INTERVAL_PATTERN,
|
|
301
|
+
STREAK_PATTERN,
|
|
302
|
+
COOLDOWN_PATTERN,
|
|
303
|
+
];
|
|
304
|
+
/**
|
|
305
|
+
* Get a time pattern by ID
|
|
306
|
+
*/
|
|
307
|
+
export function getTimePattern(id) {
|
|
308
|
+
return TIME_PATTERNS.find((p) => p.id === id);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get patterns applicable to a domain
|
|
312
|
+
*/
|
|
313
|
+
export function getTimePatternsByDomain(domain) {
|
|
314
|
+
return TIME_PATTERNS.filter((p) => p.domain.includes('all') || p.domain.includes(domain));
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Pattern examples for time patterns
|
|
318
|
+
*/
|
|
319
|
+
export const TIME_PATTERN_EXAMPLES = [
|
|
320
|
+
{
|
|
321
|
+
patternId: 'PAT-TIME-001',
|
|
322
|
+
scenario: 'クーポンの有効期限管理',
|
|
323
|
+
code: `const coupon = {
|
|
324
|
+
code: 'SUMMER2025',
|
|
325
|
+
discount: 20,
|
|
326
|
+
expiresAt: new Date('2025-08-31T23:59:59Z'),
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
if (isExpired(coupon)) {
|
|
330
|
+
throw new Error('このクーポンは有効期限切れです');
|
|
331
|
+
}`,
|
|
332
|
+
domain: 'ecommerce',
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
patternId: 'PAT-TIME-002',
|
|
336
|
+
scenario: '予約のスケジュール管理',
|
|
337
|
+
code: `const appointment = {
|
|
338
|
+
id: 'APT-001',
|
|
339
|
+
scheduledAt: new Date('2025-01-20T10:00:00Z'),
|
|
340
|
+
status: 'pending' as const,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
if (isReady(appointment)) {
|
|
344
|
+
sendReminder(appointment);
|
|
345
|
+
}`,
|
|
346
|
+
domain: 'healthcare',
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
patternId: 'PAT-TIME-004',
|
|
350
|
+
scenario: 'フィットネスアプリの連続記録',
|
|
351
|
+
code: `let streak: Streak = {
|
|
352
|
+
currentStreak: 5,
|
|
353
|
+
longestStreak: 10,
|
|
354
|
+
lastActivityAt: new Date('2025-01-18'),
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
streak = recordActivity(streak);
|
|
358
|
+
console.log(\`現在の連続: \${streak.currentStreak}日\`);`,
|
|
359
|
+
domain: 'fitness',
|
|
360
|
+
},
|
|
361
|
+
];
|
|
362
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/patterns/time/index.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -86,4 +86,49 @@ export interface PrivacyFilterResult {
|
|
|
86
86
|
reason?: string;
|
|
87
87
|
sanitizedPattern?: Pattern;
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Design Pattern - Best practice pattern definition
|
|
91
|
+
*/
|
|
92
|
+
export interface DesignPattern {
|
|
93
|
+
/** Unique pattern ID (e.g., PAT-CONC-001, PAT-TIME-001) */
|
|
94
|
+
id: string;
|
|
95
|
+
/** Human-readable name */
|
|
96
|
+
name: string;
|
|
97
|
+
/** Pattern category (concurrency, temporal, etc.) */
|
|
98
|
+
category: string;
|
|
99
|
+
/** Applicable domains */
|
|
100
|
+
domain: string[];
|
|
101
|
+
/** Brief description */
|
|
102
|
+
description: string;
|
|
103
|
+
/** Problem this pattern solves */
|
|
104
|
+
problem: string;
|
|
105
|
+
/** Solution approach */
|
|
106
|
+
solution: string;
|
|
107
|
+
/** When to apply this pattern */
|
|
108
|
+
applicability: string[];
|
|
109
|
+
/** Trade-offs */
|
|
110
|
+
consequences: {
|
|
111
|
+
positive: string[];
|
|
112
|
+
negative: string[];
|
|
113
|
+
};
|
|
114
|
+
/** TypeScript implementation example */
|
|
115
|
+
implementation: string;
|
|
116
|
+
/** Related pattern IDs */
|
|
117
|
+
relatedPatterns?: string[];
|
|
118
|
+
/** Confidence score (0-1) */
|
|
119
|
+
confidence: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Pattern Example - Concrete usage example
|
|
123
|
+
*/
|
|
124
|
+
export interface PatternExample {
|
|
125
|
+
/** Pattern ID this example demonstrates */
|
|
126
|
+
patternId: string;
|
|
127
|
+
/** Scenario description */
|
|
128
|
+
scenario: string;
|
|
129
|
+
/** Example code */
|
|
130
|
+
code: string;
|
|
131
|
+
/** Domain this example applies to */
|
|
132
|
+
domain: string;
|
|
133
|
+
}
|
|
89
134
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,QAAQ,CAAC;IACxB,WAAW,EAAE,QAAQ,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED