@noego/forge 0.1.18 → 0.1.19

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.
@@ -20,9 +20,37 @@
20
20
  page?: any | null;
21
21
  }>();
22
22
 
23
+ // root is a writable store, so we need to subscribe to it
24
+ let rootValue = $state<any>(null);
25
+
26
+ // Subscribe to root store changes
27
+ $effect(() => {
28
+ if (root) {
29
+ const unsubscribe = root.subscribe((value: any) => {
30
+ rootValue = value;
31
+ });
32
+ return unsubscribe;
33
+ }
34
+ });
35
+
23
36
  // For recursive calls, root will be passed down
24
37
  // For top-level, root comes from hydrate
25
- let active = $derived(root?.active ?? { layouts: [], view: null, data: { layout: [], view: {} }, params: {}, urlParams: {}, query: {} });
38
+ // page is also a store, so we need to get its value
39
+ let pageValue = $state<any>(null);
40
+ $effect(() => {
41
+ if (rootValue?.page) {
42
+ const unsubscribe = rootValue.page.subscribe((value: any) => {
43
+ pageValue = value;
44
+ });
45
+ return unsubscribe;
46
+ }
47
+ });
48
+
49
+ let active = $derived(rootValue?.active ?? { layouts: [], view: null, viewPath: null, layoutPaths: [], data: { layout: [], view: {} }, params: {}, urlParams: {}, query: {} });
50
+
51
+ // Get debug paths from root.active for error reporting
52
+ let viewPath = $derived(active.viewPath);
53
+ let layoutPaths = $derived(active.layoutPaths ?? []);
26
54
 
27
55
  // Use props if provided (recursive call), otherwise use root.active (top-level)
28
56
  let layouts = $derived(propsLayouts ?? active.layouts ?? []);
@@ -31,7 +59,7 @@
31
59
  let params = $derived(propsParams ?? active.params ?? {});
32
60
  let urlParams = $derived(propsUrlParams ?? active.urlParams ?? {});
33
61
  let query = $derived(propsQuery ?? active.query ?? {});
34
- let page = $derived(propsPage ?? root?.page);
62
+ let page = $derived(propsPage ?? pageValue);
35
63
 
36
64
  // Layout-specific derivations
37
65
  let topLayout = $derived(layouts[0])
@@ -46,9 +74,14 @@
46
74
 
47
75
  // Debug logging
48
76
  $effect(() => {
49
- console.log('[RecursiveRender] Data received:', { data, view_data, viewName: view?.name });
77
+ console.log('[RecursiveRender] Data received:', { data, view_data, viewName: view?.name, viewPath });
50
78
  })
51
79
 
80
+ // Compute a stable key for the {#key} block that changes when navigation occurs
81
+ // This forces Svelte to destroy the old component tree before rendering the new one,
82
+ // preventing the race condition where old components receive new props
83
+ let navigationKey = $derived(viewPath || view?.name || 'default');
84
+
52
85
  // Error state managed outside boundary for navigation reset capability
53
86
  let errorState = $state<{ error: any; context: any } | null>(null);
54
87
  let resetFn = $state<(() => void) | null>(null);
@@ -75,7 +108,9 @@
75
108
  const snapshotAllData = safeClone(data);
76
109
 
