@picked/svelte 0.0.1-dev.1780458499348 → 0.0.1-dev.1780552550363

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.
@@ -40,7 +40,10 @@
40
40
  // content arrives pre-registered via the snapshot.
41
41
  $effect(() => {
42
42
  if ($ready) {
43
- void experience.prepareExperienceContentFx()
43
+ // Content fetch failures (e.g. a 404 when the content endpoint is unavailable) are already
44
+ // swallowed inside the effect; the explicit catch guarantees no unhandled rejection even if
45
+ // that contract changes, so a missing/erroring content endpoint never breaks rendering.
46
+ experience.prepareExperienceContentFx().catch(() => undefined)
44
47
  }
45
48
  })
46
49
 
@@ -7,7 +7,7 @@
7
7
  * re-renders reactively.
8
8
  */
9
9
  import type { ExperienceStore, ExperienceTarget } from '@picked/client'
10
- import { evaluateConditionMemo, hashRule } from '@picked/client/server'
10
+ import { evaluateConditionMemo, hashRule, ruleReferencesAction } from '@picked/client/server'
11
11
  import { getPicked, useUnit } from '../hooks'
12
12
  import ExperienceHost from './ExperienceHost.svelte'
13
13
 
@@ -28,7 +28,13 @@
28
28
  const client = getPicked()
29
29
  const selector = $derived(`.picked-slot-${name}`)
30
30
 
31
- const PLACEMENT_ORDER: ExperienceTarget['placement'][] = ['before', 'prepend', 'append', 'after']
31
+ const PLACEMENT_ORDER: ExperienceTarget['placement'][] = [
32
+ 'replace',
33
+ 'before',
34
+ 'prepend',
35
+ 'append',
36
+ 'after',
37
+ ]
32
38
 
33
39
  interface Candidate {
34
40
  experience: ExperienceStore
@@ -54,10 +60,11 @@
54
60
  }
55
61
 
56
62
  /**
57
- * Maps an experience to a slot candidate, or null when it does not target this slot.
63
+ * Maps an experience to a slot candidate, or null when it does not target this slot or carries
64
+ * an action-referencing condition.
58
65
  *
59
66
  * @param {ExperienceStore} experience - The experience to inspect.
60
- * @returns {Candidate | null} The candidate, or null when the experience does not target this slot.
67
+ * @returns {Candidate | null} The candidate, or null when the experience is not an eligible slot match.
61
68
  * @example
62
69
  * toCandidate(experienceStore)
63
70
  */
@@ -70,6 +77,14 @@
70
77
  return null
71
78
  }
72
79
 
