sygnal 5.3.4 → 5.3.5

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/dist/index.d.ts CHANGED
@@ -42,11 +42,13 @@ type NextFunction<ACTIONS = any> = ACTIONS extends object
42
42
  ) => void
43
43
  : (action: string, data?: any, delay?: number) => void
44
44
 
45
- type Reducer<STATE, PROPS, ACTIONS = any, DATA = any, RETURN = any> = (
45
+ type ReducerExtras<PROPS, CONTEXT> = PROPS & { context: CONTEXT; children?: JSX.Element | JSX.Element[]; slots?: Record<string, JSX.Element[]> }
46
+
47
+ type Reducer<STATE, PROPS, ACTIONS = any, DATA = any, RETURN = any, CONTEXT = {}> = (
46
48
  state: STATE,
47
49
  args: DATA,
48
50
  next: NextFunction<ACTIONS>,
49
- props: PROPS
51
+ props: ReducerExtras<PROPS, CONTEXT>
50
52
  ) => RETURN | ABORT | undefined
51
53
 
52
54
  export type ExactShape<EXPECTED, ACTUAL extends EXPECTED> = ACTUAL &
@@ -76,24 +78,24 @@ type ResolvedNonStateSinkReturns<SINK_RETURNS extends NonStateSinkReturns = {}>
76
78
  * - true: Whatever value is received from the intent for this action is passed on as-is.
77
79
  * - Function: A reducer
78
80
  */
79
- type SinkValue<STATE, PROPS, ACTIONS, DATA, RETURN, CALCULATED> =
81
+ type SinkValue<STATE, PROPS, ACTIONS, DATA, RETURN, CALCULATED, CONTEXT = {}> =
80
82
  | true
81
- | Reducer<STATE & CALCULATED, PROPS, ACTIONS, DATA, RETURN>
83
+ | Reducer<STATE & CALCULATED, PROPS, ACTIONS, DATA, RETURN, CONTEXT>
82
84
 
83
- type EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED> =
84
- | ((state: STATE & CALCULATED, args: DATA, next: NextFunction<ACTIONS>, props: PROPS) => void)
85
+ type EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED, CONTEXT = {}> =
86
+ | ((state: STATE & CALCULATED, args: DATA, next: NextFunction<ACTIONS>, props: ReducerExtras<PROPS, CONTEXT>) => void)
85
87
 
86
- type DefaultSinks<STATE, PROPS, ACTIONS, DATA, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}> = {
87
- STATE?: SinkValue<STATE, PROPS, ACTIONS, DATA, STATE, CALCULATED>;
88
- EVENTS?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['EVENTS'], CALCULATED>;
89
- LOG?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['LOG'], CALCULATED>;
90
- PARENT?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['PARENT'], CALCULATED>;
91
- EFFECT?: EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED>;
88
+ type DefaultSinks<STATE, PROPS, ACTIONS, DATA, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}, CONTEXT = {}> = {
89
+ STATE?: SinkValue<STATE, PROPS, ACTIONS, DATA, STATE, CALCULATED, CONTEXT>;
90
+ EVENTS?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['EVENTS'], CALCULATED, CONTEXT>;
91
+ LOG?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['LOG'], CALCULATED, CONTEXT>;
92
+ PARENT?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['PARENT'], CALCULATED, CONTEXT>;
93
+ EFFECT?: EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED, CONTEXT>;
92
94
  }
93
95
 
94
- type CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED> = keyof DRIVERS extends never
96
+ type CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, CONTEXT = {}> = keyof DRIVERS extends never
95
97
  ? {
96
- [driver: string]: SinkValue<STATE, PROPS, ACTIONS, any, any, CALCULATED>
98
+ [driver: string]: SinkValue<STATE, PROPS, ACTIONS, any, any, CALCULATED, CONTEXT>
97
99
  }