77
110
  const context = {
78
- viewName: view?.name,
111
+ viewName: viewPath || view?.name || 'Unknown', // Prefer viewPath (actual file path) over HMR-wrapped name
112
+ viewPath: viewPath, // Actual file path like "pages/workflows/new/main.svelte"
113
+ layoutPaths: layoutPaths, // Layout file paths for debugging
79
114
  layoutCount: layouts.length,
80
115
  hasData: !!data,
81
116
  viewDataKeys: Object.keys(view_data || {}),
@@ -118,7 +153,10 @@
118
153
  <!-- Error UI rendered outside the boundary so navigation can clear it -->
119
154
  <div style="padding: 2rem; background: #fee; border: 2px solid #c00; border-radius: 8px; margin: 1rem;">
120
155
  <h2 style="color: #c00; margin-top: 0;">Error Rendering Page</h2>
121
- <p><strong>Component:</strong> {errorState.context.viewName || 'Unknown'}</p>
156
+ <p><strong>View:</strong> <code style="background: #fff; padding: 2px 6px; border-radius: 3px;">{errorState.context.viewPath || errorState.context.viewName || 'Unknown'}</code></p>
157
+ {#if errorState.context.layoutPaths?.length > 0}
158
+ <p><strong>Layouts:</strong> {errorState.context.layoutPaths.map((p: string) => p).join(' → ')}</p>
159
+ {/if}
122
160
  <p><strong>Error:</strong> {errorState.error?.message || String(errorState.error)}</p>
123
161
  <p style="font-size: 0.85em; color: #666;"><strong>Captured at:</strong> {errorState.context.timestamp}</p>
124
162
 
@@ -176,31 +214,55 @@
176
214
  </div>
177
215
  </div>
178
216
  {:else}
179
- <svelte:boundary onerror={handleError}>
180
- {#if childlayouts.length > 0}
181
- {#snippet nested_child()}
182
- <svelte:self
183
- root={root}
184
- layouts={childlayouts}
185
- view={view}
186
- data={recursive_child_data}
217
+ <!-- {#key} forces Svelte to destroy the entire component tree and recreate it when
218
+ navigationKey changes. This prevents the race condition where old components
219
+ receive props intended for new components during navigation. The key is based
220
+ on viewPath which changes on each navigation to a different page. -->
221
+ {#key navigationKey}
222
+ <svelte:boundary onerror={handleError}>
223
+ {#if childlayouts.length > 0}
224
+ {#snippet nested_child()}
225
+ <svelte:self
226
+ root={root}
227
+ layouts={childlayouts}
228
+ view={view}
229
+ data={recursive_child_data}
230
+ params={params}
231
+ urlParams={urlParams}
232
+ query={query}
233
+ page={page}
234
+ />
235
+ {/snippet}
236
+ <svelte:component
237
+ this={topLayout}
238
+ {...(top_data || {})}
239
+ params={params}
240
+ urlParams={urlParams}
241
+ query={query}
242
+ page={page}
243
+ children={nested_child}
244
+ />
245
+ {:else if topLayout}
246
+ {#snippet view_child()}
247
+ <svelte:component
248
+ this={view}
249
+ {...(view_data || {})}
250
+ params={params}
251
+ urlParams={urlParams}
252
+ query={query}
253
+ page={page}
254
+ />
255
+ {/snippet}
256
+ <svelte:component
257
+ this={topLayout}
258
+ {...(top_data || {})}
187
259
  params={params}
188
260
  urlParams={urlParams}
189
261
  query={query}
190
262
  page={page}
263
+ children={view_child}
191
264
  />
192
- {/snippet}
193
- <svelte:component
194
- this={topLayout}
195
- {...(top_data || {})}
196
- params={params}
197
- urlParams={urlParams}
198
- query={query}
199
- page={page}
200
- children={nested_child}
201
- />
202
- {:else if topLayout}
203
- {#snippet view_child()}
265
+ {:else}
204
266
  <svelte:component
205
267
  this={view}
206
268
  {...(view_data || {})}
@@ -209,26 +271,8 @@
209
271
  query={query}
210
272
  page={page}
211
273
  />
212
- {/snippet}
213
- <svelte:component
214
- this={topLayout}
215
- {...(top_data || {})}
216
- params={params}
217
- urlParams={urlParams}
218
- query={query}
219
- page={page}
220
- children={view_child}
221
- />
222
- {:else}
223
- <svelte:component
224
- this={view}
225
- {...(view_data || {})}
226
- params={params}
227
- urlParams={urlParams}
228
- query={query}
229
- page={page}
230
- />
231
- {/if}
232
- </svelte:boundary>
274
+ {/if}
275
+ </svelte:boundary>
276
+ {/key}
233
277
  {/if}
234
278
 
@@ -1,2 +0,0 @@
1
- "use strict";const T=require("svelte/internal/client");function A(o){const a=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(o){for(const s in o)if(s!=="default"){const e=Object.getOwnPropertyDescriptor(o,s);Object.defineProperty(a,s,e.get?e:{enumerable:!0,get:()=>o[s]})}}return a.default=o,Object.freeze(a)}const M=A(T);function U(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var k,b;function D(){if(b)return k;b=1,k=s,typeof window<"u"&&(window.pathToRegex=s);const o=/([$.+*?=!:[\]{}(|)/\\])/g;function a(e,t){return typeof e<"u"?e:t}function s(e,t){return this.init(e,t),this}return s.prototype.init=function(e="/",t={}){this.options={case:typeof t.case=="boolean"?t.case:!0,separators:typeof t.separators=="string"?t.separators:"/",fromStart:typeof t.fromStart=="boolean"?t.fromStart:!0,toEnd:typeof t.toEnd=="boolean"?t.toEnd:!0},this.options.separator="["+this.escape(this.options.separators)+"]",e instanceof RegExp?this.restructureRegExp(e):typeof e=="string"&&this.restructurePath(e)},s.prototype.restructureRegExp=function(e){e=a(e,/.*/),this.keys=[],this.path=void 0,this.regstr=""+e,this.regstr=this.regstr.substr(1,this.regstr.length-2),this.regexp=new RegExp(this.regstr,this.options.case?"":"i")},s.prototype.restructurePath=function(e){e=a(e,"/"),this.keys=[],this.path=e,this.regstr="";const t=this.options.separator,f="[^"+this.escape(this.options.separators)+"]";let i=0,n=0;if(e=e.replace(new RegExp("^"+t+"*(.*?)"+t+"*$"),"$1"),e.replace(/:([a-z]\w*)(\((.*?)\))?([\?\*\+])?/gi,(r,c,g,u,h,l,B)=>{n++;const w=h==="*"||h==="+",x=!!/^(\[[^\[\]]+\]|\([^\(\)]+\)|\.|\\.)[\+\*]$/.test(u);let E=h!=="*"&&h!=="?";!h&&u&&/^(\[[^\[\]]+\]|\([^\(\)]+\)|\.|\\.)[\*\?]?$/.test(u)&&(E=!1);const d=h||"",P=l?this.separator(e.charAt(l-1)):!0,S=l+r.length>=e.length?!0:this.separator(e.charAt(l+r.length)),R=P&&S;if(l>i){const j=e.substring(i,l),v=this.escape(j);this.regstr+=v}R&&l&&(!w||!E)&&u&&!x&&(this.regstr+="?");const p=u||f+"+",_=w?R?x?"((?:"+t+"?"+p+")"+d+")":"((?:"+t+p+")"+d+")":"((?:"+f+"*"+p+")"+d+")":R&&x?"("+p+"?)"+d:"("+p+")"+d;this.regstr+=_;const m={key:c,multiple:w,required:E,index:n,pattern:p};return w&&(m.regexp=new RegExp(p,this.options.case?"g":"gi")),this.keys.push(m),i=l+r.length,r}),i<e.length-1){const r=e.substring(i),c=this.escape(r);this.regstr+=c}this.regexp=new RegExp((this.options.fromStart?"^":"")+t+"?"+this.regstr+(this.options.toEnd?t+"?$":"("+t+"|"+t+"?$)"),this.options.case?"":"i")},s.prototype.escape=function(e){return e.replace(o,t=>"\\"+t)},s.prototype.separator=function(e){return!!(this.options.separators.indexOf(e)+1)},s.prototype.match=function(e){if(typeof e!="string")return;const t=this.options.separator,f=this.options.separators[0];e=e.replace(new RegExp("^"+t+"*(.*?)"+t+"*$"),f+"$1"+f);const i=e.match(this.regexp);if(!i)return;const n={};return this.keys.forEach(r=>{let c=!1;n[r.key]&&(c=!0),n[r.key]&&!Array.isArray(n[r.key])&&(c=!0,n[r.key]=[n[r.key]]),r.multiple&&!n[r.key]&&(c=!0,n[r.key]=[]);let g=i[r.index]?i[r.index]:void 0;if(!c&&!r.multiple){n[r.key]=g;return}if(c&&!r.multiple&&i[r.index]){n[r.key].push(g);return}i[r.index]&&i[r.index].replace(r.regexp,u=>{u&&n[r.key].push(u.replace(new RegExp(t+"*$"),""))})}),n},k}var q=D();const O=U(q);function z(o){let a="",s=0;for(;s<o.length;)if(o[s]==="{"){let e=s;s++;let t=1;for(;s<o.length&&t>0;)o[s]==="\\"?s+=2:(o[s]==="{"?t++:o[s]==="}"&&t--,s++);if(t!==0)throw new Error(`Unmatched '{' in path: ${o}`);const f=o.slice(e+1,s-1),i=f.indexOf(":");let n,r,c;if(i===-1){const g=f.match(/^([^?*+]+)([?*+])?$/);if(!g)throw new Error(`Invalid parameter segment: {${f}}`);[,n,c]=g}else n=f.slice(0,i),r=f.slice(i+1);a+=`:${n}`,r&&(a+=`(${r})`),c&&(a+=c)}else a+=o[s++];return a}let y=M.proxy({url:"",pathname:"",params:{},query:{}});typeof window<"u"&&(y.url=window.location.href,y.pathname=window.location.pathname,Object.assign(y.query,Object.fromEntries(new URLSearchParams(window.location.search))),y.params={});const $={};function I(o,a){$[o]||($[o]=new O(z(o)));const s=$[o].match(a);return s||{}}exports.page=y;exports.path_to_regex=O;exports.shadowUrl=I;
2
- //# sourceMappingURL=page.svelte-Dvj7306U.cjs.map