80
+ // Action-referencing conditions are never cached (their side effect must re-run), so they can
81
+ // never resolve through the synchronous `peek` the slot path relies on. Slots do not support
82
+ // action-gated conditions in v1; excluding them also stops the selection effect from re-firing
83
+ // the action on every render.
84
+ if (ruleReferencesAction(config.decisionLogic)) {
85
+ return null
86
+ }
87
+
73
88
  return {
74
89
  experience,
75
90
  placement: target.placement,
@@ -87,13 +102,16 @@
87
102
 
88
103
  // Referencing `$conditionResults` keeps this recompute reactive to late condition writes
89
104
  // (the standalone cache-miss path), so a `peek` that returned `undefined` re-renders once
90
- // the async evaluation resolves and writes the store.
91
- const active = $derived(
92
- candidates.filter((candidate) => {
93
- void $conditionResults
94
- return client.conditionResults.peek(candidate.hash) === true
95
- }),
96
- )
105
+ // the async evaluation resolves and writes the store. An active `replace` experience wins:
106
+ // the first one (it sorts first) renders alone, replacing any other slot matches.
107
+ const active = $derived.by(() => {
108
+ void $conditionResults
109
+ const activeCandidates = candidates.filter(
110
+ (candidate) => client.conditionResults.peek(candidate.hash) === true,
111
+ )
112
+ const firstReplace = activeCandidates.find((candidate) => candidate.placement === 'replace')
113
+ return firstReplace ? [firstReplace] : activeCandidates
114
+ })
97
115
 
98
116
  /**
99
117
  * Builds a plain, JSON-serialisable subset of the current URL for the evaluation context.
@@ -2,6 +2,7 @@ import { CreateClientOptions, Picked } from '@picked/client';
2
2
  import { PickedSnapshot } from '@picked/client/server';
3
3
  type SetPickedContextParams = {
4
4
  client: Picked;
5
+ snapshot?: PickedSnapshot;
5
6
  } | {
6
7
  options: CreateClientOptions;
7
8
  snapshot?: PickedSnapshot;
@@ -12,16 +13,17 @@ type SetPickedContextParams = {
12
13
  * Call this once at the top of a component's `<script>` so `setContext` and `onMount` run during
13
14
  * component initialisation. Pass `options` to create the client and synchronously hydrate it from an
14
15
  * optional SSR snapshot (a `versionId` mismatch is tolerated; the client reconciles via `start()`),
15
- * or pass an existing `client` (such as the app's already-running global client) to render
16
- * experiences against it. In the browser the client is started from `onMount`; `start()` is
16
+ * or pass an existing `client` (such as the app's single global client) to render experiences
17
+ * against it — supply `snapshot` alongside `client` to SSR-seed that shared client rather than
18
+ * creating a second one. In the browser the client is started from `onMount`; `start()` is
17
19
  * idempotent, so a client that was already started is left untouched.
18
20
  *
19
- * @param {SetPickedContextParams} params - Either `{ client }` or `{ options, snapshot? }`.
21
+ * @param {SetPickedContextParams} params - `{ client, snapshot? }` or `{ options, snapshot? }`.
20
22
  * @returns {Picked} The client placed into context.
21
23
  * @example
22
24
  * setPickedContext({ options })
23
25
  * @example
24
- * setPickedContext({ client: globalClient })
26
+ * setPickedContext({ client: globalClient, snapshot })
25
27
  */
26
28
  export declare function setPickedContext(params: SetPickedContextParams): Picked;
27
29
  export {};
@@ -1 +1 @@
1
- import{CLIENT_CONTEXT_KEY as e}from"../context.js";import{onMount as t,setContext as n}from"svelte";import{createClient as r,supportsWindow as i}from"@picked/client";import{registerProjectConfig as a}from"@picked/client/server";function o(e,t){let n=r(e);if(t){let e=n.session.store.getState().id;a(n,t.projectConfig,{isPreview:!1,sessionId:e}),t.content.forEach(e=>n.registerContent(e)),n.conditionResults.seed(t.conditionResults)}return n}function s(r){let a=`client`in r?r.client:o(r.options,r.snapshot);return n(e,a),t(()=>{i()&&a.start()}),a}export{s as setPickedContext};
1
+ import{CLIENT_CONTEXT_KEY as e}from"../context.js";import{onMount as t,setContext as n}from"svelte";import{createClient as r,supportsWindow as i}from"@picked/client";import{registerProjectConfig as a}from"@picked/client/server";function o(e,t){let n=e.session.store.getState().id;a(e,t.projectConfig,{isPreview:!1,sessionId:n}),t.content.forEach(t=>e.registerContent(t)),e.conditionResults.seed(t.conditionResults)}function s(e,t){let n=r(e);return t&&o(n,t),n}function c(r){let a;return`client`in r?(r.snapshot&&o(r.client,r.snapshot),a=r.client):a=s(r.options,r.snapshot),n(e,a),t(()=>{i()&&a.start()}),a}export{c as setPickedContext};
package/dist/index.d.ts CHANGED
@@ -5,4 +5,4 @@ export { adaptSvelteComponent, isValidAdaptedComponent } from './component';
5
5
  export type { ComponentWithProps } from './component';
6
6
  export { LazyImage } from './components';
7
7
  export { setPickedContext } from './experience/set-picked-context';
8
- export { default as Experience } from './experience/Experience.svelte';
8
+ export { default as ExperienceSlot } from './experience/ExperienceSlot.svelte';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{adaptSvelteComponent as e,isValidAdaptedComponent as t}from"./component.js";import{LazyImage as n}from"./components/index.js";import{setPickedContext as r}from"./experience/set-picked-context.js";import i from"./experience/Experience.svelte";export{i as Experience,n as LazyImage,e as adaptSvelteComponent,t as isValidAdaptedComponent,r as setPickedContext};
1
+ import{adaptSvelteComponent as e,isValidAdaptedComponent as t}from"./component.js";import{LazyImage as n}from"./components/index.js";import{setPickedContext as r}from"./experience/set-picked-context.js";import i from"./experience/ExperienceSlot.svelte";export{i as ExperienceSlot,n as LazyImage,e as adaptSvelteComponent,t as isValidAdaptedComponent,r as setPickedContext};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picked/svelte",
3
- "version": "0.0.1-dev.1780458499348",
3
+ "version": "0.0.1-dev.1780552550363",
4
4
  "dependencies": {
5
5
  "d3": "^7.9.0"
6
6
  },
@@ -106,7 +106,7 @@
106
106
  },
107
107
  "module": "./dist/index.js",
108
108
  "peerDependencies": {
109
- "@picked/client": "0.0.1-dev.1780458499348",
109
+ "@picked/client": "0.0.1-dev.1780552550363",
110
110
  "effector": "^23.3.0",
111
111
  "svelte": "^5.46.4"
112
112
  },