98
100
  : {
99
101
  [DRIVER_KEY in keyof DRIVERS]: SinkValue<
@@ -102,15 +104,16 @@ type CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED>
102
104
  ACTIONS,
103
105
  ACTION_ENTRY,
104
106
  DRIVERS[DRIVER_KEY] extends { source: any; sink: any } ? DRIVERS[DRIVER_KEY]['sink'] : any,
105
- CALCULATED
107
+ CALCULATED,
108
+ CONTEXT
106
109
  >
107
110
  }
108
111
 
109
- type ModelEntry<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}> =
110
- | SinkValue<STATE, PROPS, ACTIONS, ACTION_ENTRY, STATE, CALCULATED>
112
+ type ModelEntry<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}, CONTEXT = {}> =
113
+ | SinkValue<STATE, PROPS, ACTIONS, ACTION_ENTRY, STATE, CALCULATED, CONTEXT>
111
114
  | Partial<
112
- DefaultSinks<STATE, PROPS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS> &
113
- CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED>
115
+ DefaultSinks<STATE, PROPS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS, CONTEXT> &
116
+ CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, CONTEXT>
114
117
  >
115
118
 
116
119
  type WithDefaultActions<STATE, ACTIONS> = ACTIONS & {
@@ -120,7 +123,7 @@ type WithDefaultActions<STATE, ACTIONS> = ACTIONS & {
120
123
  DISPOSE?: never;
121
124
  }
122
125
 
123
- type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}> = keyof ACTIONS extends never
126
+ type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}, CONTEXT = {}> = keyof ACTIONS extends never
124
127
  ? {
125
128
  [action: string]: ModelEntry<
126
129
  STATE,
@@ -129,7 +132,8 @@ type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS ext
129
132
  WithDefaultActions<STATE, { [action: string]: any }>,
130
133
  any,
131
134
  CALCULATED,
132
- SINK_RETURNS
135
+ SINK_RETURNS,
136
+ CONTEXT
133
137
  >
134
138
  }
135
139
  : {
@@ -140,7 +144,8 @@ type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS ext
140
144
  WithDefaultActions<STATE, ACTIONS>,
141
145
  WithDefaultActions<STATE, ACTIONS>[ACTION_KEY],
142
146
  CALCULATED,
143
- SINK_RETURNS
147
+ SINK_RETURNS,
148
+ CONTEXT
144
149
  >
145
150
  }
146
151
 
@@ -259,7 +264,7 @@ export type Component<
259
264
  DOMSourceName?: string;
260
265
  stateSourceName?: string;
261
266
  requestSourceName?: string;
262
- model?: ComponentModel<STATE, PROPS, FixDrivers<DRIVERS>, ACTIONS, CALCULATED, SINK_RETURNS>;
267
+ model?: ComponentModel<STATE, PROPS, FixDrivers<DRIVERS>, ACTIONS, CALCULATED, SINK_RETURNS, CONTEXT>;
263
268
  intent?: ComponentIntent<STATE & CALCULATED, FixDrivers<DRIVERS>, ACTIONS>;
264
269
  initialState?: STATE;
265
270
  calculated?: Calculated<STATE, CALCULATED>;
