opencode-effect-enforcer 0.2.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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +278 -0
  3. package/guidance/effect-first-development.md +1247 -0
  4. package/guidance/post__effect-and-the-near-inexpressible-majesty-of-layers.md +490 -0
  5. package/guidance/post__parse-dont-validate.md +109 -0
  6. package/guidance/progressive-disclosure-guidance.md +38 -0
  7. package/package.json +63 -0
  8. package/patterns/avoid-any.md +37 -0
  9. package/patterns/avoid-data-tagged-error.md +34 -0
  10. package/patterns/avoid-direct-json.md +51 -0
  11. package/patterns/avoid-direct-tag-checks.md +54 -0
  12. package/patterns/avoid-expect-in-if.md +52 -0
  13. package/patterns/avoid-mutable-state.md +70 -0
  14. package/patterns/avoid-native-fetch.md +61 -0
  15. package/patterns/avoid-node-imports.md +86 -0
  16. package/patterns/avoid-non-null-assertion.md +44 -0
  17. package/patterns/avoid-object-type.md +46 -0
  18. package/patterns/avoid-option-getorthrow.md +39 -0
  19. package/patterns/avoid-platform-coupling.md +43 -0
  20. package/patterns/avoid-process-env.md +43 -0
  21. package/patterns/avoid-react-hooks.md +73 -0
  22. package/patterns/avoid-schema-suffix.md +45 -0
  23. package/patterns/avoid-sync-fs.md +68 -0
  24. package/patterns/avoid-try-catch.md +47 -0
  25. package/patterns/avoid-ts-ignore.md +38 -0
  26. package/patterns/avoid-untagged-errors.md +67 -0
  27. package/patterns/avoid-yield-ref.md +46 -0
  28. package/patterns/casting-awareness.md +46 -0
  29. package/patterns/context-tag-extends.md +84 -0
  30. package/patterns/effect-catchall-default.md +61 -0
  31. package/patterns/effect-promise-vs-trypromise.md +47 -0
  32. package/patterns/effect-run-in-body.md +58 -0
  33. package/patterns/imperative-loops.md +76 -0
  34. package/patterns/prefer-arr-sort.md +52 -0
  35. package/patterns/prefer-duration-values.md +56 -0
  36. package/patterns/prefer-effect-fn.md +161 -0
  37. package/patterns/prefer-match-over-switch.md +48 -0
  38. package/patterns/prefer-option-over-null.md +56 -0
  39. package/patterns/prefer-redacted-config.md +70 -0
  40. package/patterns/prefer-schema-class.md +54 -0
  41. package/patterns/require-effect-concurrency.md +83 -0
  42. package/patterns/stream-large-files.md +63 -0
  43. package/patterns/throw-in-effect-gen.md +62 -0
  44. package/patterns/use-clock-service.md +45 -0
  45. package/patterns/use-command-executor-service.md +54 -0
  46. package/patterns/use-console-service.md +54 -0
  47. package/patterns/use-filesystem-service.md +59 -0
  48. package/patterns/use-http-client-service.md +77 -0
  49. package/patterns/use-path-service.md +53 -0
  50. package/patterns/use-random-service.md +45 -0
  51. package/patterns/use-temp-file-scoped.md +66 -0
  52. package/patterns/vm-in-wrong-file.md +51 -0
  53. package/patterns/yield-in-for-loop.md +61 -0
  54. package/skills/effect-ai-chat/SKILL.md +472 -0
  55. package/skills/effect-ai-language-model/SKILL.md +652 -0
  56. package/skills/effect-ai-prompt/SKILL.md +752 -0
  57. package/skills/effect-ai-provider/SKILL.md +668 -0
  58. package/skills/effect-ai-streaming/SKILL.md +418 -0
  59. package/skills/effect-ai-tool/SKILL.md +1132 -0
  60. package/skills/effect-atom-rpc/SKILL.md +488 -0
  61. package/skills/effect-atom-state/SKILL.md +640 -0
  62. package/skills/effect-batching/SKILL.md +614 -0
  63. package/skills/effect-cache/SKILL.md +570 -0
  64. package/skills/effect-cli/SKILL.md +523 -0
  65. package/skills/effect-command-executor/SKILL.md +675 -0
  66. package/skills/effect-concurrency-testing/SKILL.md +612 -0
  67. package/skills/effect-config/SKILL.md +580 -0
  68. package/skills/effect-context-witness/SKILL.md +274 -0
  69. package/skills/effect-domain-modeling/SKILL.md +1212 -0
  70. package/skills/effect-domain-predicates/SKILL.md +867 -0
  71. package/skills/effect-error-handling/SKILL.md +1581 -0
  72. package/skills/effect-fiber/SKILL.md +731 -0
  73. package/skills/effect-filesystem/SKILL.md +624 -0
  74. package/skills/effect-graph/SKILL.md +571 -0
  75. package/skills/effect-http-api/SKILL.md +1760 -0
  76. package/skills/effect-http-client/SKILL.md +989 -0
  77. package/skills/effect-http-server/SKILL.md +920 -0
  78. package/skills/effect-incremental-migration/SKILL.md +362 -0
  79. package/skills/effect-layer-design/SKILL.md +642 -0
  80. package/skills/effect-managed-runtime/SKILL.md +395 -0
  81. package/skills/effect-mcp-server/SKILL.md +608 -0
  82. package/skills/effect-observability/SKILL.md +719 -0
  83. package/skills/effect-optics/SKILL.md +554 -0
  84. package/skills/effect-parallelization/SKILL.md +668 -0
  85. package/skills/effect-path/SKILL.md +296 -0
  86. package/skills/effect-pattern-matching/SKILL.md +914 -0
  87. package/skills/effect-platform-abstraction/SKILL.md +1175 -0
  88. package/skills/effect-platform-layers/SKILL.md +514 -0
  89. package/skills/effect-pubsub-event-bus/SKILL.md +384 -0
  90. package/skills/effect-react-composition/SKILL.md +986 -0
  91. package/skills/effect-react-vm/SKILL.md +675 -0
  92. package/skills/effect-rpc-api/SKILL.md +624 -0
  93. package/skills/effect-rpc-client/SKILL.md +666 -0
  94. package/skills/effect-rpc-cluster/SKILL.md +1623 -0
  95. package/skills/effect-rpc-server/SKILL.md +767 -0
  96. package/skills/effect-scheduling/SKILL.md +124 -0
  97. package/skills/effect-schema-composition/SKILL.md +975 -0
  98. package/skills/effect-schema-v4/SKILL.md +691 -0
  99. package/skills/effect-scope/SKILL.md +682 -0
  100. package/skills/effect-service-implementation/SKILL.md +656 -0
  101. package/skills/effect-socket/SKILL.md +703 -0
  102. package/skills/effect-sql/SKILL.md +781 -0
  103. package/skills/effect-stream/SKILL.md +765 -0
  104. package/skills/effect-testing/SKILL.md +1331 -0
  105. package/skills/effect-typeclass-design/SKILL.md +161 -0
  106. package/skills/effect-wide-events/Article.md +66 -0
  107. package/skills/effect-wide-events/SKILL.md +95 -0
  108. package/skills/effect-workflow/SKILL.md +810 -0
  109. package/src/agent-policy.ts +22 -0
  110. package/src/enforcer.ts +104 -0
  111. package/src/frontmatter.ts +34 -0
  112. package/src/guidance.ts +66 -0
  113. package/src/index.ts +38 -0
  114. package/src/pattern-catalog.ts +115 -0
  115. package/src/pattern-matcher.ts +178 -0
  116. package/src/pattern.ts +97 -0
  117. package/src/skills.ts +29 -0
  118. package/src/write-projection.ts +66 -0
