Skip to content

Commit 2e233c7

Browse files
committed
bug: A hand patch for three content errors.
1 parent 112f9fa commit 2e233c7

7 files changed

Lines changed: 210 additions & 46 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ How to test Effect code effectively, reliably, and deterministically.
267267

268268
| Pattern | Skill Level | Summary |
269269
| :--- | :--- | :--- |
270-
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/acquire-release-bracket.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
270+
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/safely-bracket-resource-usage.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
271271

272272
---
273273

@@ -284,15 +284,15 @@ How to test Effect code effectively, reliably, and deterministically.
284284

285285
| Pattern | Skill Level | Summary |
286286
| :--- | :--- | :--- |
287-
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/acquire-release-bracket.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
287+
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/safely-bracket-resource-usage.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
288288

289289
---
290290

291291
## Network Requests
292292

293293
| Pattern | Skill Level | Summary |
294294
| :--- | :--- | :--- |
295-
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/acquire-release-bracket.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
295+
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/safely-bracket-resource-usage.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
296296

297297
---
298298

@@ -309,7 +309,7 @@ How to test Effect code effectively, reliably, and deterministically.
309309

310310
| Pattern | Skill Level | Summary |
311311
| :--- | :--- | :--- |
312-
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/acquire-release-bracket.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
312+
| [Safely Bracket Resource Usage with `acquireRelease`](./content/published/safely-bracket-resource-usage.mdx) | 🟢 **Beginner** | Use `Effect.acquireRelease` to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
313313
| [Compose Resource Lifecycles with `Layer.merge`](./content/published/compose-scoped-layers.mdx) | 🟡 **Intermediate** | Combine multiple resource-managing layers, letting Effect automatically handle the acquisition and release order. |
314314
| [Create a Service Layer from a Managed Resource](./content/published/scoped-service-layer.mdx) | 🟡 **Intermediate** | Use `Layer.scoped` with `Effect.Service` to transform a managed resource into a shareable, application-wide service. |
315315
| [Create a Managed Runtime for Scoped Resources](./content/published/create-managed-runtime-for-scoped-resources.mdx) | 🟠 **Advanced** | Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run. |

content/published/scoped-service-layer.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ author: "PaulJPhilp"
1515

1616
## Guideline
1717

18-
Define a service using `class MyService extends Effect.Service(...)`. On this class, create a static `Live` property using `Layer.scoped`. This layer should be constructed with the service class itself and a scoped `Effect` (typically from `Effect.acquireRelease`) that builds and releases the underlying resource.
18+
Define a service using `class MyService extends Effect.Service(...)`. Implement the service using the `scoped` property of the service class. This property should be a scoped `Effect` (typically from `Effect.acquireRelease`) that builds and releases the underlying resource.
1919

2020
## Rationale
2121