@@ -31,17 +31,39 @@ function esc(str) {
31
31
  function onRenderHtml(pageContext) {
32
32
  const { Page, config } = pageContext;
33
33
  const data = pageContext.data || {};
34
- // SPA mode: return empty shell, let the client render everything
34
+ // SPA mode: return empty shell, let the client render everything.
35
+ // Head component and meta tags are still included — they're static
36
+ // metadata that should be present regardless of SSR being disabled.
35
37
  if (config.ssr === false) {
36
38
  const lang = config.lang || 'en';
37
39
  const title = config.title || '';
40
+ const description = config.description || '';
41
+ const favicon = config.favicon || '';
38
42
  const titleTag = title ? `<title>${esc(title)}</title>` : '';
43
+ const descTag = description
44
+ ? `<meta name="description" content="${esc(description)}">`
45
+ : '';
46
+ const faviconTag = favicon
47
+ ? `<link rel="icon" href="${esc(favicon)}">`
48
+ : '';
49
+ let headContent = '';
50
+ if (config.Head && typeof config.Head === 'function') {
51
+ try {
52
+ headContent = sygnal.renderToString(config.Head, { state: {} });
53
+ }
54
+ catch (_) {
55
+ // Head rendering failure is non-fatal
56
+ }
57
+ }
39
58
  const spaHtml = `<!DOCTYPE html>
40
59
  <html lang="${lang}">
41
60
  <head>
42
61
  <meta charset="UTF-8">
43
62
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
44
63
  ${titleTag}
64
+ ${descTag}
65
+ ${faviconTag}
66
+ ${headContent}
45
67
  </head>
46
68
  <body>
47
69
  <div id="page-view"></div>
@@ -29,17 +29,39 @@ function esc(str) {
29
29
  function onRenderHtml(pageContext) {
30
30
  const { Page, config } = pageContext;
31
31
  const data = pageContext.data || {};
32
- // SPA mode: return empty shell, let the client render everything
32
+ // SPA mode: return empty shell, let the client render everything.
33
+ // Head component and meta tags are still included — they're static
34
+ // metadata that should be present regardless of SSR being disabled.
33
35
  if (config.ssr === false) {
34
36
  const lang = config.lang || 'en';
35
37
  const title = config.title || '';
38
+ const description = config.description || '';
39
+ const favicon = config.favicon || '';
36
40
  const titleTag = title ? `<title>${esc(title)}</title>` : '';
41
+ const descTag = description
42
+ ? `<meta name="description" content="${esc(description)}">`
43
+ : '';
44
+ const faviconTag = favicon
45
+ ? `<link rel="icon" href="${esc(favicon)}">`
46
+ : '';
47
+ let headContent = '';
48
+ if (config.Head && typeof config.Head === 'function') {
49
+ try {
50
+ headContent = renderToString(config.Head, { state: {} });
51
+ }
52
+ catch (_) {
53
+ // Head rendering failure is non-fatal
54
+ }
55
+ }
37
56
  const spaHtml = `<!DOCTYPE html>
38
57
  <html lang="${lang}">
39
58
  <head>
40
59
  <meta charset="UTF-8">
41
60
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
42
61
  ${titleTag}
62
+ ${descTag}
63
+ ${faviconTag}
64
+ ${headContent}
43
65
  </head>
44
66
  <body>
45
67
  <div id="page-view"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sygnal",
3
- "version": "5.3.4",
3
+ "version": "5.3.5",
4
4
  "description": "An intuitive framework for building fast and small components or applications based on Cycle.js",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "types": "./dist/index.d.ts",
package/src/index.d.ts CHANGED
@@ -42,11 +42,13 @@ type NextFunction<ACTIONS = any> = ACTIONS extends object
42
42
  ) => void
43
43
  : (action: string, data?: any, delay?: number) => void
44
44
 
45
- type Reducer<STATE, PROPS, ACTIONS = any, DATA = any, RETURN = any> = (
45
+ type ReducerExtras<PROPS, CONTEXT> = PROPS & { context: CONTEXT; children?: JSX.Element | JSX.Element[]; slots?: Record<string, JSX.Element[]> }
46
+
47
+ type Reducer<STATE, PROPS, ACTIONS = any, DATA = any, RETURN = any, CONTEXT = {}> = (
46
48
  state: STATE,
47
49
  args: DATA,
48
50
  next: NextFunction<ACTIONS>,
49
- props: PROPS
51
+ props: ReducerExtras<PROPS, CONTEXT>
50
52
  ) => RETURN | ABORT | undefined
51
53
 
52
54
  export type ExactShape<EXPECTED, ACTUAL extends EXPECTED> = ACTUAL &
@@ -76,24 +78,24 @@ type ResolvedNonStateSinkReturns<SINK_RETURNS extends NonStateSinkReturns = {}>
76
78
  * - true: Whatever value is received from the intent for this action is passed on as-is.
77
79
  * - Function: A reducer
78
80
  */
79
- type SinkValue<STATE, PROPS, ACTIONS, DATA, RETURN, CALCULATED> =
81
+ type SinkValue<STATE, PROPS, ACTIONS, DATA, RETURN, CALCULATED, CONTEXT = {}> =
80
82
  | true
81
- | Reducer<STATE & CALCULATED, PROPS, ACTIONS, DATA, RETURN>
83
+ | Reducer<STATE & CALCULATED, PROPS, ACTIONS, DATA, RETURN, CONTEXT>
82
84
 
83
- type EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED> =
84
- | ((state: STATE & CALCULATED, args: DATA, next: NextFunction<ACTIONS>, props: PROPS) => void)
85
+ type EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED, CONTEXT = {}> =
86
+ | ((state: STATE & CALCULATED, args: DATA, next: NextFunction<ACTIONS>, props: ReducerExtras<PROPS, CONTEXT>) => void)
85
87
 
86
- type DefaultSinks<STATE, PROPS, ACTIONS, DATA, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}> = {
87
- STATE?: SinkValue<STATE, PROPS, ACTIONS, DATA, STATE, CALCULATED>;
88
- EVENTS?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['EVENTS'], CALCULATED>;
89
- LOG?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['LOG'], CALCULATED>;
90
- PARENT?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['PARENT'], CALCULATED>;
91
- EFFECT?: EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED>;
88
+ type DefaultSinks<STATE, PROPS, ACTIONS, DATA, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}, CONTEXT = {}> = {
89
+ STATE?: SinkValue<STATE, PROPS, ACTIONS, DATA, STATE, CALCULATED, CONTEXT>;
90
+ EVENTS?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['EVENTS'], CALCULATED, CONTEXT>;
91
+ LOG?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['LOG'], CALCULATED, CONTEXT>;
92
+ PARENT?: SinkValue<STATE, PROPS, ACTIONS, DATA, ResolvedNonStateSinkReturns<SINK_RETURNS>['PARENT'], CALCULATED, CONTEXT>;
93
+ EFFECT?: EffectReducer<STATE, PROPS, ACTIONS, DATA, CALCULATED, CONTEXT>;
92
94
  }
93
95
 
94
- type CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED> = keyof DRIVERS extends never
96
+ type CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, CONTEXT = {}> = keyof DRIVERS extends never
95
97
  ? {
96
- [driver: string]: SinkValue<STATE, PROPS, ACTIONS, any, any, CALCULATED>
98
+ [driver: string]: SinkValue<STATE, PROPS, ACTIONS, any, any, CALCULATED, CONTEXT>
97
99
  }
98
100
  : {
99
101
  [DRIVER_KEY in keyof DRIVERS]: SinkValue<
@@ -102,15 +104,16 @@ type CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED>
102
104
  ACTIONS,
103
105
  ACTION_ENTRY,
104
106
  DRIVERS[DRIVER_KEY] extends { source: any; sink: any } ? DRIVERS[DRIVER_KEY]['sink'] : any,
105
- CALCULATED
107
+ CALCULATED,
108
+ CONTEXT
106
109
  >
107
110
  }
108
111
 
109
- type ModelEntry<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}> =
110
- | SinkValue<STATE, PROPS, ACTIONS, ACTION_ENTRY, STATE, CALCULATED>
112
+ type ModelEntry<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}, CONTEXT = {}> =
113
+ | SinkValue<STATE, PROPS, ACTIONS, ACTION_ENTRY, STATE, CALCULATED, CONTEXT>
111
114
  | Partial<