@@ -0,0 +1,867 @@
1
+ ---
2
+ name: effect-domain-predicates
3
+ description: Generate comprehensive predicates and orders for domain types using typeclass patterns
4
+ ---
5
+
6
+ # Domain Predicates Skill
7
+
8
+ Generate complete sets of predicates and Order instances for domain types, derived from typeclass implementations.
9
+
10
+ ## Effect Source Reference
11
+
12
+ The Effect v4 source is available at `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/`.
13
+ Browse and read files there directly to look up APIs, types, and implementations.
14
+
15
+ Reference this for:
16
+
17
+ - Full Schema API: `packages/effect/SCHEMA.md`
18
+ - Order source: `packages/effect/src/Order.ts`
19
+ - Equivalence source: `packages/effect/src/Equivalence.ts`
20
+ - Predicate source: `packages/effect/src/Predicate.ts`
21
+ - Effect source: `packages/effect/src/`
22
+
23
+ ## Pattern: Deep Structural Equality (v4)
24
+
25
+ In v4, `Equal.equals` performs deep structural comparison by default — no special wrapping is needed:
26
+
27
+ ```typescript
28
+ import { Schema, Equal, DateTime } from 'effect';
29
+
30
+ export const Task = Schema.TaggedStruct('pending', {
31
+ id: Schema.String,
32
+ createdAt: Schema.DateTimeUtc
33
+ });
34
+
35
+ export type Task = Schema.Schema.Type<typeof Task>;
36
+
37
+ declare const makeTask: (props: {
38
+ id: string;
39
+ createdAt: DateTime.Utc;
40
+ }) => Task;
41
+ declare const now: DateTime.Utc;
42
+
43
+ // Usage: Deep structural equality (automatic in v4)
44
+ const task1 = makeTask({ id: '123', createdAt: now });
45
+ const task2 = makeTask({ id: '123', createdAt: now });
46
+
47
+ Equal.equals(task1, task2); // true - deep structural equality
48
+ ```
49
+
50
+ ## Pattern: Equivalence from Schema
51
+
52
+ When you need an `Equivalence` instance (for use with combinators), derive it from the schema:
53
+
54
+ ```typescript
55
+ import { Schema, Array } from 'effect';
56
+ import * as Equivalence from 'effect/Equivalence';
57
+
58
+ declare const Task: Schema.Schema<any, any, never>;
59
+ type Task = Schema.Schema.Type<typeof Task>;
60
+
61
+ // Derive from schema (structural equality)
62
+ export const TaskEquivalence = Schema.toEquivalence(Task);
63
+
64
+ declare const tasks: Array<Task>;
65
+
66
+ // Usage with combinators
67
+ const uniqueTasks = Array.dedupeWith(tasks, TaskEquivalence);
68
+ ```
69
+
70
+ ## Pattern: Field-Based Equivalence with Equivalence.mapInput
71
+
72
+ Compare by specific fields using `Equivalence.mapInput`:
73
+
74
+ ```typescript
75
+ import { DateTime } from 'effect';
76
+ import * as Equivalence from 'effect/Equivalence';
77
+
78
+ interface Task {
79
+ readonly _tag: string;
80
+ readonly id: string;
81
+ readonly createdAt: DateTime.Utc;
82
+ }
83
+
84
+ /**
85
+ * Compare tasks by ID only.
86
+ *
87
+ * @category Equivalence
88
+ * @since 0.1.0
89
+ * @example
90
+ * import * as Task from "@/schemas/Task"
91
+ * import * as Array from "effect/Array"
92
+ *
93
+ * const uniqueById = Array.dedupeWith(tasks, Task.EquivalenceById)
94
+ */
95
+ export const EquivalenceById = Equivalence.mapInput(
96
+ Equivalence.String,
97
+ (task: Task) => task.id
98
+ );
99
+
100
+ /**
101
+ * Compare by status tag.
102
+ *
103
+ * @category Equivalence
104
+ * @since 0.1.0
105
+ */
106
+ export const EquivalenceByTag = Equivalence.mapInput(
107
+ Equivalence.String,
108
+ (task: Task) => task._tag
109
+ );
110
+
111
+ /**
112
+ * Compare by creation date.
113
+ *
114
+ * @category Equivalence
115
+ * @since 0.1.0
116
+ */
117
+ export const EquivalenceByCreatedAt = Equivalence.mapInput(
118
+ DateTime.Equivalence,
119
+ (task: Task) => task.createdAt
120
+ );
121
+ ```
122
+
123
+ **Key Pattern: Equivalence.mapInput**
124
+
125
+ - Signature: `Equivalence.mapInput(baseEquivalence, (value) => extractField)`
126
+ - Compose from simpler equivalences
127
+ - Map domain type to comparable value
128
+ - Dual API: data-first and data-last
129
+
130
+ ## Pattern: Combining Equivalences
131
+
132
+ Use `Equivalence.combine` for two-field equality and `Equivalence.combineAll` for three or more fields:
133
+
134
+ ```typescript
135
+ import { DateTime } from 'effect';
136
+ import * as Equivalence from 'effect/Equivalence';
137
+
138
+ interface Task {
139
+ readonly _tag: string;
140
+ readonly id: string;
141
+ readonly createdAt: DateTime.Utc;
142
+ }
143
+
144
+ declare const EquivalenceByTag: Equivalence.Equivalence<Task>;
145
+ declare const EquivalenceById: Equivalence.Equivalence<Task>;
146
+ declare const EquivalenceByCreatedAt: Equivalence.Equivalence<Task>;
147
+
148
+ /**
149
+ * Compare by tag first, then by ID.
150
+ *
151
+ * Both must match for equivalence.
152
+ *
153
+ * @category Equivalence
154
+ * @since 0.1.0
155
+ * @example
156
+ * import * as Task from "@/schemas/Task"
157
+ *
158
+ * const areSame = Task.EquivalenceByTagAndId(task1, task2)
159
+ */
160
+ export const EquivalenceByTagAndId = Equivalence.combine(
161
+ EquivalenceByTag,
162
+ EquivalenceById
163
+ );
164
+
165
+ /**
166
+ * Compare by multiple criteria for exact equality.
167
+ *
168
+ * @category Equivalence
169
+ * @since 0.1.0
170
+ */
171
+ export const EquivalenceComplete = Equivalence.combineAll([
172
+ EquivalenceByTag,
173
+ EquivalenceById,
174
+ EquivalenceByCreatedAt
175
+ ]);
176
+ ```
177
+
178
+ **Key Pattern: Equivalence.combine / combineAll**
179
+
180
+ - `Equivalence.combine` combines two equivalences
181
+ - Use `Equivalence.combineAll([...])` for three or more equivalences
182
+ - All must match for equivalence (AND logic)
183
+ - Order doesn't matter (unlike Order.combine)
184
+
185
+ ## Pattern: Typeclass-Derived Predicates
186
+
187
+ When a domain type implements a typeclass, re-export all relevant predicates:
188
+
189
+ ```typescript
190
+ import { DateTime, Duration, Schema } from 'effect';
191
+ import * as Order from 'effect/Order';
192
+
193
+ // Inline typeclass interface declarations (instead of module augmentations)
194
+ interface SchedulableInstance<A> {
195
+ readonly get: (self: A) => DateTime.DateTime;
196
+ readonly set: (self: A, date: DateTime.DateTime) => A;
197
+ }
198
+
199
+ interface DurableInstance<A> {
200
+ readonly get: (self: A) => Duration.Duration;
201
+ readonly set: (self: A, duration: Duration.Duration) => A;
202
+ }
203
+
204
+ // Typeclass module declarations
205
+ declare const Schedulable$: {
206
+ make: <A>(
207
+ get: (self: A) => DateTime.DateTime,
208
+ set: (self: A, date: DateTime.DateTime) => A
209
+ ) => SchedulableInstance<A>;
210
+ isScheduledBefore: <A>(
211
+ instance: SchedulableInstance<A>
212
+ ) => (date: DateTime.DateTime) => (self: A) => boolean;
213
+ isScheduledAfter: <A>(
214
+ instance: SchedulableInstance<A>
215
+ ) => (date: DateTime.DateTime) => (self: A) => boolean;
216
+ isScheduledBetween: <A>(
217
+ instance: SchedulableInstance<A>
218
+ ) => (
219
+ start: DateTime.DateTime,
220
+ end: DateTime.DateTime
221
+ ) => (self: A) => boolean;
222
+ isScheduledOn: <A>(
223
+ instance: SchedulableInstance<A>
224
+ ) => (date: DateTime.DateTime) => (self: A) => boolean;
225
+ isScheduledToday: <A>(
226
+ instance: SchedulableInstance<A>
227
+ ) => (self: A) => boolean;
228
+ isScheduledThisWeek: <A>(
229
+ instance: SchedulableInstance<A>
230
+ ) => (self: A) => boolean;
231
+ isScheduledThisMonth: <A>(
232
+ instance: SchedulableInstance<A>
233
+ ) => (self: A) => boolean;
234
+ };
235
+
236
+ declare const Durable$: {
237
+ make: <A>(
238
+ get: (self: A) => Duration.Duration,
239
+ set: (self: A, duration: Duration.Duration) => A
240
+ ) => DurableInstance<A>;
241
+ isMoreThan: <A>(
242
+ instance: DurableInstance<A>
243
+ ) => (min: Duration.Duration) => (self: A) => boolean;
244
+ isLessThan: <A>(
245
+ instance: DurableInstance<A>
246
+ ) => (max: Duration.Duration) => (self: A) => boolean;
247
+ isBetween: <A>(
248
+ instance: DurableInstance<A>
249
+ ) => (
250
+ min: Duration.Duration,
251
+ max: Duration.Duration
252
+ ) => (self: A) => boolean;
253
+ hasExactDuration: <A>(
254
+ instance: DurableInstance<A>
255
+ ) => (duration: Duration.Duration) => (self: A) => boolean;
256
+ };
257
+
258
+ interface Appointment {
259
+ readonly date: DateTime.Utc;
260
+ readonly duration: Duration.Duration;
261
+ }
262
+
263
+ declare const Appointment: {
264
+ make: (props: Partial<Appointment>) => Appointment;
265
+ };
266
+
267
+ // Create typeclass instances
268
+ export const Schedulable = Schedulable$.make<Appointment>(
269
+ (self: Appointment) => self.date,
270
+ (self: Appointment, date: DateTime.DateTime) =>
271
+ Appointment.make({ ...self, date: DateTime.toUtc(date) })
272
+ );
273
+
274
+ export const Durable = Durable$.make<Appointment>(
275
+ (self: Appointment) => self.duration,
276
+ (self: Appointment, duration: Duration.Duration) =>
277
+ Appointment.make({ ...self, duration })
278
+ );
279
+
280
+ // Re-export all Schedulable predicates
281
+ export const isScheduledBefore = Schedulable$.isScheduledBefore(Schedulable);
282
+ export const isScheduledAfter = Schedulable$.isScheduledAfter(Schedulable);
283
+ export const isScheduledBetween = Schedulable$.isScheduledBetween(Schedulable);
284
+ export const isScheduledOn = Schedulable$.isScheduledOn(Schedulable);
285
+ export const isScheduledToday = Schedulable$.isScheduledToday(Schedulable);
286
+ export const isScheduledThisWeek =
287
+ Schedulable$.isScheduledThisWeek(Schedulable);
288
+ export const isScheduledThisMonth =
289
+ Schedulable$.isScheduledThisMonth(Schedulable);
290
+
291
+ // Re-export all Durable predicates
292
+ export const hasMinimumDuration = Durable$.isMoreThan(Durable);
293
+ export const hasMaximumDuration = Durable$.isLessThan(Durable);
294
+ export const hasDurationBetween = Durable$.isBetween(Durable);
295
+ export const hasExactDuration = Durable$.hasExactDuration(Durable);
296
+ ```
297
+
298
+ ## Pattern: Order Instances with Order.mapInput
299
+
300
+ Compose orders from simpler base orders using `Order.mapInput`:
301
+
302
+ ```typescript
303
+ import { DateTime } from 'effect';
304
+ import * as Order from 'effect/Order';
305
+ import * as String from 'effect/String';
306
+
307
+ interface Task {
308
+ readonly _tag: 'pending' | 'active' | 'completed';
309
+ readonly id: string;
310
+ readonly createdAt: DateTime.Utc;
311
+ }
312
+
313
+ /**
314
+ * Order by ID using Order.mapInput.
315
+ *
316
+ * @category Orders
317
+ * @since 0.1.0
318
+ * @example
319
+ * import * as Task from "@/schemas/Task"
320
+ * import * as Array from "effect/Array"
321
+ *
322
+ * const sorted = Array.sort(tasks, Task.OrderById)
323
+ */
324
+ export const OrderById: Order.Order<Task> = Order.mapInput(
325
+ Order.String,
326
+ (task: Task) => task.id
327
+ );
328
+
329
+ /**
330
+ * Order by creation date.
331
+ *
332
+ * @category Orders
333
+ * @since 0.1.0
334
+ */
335
+ export const OrderByCreatedAt: Order.Order<Task> = Order.mapInput(
336
+ DateTime.Order,
337
+ (task: Task) => task.createdAt
338
+ );
339
+
340
+ /**
341
+ * Order by status tag.
342
+ *
343
+ * @category Orders
344
+ * @since 0.1.0
345
+ */
346
+ export const OrderByTag: Order.Order<Task> = Order.mapInput(
347
+ Order.String,
348
+ (task: Task) => task._tag
349
+ );
350
+
351
+ /**
352
+ * Order by priority (domain-specific logic).
353
+ *
354
+ * @category Orders
355
+ * @since 0.1.0
356
+ */
357
+ export const OrderByPriority: Order.Order<Task> = Order.mapInput(
358
+ Order.Number,
359
+ (task: Task) => {
360
+ const priorities = { pending: 0, active: 1, completed: 2 };
361
+ return priorities[task._tag];
362
+ }
363
+ );
364
+ ```
365
+
366
+ **Key Pattern: Order.mapInput**
367
+
368
+ - Signature: `Order.mapInput(baseOrder, (value) => extractField)`
369
+ - Compose from existing orders (Order.String, Order.Number, DateTime.Order, etc.)
370
+ - Map domain type to comparable value
371
+ - Dual API: data-first and data-last
372
+
373
+ ## Pattern: Combining Orders with Order.combine
374
+
375
+ Use `Order.combine` for multi-criteria sorting:
376
+
377
+ ```typescript
378
+ import { DateTime } from 'effect';
379
+ import * as Order from 'effect/Order';
380
+
381
+ interface Task {
382
+ readonly _tag: 'pending' | 'active' | 'completed';
383
+ readonly id: string;
384
+ readonly createdAt: DateTime.Utc;
385
+ }
386
+
387
+ declare const OrderByPriority: Order.Order<Task>;
388
+ declare const OrderByCreatedAt: Order.Order<Task>;
389
+ declare const OrderByTag: Order.Order<Task>;
390
+ declare const OrderById: Order.Order<Task>;
391
+
392
+ /**
393
+ * Sort by priority first, then by creation date.
394
+ *
395
+ * @category Orders
396
+ * @since 0.1.0
397
+ * @example
398
+ * import * as Task from "@/schemas/Task"
399
+ * import * as Array from "effect/Array"
400
+ *
401
+ * // High priority tasks first, then by oldest
402
+ * const sorted = Array.sort(tasks, Task.OrderByPriorityThenDate)
403
+ */
404
+ export const OrderByPriorityThenDate: Order.Order<Task> = Order.combine(
405
+ OrderByPriority,
406
+ OrderByCreatedAt
407
+ );
408
+
409
+ /**
410
+ * Sort by tag, then ID, then creation date.
411
+ *
412
+ * @category Orders
413
+ * @since 0.1.0
414
+ */
415
+ export const OrderComplex: Order.Order<Task> = Order.combineAll([
416
+ OrderByTag,
417
+ OrderById,
418
+ OrderByCreatedAt
419
+ ]);
420
+ ```
421
+
422
+ **Key Pattern: Order.combine / combineAll**
423
+
424
+ - `Order.combine` combines two orders for multi-criteria sorting
425
+ - Use `Order.combineAll([...])` for three or more orders
426
+ - First order takes precedence, then second, etc.
427
+ - Order matters (unlike Equivalence.combine)
428
+ - Returns combined order that can be used with Array.sort
429
+
430
+ ## Pattern: Comprehensive Order Instances
431
+
432
+ Provide extensive sorting capabilities:
433
+
434
+ ```typescript
435
+ import { DateTime, Duration } from 'effect';
436
+ import * as Order from 'effect/Order';
437
+ import * as String from 'effect/String';
438
+
439
+ // Inline typeclass interface declarations
440
+ interface SchedulableInstance<A> {
441
+ readonly get: (self: A) => DateTime.DateTime;
442
+ readonly set: (self: A, date: DateTime.DateTime) => A;
443
+ }
444
+
445
+ interface DurableInstance<A> {
446
+ readonly get: (self: A) => Duration.Duration;
447
+ readonly set: (self: A, duration: Duration.Duration) => A;
448
+ }
449
+
450
+ // Typeclass module declarations
451
+ declare const Schedulable$: {
452
+ OrderByScheduledTime: <A>(
453
+ instance: SchedulableInstance<A>
454
+ ) => Order.Order<A>;
455
+ OrderByDayOfWeek: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
456
+ OrderByTimeOfDay: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
457
+ OrderByHour: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
458
+ OrderByMonth: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
459
+ OrderByYear: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
460
+ OrderByYearMonth: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
461
+ OrderByDateOnly: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
462
+ OrderByDayPeriod: <A>(instance: SchedulableInstance<A>) => Order.Order<A>;
463
+ OrderByBusinessHours: <A>(
464
+ instance: SchedulableInstance<A>
465
+ ) => Order.Order<A>;
466
+ OrderByWeekdayFirst: <A>(
467
+ instance: SchedulableInstance<A>
468
+ ) => Order.Order<A>;
469
+ };
470
+
471
+ declare const Durable$: {
472
+ OrderByDuration: <A>(instance: DurableInstance<A>) => Order.Order<A>;
473
+ OrderByHours: <A>(instance: DurableInstance<A>) => Order.Order<A>;
474
+ OrderByMinutes: <A>(instance: DurableInstance<A>) => Order.Order<A>;
475
+ OrderBySeconds: <A>(instance: DurableInstance<A>) => Order.Order<A>;
476
+ };
477
+
478
+ type AppointmentStatus = 'scheduled' | 'confirmed' | 'completed' | 'cancelled';
479
+
480
+ interface Appointment {
481
+ readonly date: DateTime.Utc;
482
+ readonly duration: Duration.Duration;
483
+ readonly status: AppointmentStatus;
484
+ }
485
+
486
+ declare const Schedulable: SchedulableInstance<Appointment>;
487
+ declare const Durable: DurableInstance<Appointment>;
488
+
489
+ // Schedulable orders (temporal sorting)
490
+ export const OrderByScheduledTime =
491
+ Schedulable$.OrderByScheduledTime(Schedulable);
492
+ export const OrderByDayOfWeek = Schedulable$.OrderByDayOfWeek(Schedulable);
493
+ export const OrderByTimeOfDay = Schedulable$.OrderByTimeOfDay(Schedulable);
494
+ export const OrderByHour = Schedulable$.OrderByHour(Schedulable);
495
+ export const OrderByMonth = Schedulable$.OrderByMonth(Schedulable);
496
+ export const OrderByYear = Schedulable$.OrderByYear(Schedulable);
497
+ export const OrderByYearMonth = Schedulable$.OrderByYearMonth(Schedulable);
498
+ export const OrderByDateOnly = Schedulable$.OrderByDateOnly(Schedulable);
499
+ export const OrderByDayPeriod = Schedulable$.OrderByDayPeriod(Schedulable);
500
+ export const OrderByBusinessHours =
501
+ Schedulable$.OrderByBusinessHours(Schedulable);
502
+ export const OrderByWeekdayFirst =
503
+ Schedulable$.OrderByWeekdayFirst(Schedulable);
504
+
505
+ // Durable orders (duration sorting)
506
+ export const OrderByDuration = Durable$.OrderByDuration(Durable);
507
+ export const OrderByHours = Durable$.OrderByHours(Durable);
508
+ export const OrderByMinutes = Durable$.OrderByMinutes(Durable);
509
+ export const OrderBySeconds = Durable$.OrderBySeconds(Durable);
510
+
511
+ // Domain-specific orders using Order.mapInput
512
+ export const OrderByStatus: Order.Order<Appointment> = Order.mapInput(
513
+ String.Order,
514
+ (appt: Appointment) => appt.status
515
+ );
516
+
517
+ export const OrderByStatusPriority: Order.Order<Appointment> = Order.mapInput(
518
+ Order.Number,
519
+ (appt: Appointment) => {
520
+ const priorities: Record<AppointmentStatus, number> = {
521
+ scheduled: 0,
522
+ confirmed: 1,
523
+ completed: 2,
524
+ cancelled: 3
525
+ };
526
+ return priorities[appt.status];
527
+ }
528
+ );
529
+
530
+ // Combined orders for complex sorting
531
+ export const OrderByStatusThenTime: Order.Order<Appointment> = Order.combine(
532
+ OrderByStatusPriority,
533
+ OrderByScheduledTime
534
+ );
535
+ ```
536
+
537
+ ## Usage Examples
538
+
539
+ ### Equality Examples
540
+
541
+ ```typescript
542
+ import { Equal, Array } from 'effect';
543
+ import * as Equivalence from 'effect/Equivalence';
544
+
545
+ declare module '@/schemas/Task' {
546
+ export interface Task {
547
+ readonly _tag: string;
548
+ readonly id: string;
549
+ }
550
+ export const EquivalenceById: Equivalence.Equivalence<Task>;
551
+ export const EquivalenceByTagAndId: Equivalence.Equivalence<Task>;
552
+ }
553
+
554
+ import * as Task from '@/schemas/Task';
555
+
556
+ declare const task1: Task.Task;
557
+ declare const task2: Task.Task;
558
+ declare const tasks: Array<Task.Task>;
559
+ declare const searchTask: Task.Task;
560
+
561
+ // Deep structural equality (automatic in v4)
562
+ const areSame = Equal.equals(task1, task2);
563
+
564
+ // Deduplicate by ID only
565
+ const uniqueById = Array.dedupeWith(tasks, Task.EquivalenceById);
566
+
567
+ // Deduplicate by tag and ID
568
+ const uniqueByTagAndId = Array.dedupeWith(tasks, Task.EquivalenceByTagAndId);
569
+
570
+ // Find if array contains equivalent task
571
+ const hasTask = Array.containsWith(tasks, Task.EquivalenceById)(searchTask);
572
+ ```
573
+
574
+ ### Filtering Examples
575
+
576
+ Document how these predicates enable powerful filtering:
577
+
578
+ ```typescript
579
+ import { DateTime, Duration, Array } from 'effect';
580
+ import { pipe } from 'effect/Function';
581
+
582
+ declare module '@/schemas/Appointment' {
583
+ export interface Appointment {
584
+ readonly date: DateTime.Utc;
585
+ }
586
+ export const isScheduledBefore: (
587
+ date: DateTime.DateTime
588
+ ) => (appointment: Appointment) => boolean;
589
+ }
590
+
591
+ import * as Appointment from '@/schemas/Appointment';
592
+
593
+ declare const appointments: Array<Appointment.Appointment>;
594
+
595
+ /**
596
+ * Filter appointments scheduled before a date.
597
+ *
598
+ * @example
599
+ * import * as Appointment from "@/schemas/Appointment"
600
+ * import * as DateTime from "effect/DateTime"
601
+ * import * as Duration from "effect/Duration"
602
+ * import * as Array from "effect/Array"
603
+ * import { pipe } from "effect/Function"
604
+ *
605
+ * const tomorrow = DateTime.addDuration(
606
+ * DateTime.unsafeNow(),
607
+ * Duration.days(1)
608
+ * )
609
+ *
610
+ * const beforeTomorrow = pipe(
611
+ * appointments,
612
+ * Array.filter(Appointment.isScheduledBefore(tomorrow))
613
+ * )
614
+ */
615
+ const tomorrow = DateTime.addDuration(DateTime.unsafeNow(), Duration.days(1));
616
+
617
+ const beforeTomorrow = pipe(
618
+ appointments,
619
+ Array.filter(Appointment.isScheduledBefore(tomorrow))
620
+ );
621
+ ```
622
+
623
+ ### Sorting Examples
624
+
625
+ ```typescript
626
+ import { Array, DateTime } from 'effect';
627
+ import * as Order from 'effect/Order';
628
+ import { pipe } from 'effect/Function';
629
+
630
+ declare module '@/schemas/Task' {
631
+ export interface Task {
632
+ readonly _tag: 'pending' | 'active' | 'completed';
633
+ readonly id: string;
634
+ readonly createdAt: DateTime.Utc;
635
+ }
636
+ export const OrderById: Order.Order<Task>;
637
+ export const OrderByPriority: Order.Order<Task>;
638
+ export const OrderByCreatedAt: Order.Order<Task>;
639
+ export const isPending: (task: Task) => boolean;
640
+ }
641
+
642
+ import * as Task from '@/schemas/Task';
643
+
644
+ declare const tasks: Array<Task.Task>;
645
+
646
+ // Simple sort by single field
647
+ const sortedById = Array.sort(tasks, Task.OrderById);
648
+
649
+ // Multi-criteria sort
650
+ const sortedComplex = Array.sort(
651
+ tasks,
652
+ Order.combine(Task.OrderByPriority, Task.OrderByCreatedAt)
653
+ );
654
+
655
+ // Sort with filter
656
+ const sortedFiltered = pipe(
657
+ tasks,
658
+ Array.filter(Task.isPending),
659
+ Array.sort(Task.OrderByCreatedAt)
660
+ );
661
+ ```
662
+
663
+ ## Pattern: Complex Filtering
664
+
665
+ Combine predicates for sophisticated queries:
666
+
667
+ ```typescript
668
+ import { DateTime, Duration, Array } from 'effect';
669
+ import { pipe } from 'effect/Function';
670
+ import * as Order from 'effect/Order';
671
+ import * as Equivalence from 'effect/Equivalence';
672
+
673
+ declare module '@/schemas/Appointment' {
674
+ export interface Appointment {
675
+ readonly id: string;
676
+ readonly date: DateTime.Utc;
677
+ readonly duration: Duration.Duration;
678
+ readonly status: string;
679
+ }
680
+ export const isScheduledThisWeek: (appointment: Appointment) => boolean;
681
+ export const hasMinimumDuration: (
682
+ min: Duration.Duration
683
+ ) => (appointment: Appointment) => boolean;
684
+ export const isScheduledToday: (appointment: Appointment) => boolean;
685
+ export const OrderByStatusPriority: Order.Order<Appointment>;
686
+ export const OrderByScheduledTime: Order.Order<Appointment>;
687
+ export const EquivalenceById: Equivalence.Equivalence<Appointment>;
688
+ export const OrderByPriorityThenDate: Order.Order<Appointment>;
689
+ }
690
+
691
+ import * as Appointment from '@/schemas/Appointment';
692
+
693
+ declare const appointments: Array<Appointment.Appointment>;
694
+
695
+ // Find long appointments this week
696
+ const longThisWeek = pipe(
697
+ appointments,
698
+ Array.filter(Appointment.isScheduledThisWeek),
699
+ Array.filter(Appointment.hasMinimumDuration(Duration.hours(2)))
700
+ );
701
+
702
+ // Sort by multiple criteria
703
+ const sorted = pipe(
704
+ appointments,
705
+ Array.filter(Appointment.isScheduledToday),
706
+ Array.sort(
707
+ Order.combine(
708
+ Appointment.OrderByStatusPriority,
709
+ Appointment.OrderByScheduledTime
710
+ )
711
+ )
712
+ );
713
+
714
+ // Deduplicate and sort
715
+ const uniqueSorted = pipe(
716
+ appointments,
717
+ Array.dedupeWith(Appointment.EquivalenceById),
718
+ Array.sort(Appointment.OrderByPriorityThenDate)
719
+ );
720
+ ```
721
+
722
+ ## Checklist for Complete Coverage
723
+
724
+ ### Equality
725
+
726
+ - [ ] `Equal.equals()` works via deep structural comparison (automatic in v4)
727
+ - [ ] Export `Schema.toEquivalence()` when needed for combinators
728
+ - [ ] Export field-based equivalences using `Equivalence.mapInput`
729
+ - [ ] Export combined equivalences using `Equivalence.combine`
730
+
731
+ ### Orders
732
+
733
+ - [ ] Export orders for all sortable fields using `Order.mapInput`
734
+ - [ ] Export combined orders using `Order.combine`
735
+ - [ ] Document which field takes precedence in combined orders
736
+
737
+ ### Schedulable types
738
+
739
+ - [ ] isScheduledBefore
740
+ - [ ] isScheduledAfter
741
+ - [ ] isScheduledBetween
742
+ - [ ] isScheduledOn
743
+ - [ ] isScheduledToday
744
+ - [ ] isScheduledThisWeek
745
+ - [ ] isScheduledThisMonth
746
+ - [ ] All Order instances
747
+
748
+ ### Durable types
749
+
750
+ - [ ] hasMinimumDuration (isMoreThan)
751
+ - [ ] hasMaximumDuration (isLessThan)
752
+ - [ ] hasDurationBetween (isBetween)
753
+ - [ ] hasExactDuration
754
+ - [ ] All Order instances
755
+
756
+ ### Domain-specific fields
757
+
758
+ - [ ] Predicate for each variant (isPending, isActive, etc.)
759
+ - [ ] Order by field value using `Order.mapInput`
760
+ - [ ] Order by priority/importance if applicable
761
+ - [ ] Combined orders for common sorting patterns
762
+
763
+ ## Documentation Requirements
764
+
765
+ Every predicate, equivalence, and order MUST have:
766
+
767
+ - JSDoc description
768
+ - @category tag
769
+ - @since tag
770
+ - @example with realistic usage showing imports and pipe
771
+
772
+ ## Key Patterns Summary
773
+
774
+ **1. Deep Structural Equality (v4)**
775
+
776
+ ```typescript
777
+ import { Schema, Equal } from 'effect';
778
+
779
+ const TaskSchema = Schema.TaggedStruct('task', { id: Schema.String });
780
+ type Task = Schema.Schema.Type<typeof TaskSchema>;
781
+
782
+ declare const t1: Task;
783
+ declare const t2: Task;
784
+
785
+ // Usage: Equal.equals(t1, t2) — deep structural comparison is automatic in v4
786
+ const areSame = Equal.equals(t1, t2);
787
+ ```
788
+
789
+ **2. Schema.toEquivalence() for Combinators**
790
+
791
+ ```typescript
792
+ import { Schema, Array } from 'effect';
793
+
794
+ declare const Task: Schema.Schema<any, any, never>;
795
+ type Task = Schema.Schema.Type<typeof Task>;
796
+
797
+ export const Equivalence = Schema.toEquivalence(Task);
798
+
799
+ declare const tasks: Array<Task>;
800
+
801
+ // Usage: Array.dedupeWith(tasks, Equivalence)
802
+ const uniqueTasks = Array.dedupeWith(tasks, Equivalence);
803
+ ```
804
+
805
+ **3. Equivalence.mapInput for Field-Based**
806
+
807
+ ```typescript
808
+ import * as Equivalence from 'effect/Equivalence';
809
+
810
+ interface Task {
811
+ readonly id: string;
812
+ }
813
+
814
+ const EquivalenceById = Equivalence.mapInput(
815
+ Equivalence.String,
816
+ (t: Task) => t.id
817
+ );
818
+ ```
819
+
820
+ **4. Equivalence.combine for Multi-Field**
821
+
822
+ ```typescript
823
+ import * as Equivalence from 'effect/Equivalence';
824
+
825
+ interface Task {
826
+ readonly _tag: string;
827
+ readonly id: string;
828
+ }
829
+
830
+ declare const EquivalenceByTag: Equivalence.Equivalence<Task>;
831
+ declare const EquivalenceById: Equivalence.Equivalence<Task>;
832
+
833
+ const EquivalenceCombined = Equivalence.combine(
834
+ EquivalenceByTag,
835
+ EquivalenceById
836
+ );
837
+ ```
838
+
839
+ **5. Order.mapInput for Field-Based Sorting**
840
+
841
+ ```typescript
842
+ import * as Order from 'effect/Order';
843
+
844
+ interface Task {
845
+ readonly id: string;
846
+ }
847
+
848
+ const OrderById = Order.mapInput(Order.String, (t: Task) => t.id);
849
+ ```
850
+
851
+ **6. Order.combine for Multi-Criteria Sorting**
852
+
853
+ ```typescript
854
+ import * as Order from 'effect/Order';
855
+
856
+ interface Task {
857
+ readonly priority: number;
858
+ readonly date: Date;
859
+ }
860
+
861
+ declare const OrderByPriority: Order.Order<Task>;
862
+ declare const OrderByDate: Order.Order<Task>;
863
+
864
+ const OrderCombined = Order.combine(OrderByPriority, OrderByDate);
865
+ ```
866
+
867
+ This ensures comprehensive equality checking, predicates, and sorting capabilities are discoverable and developers understand how to use them effectively with Effect's compositional patterns.