@@ -34,7 +34,7 @@ interface DbOps {
3434
class Database extends Effect.Service<DbOps>()(
3535
"Database",
3636
{
37-
effect: Effect.gen(function* () {
37+
scoped: Effect.gen(function* () {
3838
const id = Math.floor(Math.random() * 1000);
3939
yield* Console.log(`[Pool ${id}] Acquired`);
4040
return {

content/published/stream-from-file.mdx

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,35 @@ This example demonstrates reading a text file, splitting it into individual line
4444
```typescript
4545
import { FileSystem } from '@effect/platform';
4646
import { NodeFileSystem } from '@effect/platform-node';
47-
import { Effect } from 'effect';
47+
import type { PlatformError } from '@effect/platform/Error';
48+
import { Effect, Stream } from 'effect';
4849
import * as path from 'node:path';
4950

50-
const processFile = (filePath: string, content: string): Effect.Effect<void, Error, FileSystem.FileSystem> =>
51+
const processFile = (
52+
filePath: string,
53+
content: string
54+
): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>
5155
Effect.gen(function* () {
5256
const fs = yield* FileSystem.FileSystem;
53-
try {
54-
yield* fs.writeFileString(filePath, content);
55-
const fileContent = yield* fs.readFileString(filePath);
56-
const lines = fileContent.split('\n');
57-
58-
for (const line of lines) {
59-
yield* Effect.log(`Processing: ${line}`);
60-
}
61-
} finally {
62-
yield* fs.remove(filePath);
63-
}
57+
58+
// Write content to file
59+
yield* fs.writeFileString(filePath, content);
60+
61+
// Create a stream from file content
62+
const fileStream = Stream.fromEffect(fs.readFileString(filePath))
63+
.pipe(
64+
// Split content into lines
65+
Stream.map((content: string) => content.split('\n')),
66+
Stream.flatMap(Stream.fromIterable),
67+
// Process each line
68+
Stream.tap((line) => Effect.log(`Processing: ${line}`))
69+
);
70+
71+
// Run the stream to completion
72+
yield* Stream.runDrain(fileStream);
73+
74+
// Clean up file
75+
yield* fs.remove(filePath);
6476
});
6577

6678
const program = Effect.gen(function* () {
@@ -69,15 +81,17 @@ const program = Effect.gen(function* () {
6981
yield* processFile(
7082
filePath,
7183
'line 1\nline 2\nline 3'
84+
).pipe(
85+
Effect.catchAll((error: PlatformError) =>
86+
Effect.logError(`Error processing file: ${error.message}`)
87+
)
7288
);
73-
}).pipe(
74-
Effect.catchAll((error) =>
75-
Effect.logError(`Error processing file: ${String(error)}`)
76-
)
77-
);
89+
});
7890

7991
Effect.runPromise(
80-
program.pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect<undefined, never, never>
92+
program.pipe(
93+
Effect.provide(NodeFileSystem.layer)
94+
)
8195
).catch(console.error);
8296
/*
8397
Output:

content/raw/scoped-service-layer.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ author: "PaulJPhilp"
1515

1616
## Guideline
1717

18-
Define a service using `class MyService extends Effect.Service(...)`. On this class, create a static `Live` property using `Layer.scoped`. This layer should be constructed with the service class itself and a scoped `Effect` (typically from `Effect.acquireRelease`) that builds and releases the underlying resource.
18+
Define a service using `class MyService extends Effect.Service(...)`. Implement the service using the `scoped` property of the service class. This property should be a scoped `Effect` (typically from `Effect.acquireRelease`) that builds and releases the underlying resource.
1919

2020
## Rationale
2121

content/src/scoped-service-layer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ interface DbOps {
88
class Database extends Effect.Service<DbOps>()(
99
"Database",
1010
{
11-
effect: Effect.gen(function* () {
11+
scoped: Effect.gen(function* () {
1212
const id = Math.floor(Math.random() * 1000);
1313
yield* Console.log(`[Pool ${id}] Acquired`);
1414
return {

content/src/stream-from-file.ts

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
11
import { FileSystem } from '@effect/platform';
22
import { NodeFileSystem } from '@effect/platform-node';
3-
import { Effect } from 'effect';
3+
import type { PlatformError } from '@effect/platform/Error';
4+
import { Effect, Stream } from 'effect';
45
import * as path from 'node:path';
56

6-
const processFile = (filePath: string, content: string): Effect.Effect<void, Error, FileSystem.FileSystem> =>
7+
const processFile = (
8+
filePath: string,
9+
content: string
10+
): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>
711
Effect.gen(function* () {
812
const fs = yield* FileSystem.FileSystem;
9-
try {
10-
yield* fs.writeFileString(filePath, content);
11-
const fileContent = yield* fs.readFileString(filePath);
12-
const lines = fileContent.split('\n');
13-
14-
for (const line of lines) {
15-
yield* Effect.log(`Processing: ${line}`);
16-
}
17-
} finally {
18-
yield* fs.remove(filePath);
19-
}
13+
14+
// Write content to file
15+
yield* fs.writeFileString(filePath, content);
16+
17+
// Create a stream from file content
18+
const fileStream = Stream.fromEffect(fs.readFileString(filePath))
19+
.pipe(
20+
// Split content into lines
21+
Stream.map((content: string) => content.split('\n')),
22+
Stream.flatMap(Stream.fromIterable),
23+
// Process each line
24+
Stream.tap((line) => Effect.log(`Processing: ${line}`))
25+
);
26+
27+
// Run the stream to completion
28+
yield* Stream.runDrain(fileStream);
29+
30+
// Clean up file
31+
yield* fs.remove(filePath);
2032
});
2133

2234
const program = Effect.gen(function* () {
@@ -25,15 +37,17 @@ const program = Effect.gen(function* () {
2537
yield* processFile(
2638
filePath,
2739
'line 1\nline 2\nline 3'
40+
).pipe(
41+
Effect.catchAll((error: PlatformError) =>
42+
Effect.logError(`Error processing file: ${error.message}`)
43+
)
2844
);
29-
}).pipe(
30-
Effect.catchAll((error) =>
31-
Effect.logError(`Error processing file: ${String(error)}`)
32-
)
33-
);
45+
});
3446

3547
Effect.runPromise(
36-
program.pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect<undefined, never, never>
48+
program.pipe(
49+
Effect.provide(NodeFileSystem.layer)
50+
)
3751
).catch(console.error);
3852
/*
3953
Output:

scripts/README.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Effect Patterns Publishing Pipeline
2+
3+
This directory contains the scripts that power the Effect Patterns publishing pipeline. These scripts help maintain consistency between TypeScript source files and their corresponding MDX documentation.
4+
5+
## Pipeline Overview
6+
7+
The publishing pipeline consists of a 3-stage workflow:
8+
9+
1. **New Patterns** (`content/new/`): Empty subdirectories for new patterns that haven't been published yet
10+
2. **Raw Patterns** (`content/raw/`): MDX files with TypeScript code replaced by `<Example />` components
11+
3. **Published Patterns** (`content/published/`): Final MDX files with embedded TypeScript code blocks
12+
13+
## Available Scripts
14+
15+
### 1. `process_patterns.ts`
16+
17+
Processes MDX files by extracting TypeScript code blocks and replacing them with Example components.
18+
19+
```bash
20+
npx tsx process_patterns.ts --indir <input_directory> --outdir <output_directory> [--srcdir <source_directory>] [--count <number>]
21+
```
22+
23+
**Arguments:**
24+
- `--indir`: Input directory containing MDX files (required)
25+
- `--outdir`: Output directory for processed MDX files (required)
26+
- `--srcdir`: Directory to write extracted TypeScript code (optional)
27+
- `--count`: Number of files to process (optional)
28+
29+
**Example:**
30+
```bash
31+
npx tsx process_patterns.ts --indir content/published --outdir content/raw --srcdir content/src
32+
```
33+
34+
### 2. `publish-patterns.ts`
35+
36+
Processes MDX files by replacing Example components with TypeScript code from source files.
37+
38+
```bash
39+
npx tsx publish-patterns.ts --indir <input_directory> --outdir <output_directory> --srcdir <source_directory> [--count <number>]
40+
```
41+
42+
**Arguments:**
43+
- `--indir`: Input directory containing MDX files with Example components (required)
44+
- `--outdir`: Output directory for processed MDX files (required)
45+
- `--srcdir`: Directory containing TypeScript source files (required)
46+
- `--count`: Number of files to process (optional)
47+
48+
**Example:**
49+
```bash
50+
npx tsx publish-patterns.ts --indir content/raw --outdir content/published --srcdir content/src
51+
```
52+
53+
### 3. `pattern-validator.ts`
54+
55+
Validates that TypeScript code blocks in MDX files match their corresponding source files.
56+
57+
```bash
58+
npx tsx pattern-validator.ts --indir <input_directory> --srcdir <source_directory> [--count <number>]
59+
```
60+
61+
**Arguments:**
62+
- `--indir`: Input directory containing MDX files (required)
63+
- `--srcdir`: Directory containing TypeScript source files (required)
64+
- `--count`: Number of files to process (optional)
65+
66+
**Example:**
67+
```bash
68+
npx tsx pattern-validator.ts --indir content/published --srcdir content/src
69+
```
70+
71+
### 4. `generate_readme.ts`
72+
73+
Generates the main README.md file with links to all patterns, organized by category.
74+
75+
```bash
76+
npx tsx generate_readme.ts
77+
```
78+
79+
**Note:** This script looks for MDX files in the `content/published` directory.
80+
81+
### 5. `validate_and_generate.ts`
82+
83+
Combines validation and README generation in one step.
84+
85+
```bash
86+
npx tsx validate_and_generate.ts
87+
```
88+
89+
This script:
90+
1. Validates all TypeScript code blocks against source files
91+
2. Generates the README.md with links to all patterns
92+
3. Ensures complete consistency between code, documentation, and README
93+
94+
## Typical Workflow
95+
96+
1. **Create new patterns** in `content/new/`
97+
2. **Process patterns** to extract TypeScript code:
98+
```bash
99+
npx tsx process_patterns.ts --indir content/published --outdir content/raw --srcdir content/src
100+
```
101+
3. **Make changes** to TypeScript source files in `content/src/`
102+
4. **Publish patterns** to restore TypeScript code blocks:
103+
```bash
104+
npx tsx publish-patterns.ts --indir content/raw --outdir content/published --srcdir content/src
105+
```
106+
5. **Validate patterns** to ensure consistency:
107+
```bash
108+
npx tsx pattern-validator.ts --indir content/published --srcdir content/src
109+
```
110+
6. **Generate README** with links to all patterns:
111+
```bash
112+
npx tsx generate_readme.ts
113+
```
114+
115+
Or use the combined validation and generation script:
116+
```bash
117+
npx tsx validate_and_generate.ts
118+
```
119+
120+
## Future Automation
121+
122+
Future enhancements to this pipeline could include:
123+
124+
1. GitHub Actions workflow to automatically validate patterns on pull requests
125+
2. Automated tests for the pipeline scripts
126+
3. Integration with a CI/CD system for automated deployment
127+
4. Pre-commit hooks to ensure consistency before committing changes
128+
129+
## Dependencies
130+
131+
- TypeScript
132+
- Node.js
133+
- commander (for CLI argument parsing)
134+
- gray-matter (for parsing MDX frontmatter)
135+
- fs/promises (for file system operations)
136+
- path (for path manipulation)

0 commit comments

Comments
 (0)