112
- DefaultSinks<STATE, PROPS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS> &
113
- CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED>
115
+ DefaultSinks<STATE, PROPS, ACTIONS, ACTION_ENTRY, CALCULATED, SINK_RETURNS, CONTEXT> &
116
+ CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY, CALCULATED, CONTEXT>
114
117
  >
115
118
 
116
119
  type WithDefaultActions<STATE, ACTIONS> = ACTIONS & {
@@ -120,7 +123,7 @@ type WithDefaultActions<STATE, ACTIONS> = ACTIONS & {
120
123
  DISPOSE?: never;
121
124
  }
122
125
 
123
- type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}> = keyof ACTIONS extends never
126
+ type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}, CONTEXT = {}> = keyof ACTIONS extends never
124
127
  ? {
125
128
  [action: string]: ModelEntry<
126
129
  STATE,
@@ -129,7 +132,8 @@ type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS ext
129
132
  WithDefaultActions<STATE, { [action: string]: any }>,
130
133
  any,
131
134
  CALCULATED,
132
- SINK_RETURNS
135
+ SINK_RETURNS,
136
+ CONTEXT
133
137
  >
134
138
  }
135
139
  : {
@@ -140,7 +144,8 @@ type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS ext
140
144
  WithDefaultActions<STATE, ACTIONS>,
141
145
  WithDefaultActions<STATE, ACTIONS>[ACTION_KEY],
142
146
  CALCULATED,
143
- SINK_RETURNS
147
+ SINK_RETURNS,
148
+ CONTEXT
144
149
  >
145
150
  }
