@zio.dev/zio-blocks 0.0.25 → 0.0.27

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/scope.md CHANGED
@@ -417,16 +417,52 @@ A `scoped { ... }` block can only return pure data (or `Nothing`). Resources and
417
417
 
418
418
  **Pragmatic safety.** The type-level tagging prevents *accidental* scope misuse in normal code, but it is not a security boundary. A determined developer can bypass it via `leak` (which emits a compiler warning), unsafe casts (`asInstanceOf`), or storing scoped references in mutable state (`var`).
419
419
 
420
- ### Closed-scope defense (runtime)
420
+ ### Closed-scope safety (runtime)
421
421
 
422
- If a scope escapes and is used after closing, operations become **no-ops returning default values**:
422
+ If a scope reference escapes its `scoped { }` block and an operation is attempted after closing, Scope throws `IllegalStateException` with a detailed, actionable error message:
423
423
 
424
- - `$`, `allocate`, `open`, `lower` return defaults (`null`, `0`, `false`, …)
425
- - `$` does not run the lambda when closed
426
- - `defer` on a closed scope is ignored
427
- - `scoped` creates a born-closed child if the parent is already closed
424
+ - **`allocate`** on a closed scope:
428
425
 
429
- This prevents post-close interaction with released resources, but can produce surprising default values if scopes are misused across threads.
426
+ ```
427
+ ── Scope Error ─────────────────────────────────────────────────────────────────
428
+
429
+ Cannot allocate resource: scope is already closed.
430
+
431
+ Scope: Scope.Child
432
+
433
+ What happened:
434
+ A call to allocate was made on a scope whose finalizers have
435
+ already run. The resource was never acquired.
436
+
437
+ Common causes:
438
+ • A scope reference escaped a scoped { } block (e.g. stored in a
439
+ field, captured in a Future or passed to another thread).
440
+ • close() was called on an OpenScope before all
441
+ allocations inside it completed.
442
+
443
+ Fix:
444
+ Call allocate only inside a live scoped { } block, or before
445
+ calling close() on an OpenScope.
446
+
447
+ // Correct usage:
448
+ Scope.global.scoped { scope =>
449
+ import scope.*
450
+ val db = allocate(Resource(new Database))
451
+ $(db)(_.query("SELECT 1"))
452
+ }
453
+
454
+ ────────────────────────────────────────────────────────────────────────────────
455
+ ```
456
+
457
+ - **`open()`** on a closed scope gives the same treatment, explaining that no child scope was created and directing the user to call `open()` only on a live scope.
458
+
459
+ - **`$`** on a closed scope explains that the resource may have already been released and accessing it would be undefined behaviour.
460
+
461
+ The following operations on a closed scope do **not** throw:
462
+
463
+ - `defer` — silently ignored (no-op)
464
+ - `scoped` — runs normally but creates a born-closed child scope
465
+ - `lower` — zero-cost cast, no closed check needed
430
466
 
431
467
  ### Thread ownership rule (JVM)
432
468
 
@@ -780,6 +816,112 @@ val appResource: Resource[App] =
780
816
 
781
817
  ---
782
818
 