146
151
 
@@ -259,7 +264,7 @@ export type Component<
259
264
  DOMSourceName?: string;
260
265
  stateSourceName?: string;
261
266
  requestSourceName?: string;
262
- model?: ComponentModel<STATE, PROPS, FixDrivers<DRIVERS>, ACTIONS, CALCULATED, SINK_RETURNS>;
267
+ model?: ComponentModel<STATE, PROPS, FixDrivers<DRIVERS>, ACTIONS, CALCULATED, SINK_RETURNS, CONTEXT>;
263
268
  intent?: ComponentIntent<STATE & CALCULATED, FixDrivers<DRIVERS>, ACTIONS>;
264
269
  initialState?: STATE;
265
270
  calculated?: Calculated<STATE, CALCULATED>;
@@ -50,11 +50,30 @@ export function onRenderHtml(pageContext: PageContext) {
50
50
  const { Page, config } = pageContext
51
51
  const data = pageContext.data || {}
52
52
 
53
- // SPA mode: return empty shell, let the client render everything
53
+ // SPA mode: return empty shell, let the client render everything.
54
+ // Head component and meta tags are still included — they're static
55
+ // metadata that should be present regardless of SSR being disabled.
54
56
  if (config.ssr === false) {
55
57
  const lang = config.lang || 'en'
56
58
  const title = config.title || ''
59
+ const description = config.description || ''
60
+ const favicon = config.favicon || ''
57
61
  const titleTag = title ? `<title>${esc(title)}</title>` : ''
62
+ const descTag = description
63
+ ? `<meta name="description" content="${esc(description)}">`
64
+ : ''
65
+ const faviconTag = favicon
66
+ ? `<link rel="icon" href="${esc(favicon)}">`
67
+ : ''
68
+
69
+ let headContent = ''
70
+ if (config.Head && typeof config.Head === 'function') {
71
+ try {
72
+ headContent = renderToString(config.Head, { state: {} })
73
+ } catch (_) {
74
+ // Head rendering failure is non-fatal
75
+ }
76
+ }
58
77
 
59
78
  const spaHtml = `<!DOCTYPE html>
60
79
  <html lang="${lang}">
@@ -62,6 +81,9 @@ export function onRenderHtml(pageContext: PageContext) {
62
81
  <meta charset="UTF-8">
63
82
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
64
83
  ${titleTag}
84
+ ${descTag}
85
+ ${faviconTag}
86
+ ${headContent}
65
87
  </head>
66
88
  <body>
67
89
  <div id="page-view"></div>