819
+ ## Common runtime errors (and what they mean)
820
+
821
+ These `IllegalStateException`s are thrown when a scope operation is attempted on a closed scope. Each message identifies the scope type, explains what went wrong, lists common causes, and shows a correct usage example.
822
+
823
+ ### `allocate` on a closed scope
824
+
825
+ ```
826
+ ── Scope Error ─────────────────────────────────────────────────────────────────
827
+
828
+ Cannot allocate resource: scope is already closed.
829
+
830
+ Scope: Scope.Child
831
+
832
+ What happened:
833
+ A call to allocate was made on a scope whose finalizers have
834
+ already run. The resource was never acquired.
835
+
836
+ Common causes:
837
+ • A scope reference escaped a scoped { } block (e.g. stored in a
838
+ field, captured in a Future or passed to another thread).
839
+ • close() was called on an OpenScope before all
840
+ allocations inside it completed.
841
+
842
+ Fix:
843
+ Call allocate only inside a live scoped { } block, or before
844
+ calling close() on an OpenScope.
845
+
846
+ // Correct usage:
847
+ Scope.global.scoped { scope =>
848
+ import scope.*
849
+ val db = allocate(Resource(new Database))
850
+ $(db)(_.query("SELECT 1"))
851
+ }
852
+
853
+ ────────────────────────────────────────────────────────────────────────────────
854
+ ```
855
+
856
+ ### `open()` on a closed scope
857
+
858
+ ```
859
+ ── Scope Error ─────────────────────────────────────────────────────────────────
860
+
861
+ Cannot open child scope: scope is already closed.
862
+
863
+ Scope: Scope.Child
864
+
865
+ What happened:
866
+ A call to open() was made on a scope whose finalizers have
867
+ already run. No child scope was created.
868
+
869
+ Common causes:
870
+ • A scope reference escaped a scoped { } block and open()
871
+ was called after the block exited.
872
+ • close() was called on the parent OpenScope before
873
+ open() was called on it.
874
+
875
+ Fix:
876
+ Call open() only on a live (not yet closed) scope.
877
+
878
+ // Correct usage:
879
+ Scope.global.scoped { scope =>
880
+ import scope.*
881
+ val child = open()
882
+ $(child)(_.scope.allocate(Resource(new Database)))
883
+ }
884
+
885
+ ────────────────────────────────────────────────────────────────────────────────
886
+ ```
887
+
888
+ ### `$` on a closed scope
889
+
890
+ ```
891
+ ── Scope Error ─────────────────────────────────────────────────────────────────
892
+
893
+ Cannot access scoped value: scope is already closed.
894
+
895
+ Scope: Scope.Child
896
+
897
+ What happened:
898
+ The $ operator was called on a scope whose finalizers have
899
+ already run. The underlying resource may have been released.
900
+ Accessing it would be undefined behavior.
901
+
902
+ Common causes:
903
+ • A $[A] value or its owning scope escaped a scoped { }
904
+ block (e.g. captured in a Future, stored in a field, or
905
+ passed to another thread).
906
+ • close() was called on an OpenScope that still has
907
+ live $[A] values being accessed.
908
+
909
+ Fix:
910
+ Ensure all $ calls occur strictly within the scoped { }
911
+ block that owns the value, and that the scope has not been closed.
912
+
913
+ // Correct usage:
914
+ Scope.global.scoped { scope =>
915
+ import scope.*
916
+ val db = allocate(Resource(new Database))
917
+ $(db)(_.query("SELECT 1")) // $ used inside the block
918
+ }
919
+
920
+ ────────────────────────────────────────────────────────────────────────────────
921
+ ```
922
+
923
+ ---
924
+
783
925
  ## Common compile errors (and what they mean)
784
926
 
785
927
  This module produces two kinds of compile-time feedback:
@@ -1028,7 +1170,7 @@ Notes:
1028
1170
 
1029
1171
  - `$` requires a **lambda literal** and enforces safe receiver-only usage.
1030
1172
  - `$` returns `B` if `Unscoped[B]` exists; otherwise returns `$[B]`.
1031
- - If the scope is closed, `$` / `allocate` / `open` / `lower` return default values and perform no work.
1173
+ - If the scope is closed, `$`, `allocate`, and `open` throw `IllegalStateException` with a detailed error message. `defer` and `lower` are unaffected.
1032
1174
 
1033
1175
  Syntax enrichments available after `import scope.*` inside a scope:
1034
1176
 
package/sidebars.js CHANGED
@@ -21,6 +21,7 @@ const sidebars = {
21
21
  "reference/formats",
22
22
  "path-interpolator",
23
23
  "reference/chunk",
24
+ "reference/schema-error",
24
25
  "reference/validation",
25
26
  "reference/schema-evolution",
26
27
  "reference/context",
@@ -35,6 +36,7 @@ const sidebars = {
35
36
  type: "category",
36
37
  label: "Guides",
37
38
  items: [
39
+ "guides/zio-schema-migration",
38
40
  "guides/query-dsl-reified-optics",
39
41
  "guides/query-dsl-sql",
40
42
  "guides/query-dsl-extending",