@livenetworks/ashlar 2.0.7 → 2.0.9

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.
Files changed (213) hide show
  1. package/LICENSE +641 -641
  2. package/components/COMPONENTS.md +1116 -1116
  3. package/components/index.js +81 -81
  4. package/components/ln-accordion/ln-accordion-dev.scss +8 -8
  5. package/components/ln-accordion/ln-accordion.js +1 -1
  6. package/components/ln-accordion/ln-accordion.schema.json +30 -30
  7. package/components/ln-ajax/ln-ajax-dev.scss +8 -8
  8. package/components/ln-ajax/ln-ajax.js +1 -1
  9. package/components/ln-ajax/ln-ajax.schema.json +30 -30
  10. package/components/ln-api-connector/ln-api-connector.js +1 -1
  11. package/components/ln-api-connector/ln-api-connector.schema.json +9 -0
  12. package/components/ln-api-connector/src/ln-api-connector.js +2 -0
  13. package/components/ln-api-queue/ln-api-queue.js +1 -1
  14. package/components/ln-api-queue/ln-api-queue.schema.json +34 -34
  15. package/components/ln-api-queue/src/ln-api-queue.js +3 -13
  16. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -8
  17. package/components/ln-autoresize/ln-autoresize.js +1 -1
  18. package/components/ln-autoresize/ln-autoresize.schema.json +22 -22
  19. package/components/ln-autosave/ln-autosave-dev.scss +13 -13
  20. package/components/ln-autosave/ln-autosave.js +1 -1
  21. package/components/ln-autosave/ln-autosave.schema.json +48 -48
  22. package/components/ln-autosave/src/autosave-model.js +34 -34
  23. package/components/ln-chart/ln-chart.js +1 -1
  24. package/components/ln-chart/ln-chart.schema.json +146 -146
  25. package/components/ln-chart/src/chart-model.js +154 -154
  26. package/components/ln-circular-progress/ln-circular-progress.js +1 -1
  27. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -42
  28. package/components/ln-confirm/ln-confirm-dev.scss +24 -24
  29. package/components/ln-confirm/ln-confirm.js +1 -1
  30. package/components/ln-confirm/ln-confirm.schema.json +66 -66
  31. package/components/ln-confirm/src/ln-confirm.js +178 -178
  32. package/components/ln-core/attrs.js +13 -0
  33. package/components/ln-core/crypto.js +286 -265
  34. package/components/ln-core/dom.js +115 -115
  35. package/components/ln-core/form.js +146 -136
  36. package/components/ln-core/helpers.js +33 -33
  37. package/components/ln-core/http.js +48 -48
  38. package/components/ln-core/index.js +1 -1
  39. package/components/ln-core/lifecycle.js +5 -0
  40. package/components/ln-core/ln-core.schema.json +3 -2
  41. package/components/ln-core/locale.js +55 -55
  42. package/components/ln-core/matching.js +78 -78
  43. package/components/ln-core/number.js +125 -125
  44. package/components/ln-core/template.js +206 -206
  45. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  46. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -50
  47. package/components/ln-couchdb-connector/src/ln-couchdb-connector.js +1 -0
  48. package/components/ln-data-coordinator/README.md +9 -38
  49. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -8
  50. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
  51. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +16 -16
  52. package/components/ln-data-coordinator/src/data-read-policy.js +0 -7
  53. package/components/ln-data-coordinator/src/ln-data-coordinator.js +432 -1308
  54. package/components/ln-data-store/ln-data-store-dev.scss +13 -13
  55. package/components/ln-data-store/ln-data-store.js +1 -1
  56. package/components/ln-data-store/ln-data-store.schema.json +84 -84
  57. package/components/ln-data-store/src/data-store-model.js +184 -184
  58. package/components/ln-data-store/src/ln-data-store.js +1 -1
  59. package/components/ln-date/ln-date.js +1 -1
  60. package/components/ln-date/ln-date.schema.json +92 -92
  61. package/components/ln-date/src/date-model.js +186 -186
  62. package/components/ln-debug/ln-debug.js +1 -1
  63. package/components/ln-debug/ln-debug.schema.json +28 -28
  64. package/components/ln-debug/src/console-sink.js +19 -19
  65. package/components/ln-debug/src/gate.js +95 -95
  66. package/components/ln-debug/src/generated-attributes.js +4 -2
  67. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -13
  68. package/components/ln-dropdown/ln-dropdown.js +1 -1
  69. package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
  70. package/components/ln-editor/ln-editor.js +1 -1
  71. package/components/ln-editor/ln-editor.schema.json +58 -58
  72. package/components/ln-external-links/ln-external-links.js +1 -1
  73. package/components/ln-external-links/ln-external-links.schema.json +18 -18
  74. package/components/ln-fill/ln-fill.js +1 -1
  75. package/components/ln-fill/ln-fill.schema.json +23 -23
  76. package/components/ln-filter/ln-filter.js +1 -1
  77. package/components/ln-filter/ln-filter.schema.json +153 -153
  78. package/components/ln-filter/src/filter-model.js +185 -185
  79. package/components/ln-form/ln-form-dev.scss +8 -8
  80. package/components/ln-form/ln-form.js +1 -1
  81. package/components/ln-form/ln-form.schema.json +6 -0
  82. package/components/ln-form/src/ln-form.js +21 -0
  83. package/components/ln-http/ln-http.js +1 -1
  84. package/components/ln-http/ln-http.schema.json +6 -6
  85. package/components/ln-icon/ln-icon.schema.json +6 -6
  86. package/components/ln-include/ln-include.js +1 -1
  87. package/components/ln-include/ln-include.schema.json +23 -23
  88. package/components/ln-key/ln-key-dev.scss +18 -18
  89. package/components/ln-key/ln-key.js +1 -1
  90. package/components/ln-key/ln-key.schema.json +56 -56
  91. package/components/ln-key/src/ln-key.js +10 -10
  92. package/components/ln-link/ln-link-dev.scss +8 -8
  93. package/components/ln-link/ln-link.js +1 -1
  94. package/components/ln-link/ln-link.schema.json +22 -22
  95. package/components/ln-list/README.md +152 -152
  96. package/components/ln-list/ln-list-dev.scss +8 -8
  97. package/components/ln-list/ln-list.js +1 -1
  98. package/components/ln-list/ln-list.schema.json +166 -166
  99. package/components/ln-modal/ln-modal.js +1 -1
  100. package/components/ln-modal/ln-modal.schema.json +65 -65
  101. package/components/ln-nav/ln-nav.js +1 -1
  102. package/components/ln-nav/ln-nav.schema.json +25 -25
  103. package/components/ln-number/ln-number.js +1 -1
  104. package/components/ln-number/ln-number.schema.json +63 -63
  105. package/components/ln-number/src/ln-number.js +26 -0
  106. package/components/ln-number/src/number-model.js +31 -31
  107. package/components/ln-obfuscator/README.md +60 -60
  108. package/components/ln-obfuscator/ln-obfuscator.js +1 -1
  109. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -49
  110. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -159
  111. package/components/ln-obfuscator/src/obfuscator-model.js +250 -250
  112. package/components/ln-options/ln-options.js +1 -1
  113. package/components/ln-options/ln-options.schema.json +6 -0
  114. package/components/ln-options/src/ln-options.js +4 -4
  115. package/components/ln-persist/README.md +151 -151
  116. package/components/ln-persist/ln-persist.js +1 -1
  117. package/components/ln-persist/ln-persist.schema.json +28 -28
  118. package/components/ln-persist/src/ln-persist.js +83 -83
  119. package/components/ln-picklist/README.md +230 -230
  120. package/components/ln-picklist/ln-picklist-dev.scss +22 -22
  121. package/components/ln-picklist/ln-picklist.js +1 -1
  122. package/components/ln-picklist/ln-picklist.schema.json +49 -49
  123. package/components/ln-picklist/src/ln-picklist.js +228 -228
  124. package/components/ln-popover/ln-popover-dev.scss +13 -13
  125. package/components/ln-popover/ln-popover.js +1 -1
  126. package/components/ln-popover/ln-popover.schema.json +82 -82
  127. package/components/ln-progress/ln-progress-dev.scss +8 -8
  128. package/components/ln-progress/ln-progress.js +1 -1
  129. package/components/ln-progress/ln-progress.schema.json +40 -40
  130. package/components/ln-router/ln-router.js +1 -1
  131. package/components/ln-router/ln-router.schema.json +76 -76
  132. package/components/ln-scroll/README.md +45 -45
  133. package/components/ln-scroll/ln-scroll.js +1 -1
  134. package/components/ln-scroll/ln-scroll.schema.json +85 -85
  135. package/components/ln-scroll/src/ln-scroll.js +150 -150
  136. package/components/ln-search/README.md +188 -188
  137. package/components/ln-search/ln-search.js +1 -1
  138. package/components/ln-search/ln-search.schema.json +103 -103
  139. package/components/ln-slug/ln-slug.js +1 -1
  140. package/components/ln-slug/ln-slug.schema.json +23 -23
  141. package/components/ln-slug/src/ln-slug.js +25 -1
  142. package/components/ln-sort/ln-sort.js +1 -1
  143. package/components/ln-sort/ln-sort.schema.json +104 -104
  144. package/components/ln-sort/src/ln-sort.js +321 -321
  145. package/components/ln-sort/src/sort-model.js +117 -117
  146. package/components/ln-sortable/ln-sortable-dev.scss +13 -13
  147. package/components/ln-sortable/ln-sortable.js +1 -1
  148. package/components/ln-sortable/ln-sortable.schema.json +36 -36
  149. package/components/ln-stat/ln-stat-dev.scss +8 -8
  150. package/components/ln-stat/ln-stat.js +1 -1
  151. package/components/ln-stat/ln-stat.schema.json +32 -32
  152. package/components/ln-stat/src/stat-model.js +28 -28
  153. package/components/ln-table/ln-table.js +1 -1
  154. package/components/ln-table/ln-table.schema.json +211 -211
  155. package/components/ln-table/src/table-model.js +98 -98
  156. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
  157. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -93
  158. package/components/ln-tabs/ln-tabs.js +1 -1
  159. package/components/ln-tabs/ln-tabs.schema.json +75 -75
  160. package/components/ln-tabs/src/ln-tabs.js +208 -208
  161. package/components/ln-tabs/src/tabs-model.js +82 -82
  162. package/components/ln-time/ln-time-dev.scss +13 -13
  163. package/components/ln-time/ln-time.js +1 -1
  164. package/components/ln-time/ln-time.schema.json +38 -38
  165. package/components/ln-toast/ln-toast.js +1 -1
  166. package/components/ln-toast/ln-toast.schema.json +65 -65
  167. package/components/ln-toggle/ln-toggle.js +1 -1
  168. package/components/ln-toggle/ln-toggle.schema.json +52 -52
  169. package/components/ln-tooltip/ln-tooltip.js +1 -1
  170. package/components/ln-tooltip/ln-tooltip.schema.json +86 -86
  171. package/components/ln-translations/ln-translations.js +1 -1
  172. package/components/ln-translations/ln-translations.schema.json +120 -120
  173. package/components/ln-ui-coordinator/README.md +62 -62
  174. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -1
  175. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +28 -28
  176. package/components/ln-ui-coordinator/src/ln-ui-coordinator.js +140 -140
  177. package/components/ln-upload/ln-upload.js +1 -1
  178. package/components/ln-upload/ln-upload.schema.json +193 -193
  179. package/components/ln-upload/src/upload-model.js +78 -78
  180. package/components/ln-validate/ln-validate.js +1 -1
  181. package/components/ln-validate/ln-validate.schema.json +50 -50
  182. package/components/ln-validate/src/validate-model.js +64 -64
  183. package/components/ln-websocket-connector/README.md +166 -166
  184. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -1
  185. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -29
  186. package/components/ln-websocket-connector/src/ln-websocket-connector.js +283 -279
  187. package/ln-ashlar.html-data.json +15 -8
  188. package/package.json +85 -84
  189. package/theme/brand.css +73 -73
  190. package/theme/components/_confirm.scss +6 -6
  191. package/theme/components/_picklist.scss +3 -3
  192. package/theme/config/_palette.scss +274 -274
  193. package/theme/config/_theme.scss +292 -292
  194. package/theme/config/_tokens.scss +285 -285
  195. package/theme/config/mixins/_index.scss +62 -62
  196. package/theme/config/mixins/_picklist.scss +66 -66
  197. package/theme/ln-ashlar-dev.scss +66 -66
  198. package/theme/ln-ashlar-landing.scss +56 -56
  199. package/theme/ln-ashlar-theme.scss +63 -63
  200. package/theme/presentation/_bento.scss +151 -151
  201. package/theme/presentation/_cta.scss +111 -111
  202. package/theme/presentation/_effects.scss +216 -216
  203. package/theme/presentation/_features.scss +108 -108
  204. package/theme/presentation/_footer.scss +129 -129
  205. package/theme/presentation/_hero.scss +158 -158
  206. package/theme/presentation/_layout.scss +170 -170
  207. package/theme/presentation/_navbar.scss +140 -140
  208. package/theme/presentation/_pricing.scss +262 -262
  209. package/theme/presentation/_stats.scss +87 -87
  210. package/theme/presentation/_testimonials.scss +124 -124
  211. package/theme/presentation/_tokens.scss +39 -39
  212. package/theme/presentation/_typography.scss +163 -163
  213. package/web-types.json +26 -12
@@ -1,50 +1,50 @@
1
- {
2
- "$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
3
- "component": "ln-validate",
4
- "generator": "scripts/sync-ln-schemas.mjs",
5
- "attributes": {
6
- "data-ln-debug": {
7
- "direction": null,
8
- "sources": [
9
- "ln-validate-dev.scss"
10
- ]
11
- },
12
- "data-ln-error": {
13
- "direction": null,
14
- "sources": [
15
- "ln-validate.scss"
16
- ]
17
- },
18
- "data-ln-form": {
19
- "direction": null,
20
- "sources": [
21
- "ln-validate-dev.scss"
22
- ]
23
- },
24
- "data-ln-validate": {
25
- "direction": null,
26
- "sources": [
27
- "ln-validate-dev.scss",
28
- "src/ln-validate.js"
29
- ],
30
- "type": "marker",
31
- "description": "Activates validation on form input or field"
32
- },
33
- "data-ln-validate-error": {
34
- "direction": "author",
35
- "sources": [
36
- "src/ln-validate.js"
37
- ],
38
- "type": "string",
39
- "description": "Identifies error message template for a specific validation rule"
40
- },
41
- "data-ln-validate-errors": {
42
- "direction": "author",
43
- "sources": [
44
- "src/ln-validate.js"
45
- ],
46
- "type": "marker",
47
- "description": "Container element holding validation error message elements"
48
- }
49
- }
50
- }
1
+ {
2
+ "$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
3
+ "component": "ln-validate",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-debug": {
7
+ "direction": null,
8
+ "sources": [
9
+ "ln-validate-dev.scss"
10
+ ]
11
+ },
12
+ "data-ln-error": {
13
+ "direction": null,
14
+ "sources": [
15
+ "ln-validate.scss"
16
+ ]
17
+ },
18
+ "data-ln-form": {
19
+ "direction": null,
20
+ "sources": [
21
+ "ln-validate-dev.scss"
22
+ ]
23
+ },
24
+ "data-ln-validate": {
25
+ "direction": null,
26
+ "sources": [
27
+ "ln-validate-dev.scss",
28
+ "src/ln-validate.js"
29
+ ],
30
+ "type": "marker",
31
+ "description": "Activates validation on form input or field"
32
+ },
33
+ "data-ln-validate-error": {
34
+ "direction": "author",
35
+ "sources": [
36
+ "src/ln-validate.js"
37
+ ],
38
+ "type": "string",
39
+ "description": "Identifies error message template for a specific validation rule"
40
+ },
41
+ "data-ln-validate-errors": {
42
+ "direction": "author",
43
+ "sources": [
44
+ "src/ln-validate.js"
45
+ ],
46
+ "type": "marker",
47
+ "description": "Container element holding validation error message elements"
48
+ }
49
+ }
50
+ }
@@ -1,64 +1,64 @@
1
- export const ERROR_MAP = {
2
- required: 'valueMissing',
3
- typeMismatch: 'typeMismatch',
4
- tooShort: 'tooShort',
5
- tooLong: 'tooLong',
6
- patternMismatch: 'patternMismatch',
7
- rangeUnderflow: 'rangeUnderflow',
8
- rangeOverflow: 'rangeOverflow'
9
- };
10
-
11
- /**
12
- * Checks if a field is valid given its native validity state and custom errors count.
13
- * @param {{ valid?: boolean } | null | undefined} validityState
14
- * @param {number} [customErrorsCount=0]
15
- * @returns {boolean}
16
- */
17
- export function isFieldValid(validityState, customErrorsCount = 0) {
18
- if (!validityState) return customErrorsCount === 0;
19
- return Boolean(validityState.valid && customErrorsCount === 0);
20
- }
21
-
22
- /**
23
- * Whether a field already holds a value at init, so it is validated up front.
24
- * A checkbox or radio always carries a `value` ("on" by default) — only its
25
- * `checked` state says whether anything was chosen.
26
- * @param {{ type?: string, value?: string, checked?: boolean }} field
27
- * @returns {boolean}
28
- */
29
- export function hasInitialValue(field) {
30
- if (field.type === 'checkbox' || field.type === 'radio') return Boolean(field.checked);
31
- return Boolean(field.value && field.value.trim() !== '');
32
- }
33
-
34
- /**
35
- * Resolves all active error keys for a given validity state and custom errors set.
36
- * @param {Record<string, boolean>} validityState
37
- * @param {Set<string>|Array<string>} [customErrors]
38
- * @returns {string[]}
39
- */
40
- export function resolveActiveErrorKeys(validityState, customErrors) {
41
- const active = [];
42
-
43
- if (validityState) {
44
- const keys = Object.keys(ERROR_MAP);
45
- for (let i = 0; i < keys.length; i++) {
46
- const errorKey = keys[i];
47
- const prop = ERROR_MAP[errorKey];
48
- if (validityState[prop]) {
49
- active.push(errorKey);
50
- }
51
- }
52
- }
53
-
54
- if (customErrors) {
55
- const customList = Array.from(customErrors);
56
- for (let i = 0; i < customList.length; i++) {
57
- if (customList[i] && active.indexOf(customList[i]) === -1) {
58
- active.push(customList[i]);
59
- }
60
- }
61
- }
62
-
63
- return active;
64
- }
1
+ export const ERROR_MAP = {
2
+ required: 'valueMissing',
3
+ typeMismatch: 'typeMismatch',
4
+ tooShort: 'tooShort',
5
+ tooLong: 'tooLong',
6
+ patternMismatch: 'patternMismatch',
7
+ rangeUnderflow: 'rangeUnderflow',
8
+ rangeOverflow: 'rangeOverflow'
9
+ };
10
+
11
+ /**
12
+ * Checks if a field is valid given its native validity state and custom errors count.
13
+ * @param {{ valid?: boolean } | null | undefined} validityState
14
+ * @param {number} [customErrorsCount=0]
15
+ * @returns {boolean}
16
+ */
17
+ export function isFieldValid(validityState, customErrorsCount = 0) {
18
+ if (!validityState) return customErrorsCount === 0;
19
+ return Boolean(validityState.valid && customErrorsCount === 0);
20
+ }
21
+
22
+ /**
23
+ * Whether a field already holds a value at init, so it is validated up front.
24
+ * A checkbox or radio always carries a `value` ("on" by default) — only its
25
+ * `checked` state says whether anything was chosen.
26
+ * @param {{ type?: string, value?: string, checked?: boolean }} field
27
+ * @returns {boolean}
28
+ */
29
+ export function hasInitialValue(field) {
30
+ if (field.type === 'checkbox' || field.type === 'radio') return Boolean(field.checked);
31
+ return Boolean(field.value && field.value.trim() !== '');
32
+ }
33
+
34
+ /**
35
+ * Resolves all active error keys for a given validity state and custom errors set.
36
+ * @param {Record<string, boolean>} validityState
37
+ * @param {Set<string>|Array<string>} [customErrors]
38
+ * @returns {string[]}
39
+ */
40
+ export function resolveActiveErrorKeys(validityState, customErrors) {
41
+ const active = [];
42
+
43
+ if (validityState) {
44
+ const keys = Object.keys(ERROR_MAP);
45
+ for (let i = 0; i < keys.length; i++) {
46
+ const errorKey = keys[i];
47
+ const prop = ERROR_MAP[errorKey];
48
+ if (validityState[prop]) {
49
+ active.push(errorKey);
50
+ }
51
+ }
52
+ }
53
+
54
+ if (customErrors) {
55
+ const customList = Array.from(customErrors);
56
+ for (let i = 0; i < customList.length; i++) {
57
+ if (customList[i] && active.indexOf(customList[i]) === -1) {
58
+ active.push(customList[i]);
59
+ }
60
+ }
61
+ }
62
+
63
+ return active;
64
+ }
@@ -1,166 +1,166 @@
1
- # `data-ln-websocket-connector`
2
-
3
- > Applied to an element inside a `ln-data-coordinator`. Opens a WebSocket, turns server pushes into
4
- > `ln-websocket-connector:fetched`, and answers the same request events as `ln-api-connector` and
5
- > `ln-couchdb-connector`. Connection status is private state announced by events — never an attribute.
6
-
7
- A zero-dependency push transport. Beside a REST connector it only delivers live changes; on its own
8
- it carries reads, writes and pushes over one socket.
9
-
10
- ---
11
-
12
- ## Declarative DOM Setup
13
-
14
- ### Live changes beside REST
15
-
16
- The REST connector takes every request; the socket only pushes changes into the store.
17
-
18
- ```html
19
- <ul data-ln-data-coordinator="tasks" hidden>
20
- <li data-ln-data-store="tasks" id="tasks"></li>
21
- <li data-ln-api-connector data-ln-api-base-url="https://api.example.com" data-ln-api-path="tasks"></li>
22
- <li data-ln-websocket-connector data-ln-websocket-connector-url="wss://api.example.com/live"></li>
23
- </ul>
24
- ```
25
-
26
- ### Socket alone
27
-
28
- With no REST or CouchDB connector in the coordinator, the socket takes every request too.
29
-
30
- ```html
31
- <ul data-ln-data-coordinator="tasks" hidden>
32
- <li data-ln-data-store="tasks" id="tasks"></li>
33
- <li data-ln-websocket-connector data-ln-websocket-connector-url="wss://api.example.com/live"></li>
34
- </ul>
35
- ```
36
-
37
- ---
38
-
39
- ## Attributes
40
-
41
- Both attributes are commands set from outside; the component obeys them.
42
-
43
- | Attribute | Values | Default | Description |
44
- |-----------|--------|---------|-------------|
45
- | `data-ln-websocket-connector` | `connect` \| `disconnect` | `connect` | Opens or closes the socket. A bare attribute connects. |
46
- | `data-ln-websocket-connector-url` | `ws://…` \| `wss://…` | — | Endpoint. Changing it while connected closes and reopens on the new URL. |
47
-
48
- There is no attribute for the connection status. `connecting`, `connected` and `disconnected` are
49
- what the component observes, not something anyone can set — writing `connected` into an attribute
50
- would not connect anything. The status is announced by events; whoever listens decides how to show it.
51
-
52
- ---
53
-
54
- ## DOM Events
55
-
56
- ### Commands (dispatched TO the connector)
57
-
58
- Only the `ln-websocket-connector:*` namespace is handled.
59
-
60
- | Event | `detail` | Description |
61
- |-------|----------|-------------|
62
- | `ln-websocket-connector:request-sync` | `{ since, meta? }` | Delta sync since a token. |
63
- | `ln-websocket-connector:request-query` | `{ query, meta? }` | One page of a query (`search`, `filters`, `sort`, `offset`, `limit`). |
64
- | `ln-websocket-connector:request-create` | `{ data, tempId?, idempotencyKey?, meta? }` | Create a record. |
65
- | `ln-websocket-connector:request-update` | `{ id, data, expected_version?, idempotencyKey?, meta? }` | Update a record. |
66
- | `ln-websocket-connector:request-delete` | `{ id, idempotencyKey?, meta? }` | Delete a record. |
67
- | `ln-websocket-connector:request-bulk-delete` | `{ ids, idempotencyKey?, meta? }` | Delete several records. |
68
-
69
- ### Notifications (emitted BY the connector)
70
-
71
- | Event | `detail` | Description |
72
- |-------|----------|-------------|
73
- | `ln-websocket-connector:connecting` | `{ url, attempt }` | A connection attempt starts. `attempt` counts from 1 and resets on open. |
74
- | `ln-websocket-connector:connected` | `{ url }` | The socket is open. |
75
- | `ln-websocket-connector:disconnected` | `{ url, code, reason, willReconnect }` | The socket closed, or a pending reconnect was cancelled. |
76
- | `ln-websocket-connector:fetched` | `{ data, since, meta }` / `{ data, total, filtered, offset, queryGen, meta }` | A sync reply, a query page, or a server push (`meta: null`). |
77
- | `ln-websocket-connector:created` | `{ record, tempId, message, meta }` | Create confirmed. |
78
- | `ln-websocket-connector:updated` | `{ record, id, message, meta }` | Update confirmed. |
79
- | `ln-websocket-connector:deleted` | `{ response, id, message, meta }` | Delete confirmed. |
80
- | `ln-websocket-connector:bulk-deleted` | `{ response, ids, message, meta }` | Bulk delete confirmed. |
81
- | `ln-websocket-connector:error` | `{ action, error, status, data, conflictData, meta, … }` | A rejected request, or one that could not be delivered (`status: 0`). |
82
-
83
- `meta` is echoed unchanged and never leaves the page.
84
-
85
- ---
86
-
87
- ## Wire Protocol
88
-
89
- JSON text frames.
90
-
91
- **Request** — `{ "ref": "<string>", "type": "<type>", ...payload }`
92
-
93
- | `type` | payload |
94
- |--------|---------|
95
- | `sync` | `since` |
96
- | `query` | `query` |
97
- | `create` | `data`, `idempotencyKey` |
98
- | `update` | `id`, `data`, `expected_version`, `idempotencyKey` |
99
- | `delete` | `id`, `idempotencyKey` |
100
- | `bulk-delete` | `ids`, `idempotencyKey` |
101
-
102
- **Reply** — the same `ref`, then either `{ "ok": true, "content": …, "message": … }` or
103
- `{ "ok": false, "status": 409, "error": "…", "data": … }`. `content` is `{ data, deleted, synced_at }`
104
- for `sync`, `{ data, total, filtered }` for `query`, the record for `create` / `update`, and the
105
- server response for `delete` / `bulk-delete`. `message` is the same opaque toast payload as the REST
106
- `{ message, content }` envelope.
107
-
108
- **Push** — no `ref`: `{ "type": "changes", "data": [...], "deleted": [...], "synced_at": … }`.
109
-
110
- `synced_at` is the same token the REST delta takes as `since`. Beside REST, a push advances the
111
- store's `lastSyncedAt`, and the next REST sync sends it — the server must keep both in one currency.
112
-
113
- ---
114
-
115
- ## Coordinator Integration
116
-
117
- `ln-data-coordinator` needs no wiring:
118
-
119
- - **Request connector:** `[data-ln-api-connector]` or `[data-ln-couchdb-connector]` when present,
120
- otherwise `[data-ln-websocket-connector]`.
121
- - **Pushes:** `:fetched` without `meta.kind` goes to `store.applySync` — upsert plus delete, the path
122
- CouchDB sync already uses. Bound tables and lists refresh on `ln-data-store:synced`.
123
- - **Catch-up:** on every `:connected` the coordinator runs `store.forceSync()`, a delta since
124
- `lastSyncedAt`, over whichever connector takes requests.
125
-
126
- ### Showing the status
127
-
128
- The coordinator does not render connection status. Page wiring listens and draws it — here with
129
- an app-owned `data-status`, since app state stays out of `data-ln-*`:
130
-
131
- ```js
132
- const chip = document.getElementById('tasks-status');
133
- ['connecting', 'connected', 'disconnected'].forEach(function (status) {
134
- coordinatorEl.addEventListener('ln-websocket-connector:' + status, function () {
135
- chip.setAttribute('data-status', status);
136
- });
137
- });
138
- ```
139
-
140
- ---
141
-
142
- ## 🔧 Internals
143
-
144
- Source: `components/ln-websocket-connector/src/ln-websocket-connector.js`.
145
-
146
- ### Requests while not connected
147
-
148
- - **Command `connect`, socket not open** (first connect, or between reconnect attempts): the request
149
- is held and sent in order on open. `:connected` is dispatched before the flush, so the coordinator's
150
- catch-up sync sees the store already syncing and does not send a second one.
151
- - **Command `disconnect`:** the request fails at once with `:error { status: 0 }`.
152
- - **Socket closes with replies outstanding:** each one fails with `:error { status: 0 }`. The
153
- coordinator treats status 0 as transient and never deletes local data on it.
154
-
155
- ### Reconnect
156
-
157
- An unexpected close while the command is `connect` retries after 1 s, doubling up to 30 s, and the
158
- counter resets on a successful open. `new WebSocket()` throwing synchronously (an invalid URL, or
159
- `ws://` blocked on an `https://` page) is not retried: retrying cannot fix it. The component reports
160
- `:disconnected { willReconnect: false }`; a new URL or a new `connect` command opens again.
161
-
162
- ### Teardown
163
-
164
- `destroy()` closes the socket, clears the reconnect timer, removes the request listeners and
165
- deletes the instance. It dispatches nothing, so held and outstanding requests are discarded, not
166
- failed.
1
+ # `data-ln-websocket-connector`
2
+
3
+ > Applied to an element inside a `ln-data-coordinator`. Opens a WebSocket, turns server pushes into
4
+ > `ln-websocket-connector:fetched`, and answers the same request events as `ln-api-connector` and
5
+ > `ln-couchdb-connector`. Connection status is private state announced by events — never an attribute.
6
+
7
+ A zero-dependency push transport. Beside a REST connector it only delivers live changes; on its own
8
+ it carries reads, writes and pushes over one socket.
9
+
10
+ ---
11
+
12
+ ## Declarative DOM Setup
13
+
14
+ ### Live changes beside REST
15
+
16
+ The REST connector takes every request; the socket only pushes changes into the store.
17
+
18
+ ```html
19
+ <ul data-ln-data-coordinator="tasks" hidden>
20
+ <li data-ln-data-store="tasks" id="tasks"></li>
21
+ <li data-ln-api-connector data-ln-api-base-url="https://api.example.com" data-ln-api-path="tasks"></li>
22
+ <li data-ln-websocket-connector data-ln-websocket-connector-url="wss://api.example.com/live"></li>
23
+ </ul>
24
+ ```
25
+
26
+ ### Socket alone
27
+
28
+ With no REST or CouchDB connector in the coordinator, the socket takes every request too.
29
+
30
+ ```html
31
+ <ul data-ln-data-coordinator="tasks" hidden>
32
+ <li data-ln-data-store="tasks" id="tasks"></li>
33
+ <li data-ln-websocket-connector data-ln-websocket-connector-url="wss://api.example.com/live"></li>
34
+ </ul>
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Attributes
40
+
41
+ Both attributes are commands set from outside; the component obeys them.
42
+
43
+ | Attribute | Values | Default | Description |
44
+ |-----------|--------|---------|-------------|
45
+ | `data-ln-websocket-connector` | `connect` \| `disconnect` | `connect` | Opens or closes the socket. A bare attribute connects. |
46
+ | `data-ln-websocket-connector-url` | `ws://…` \| `wss://…` | — | Endpoint. Changing it while connected closes and reopens on the new URL. |
47
+
48
+ There is no attribute for the connection status. `connecting`, `connected` and `disconnected` are
49
+ what the component observes, not something anyone can set — writing `connected` into an attribute
50
+ would not connect anything. The status is announced by events; whoever listens decides how to show it.
51
+
52
+ ---
53
+
54
+ ## DOM Events
55
+
56
+ ### Commands (dispatched TO the connector)
57
+
58
+ Only the `ln-websocket-connector:*` namespace is handled.
59
+
60
+ | Event | `detail` | Description |
61
+ |-------|----------|-------------|
62
+ | `ln-websocket-connector:request-sync` | `{ since, meta? }` | Delta sync since a token. |
63
+ | `ln-websocket-connector:request-query` | `{ query, meta? }` | One page of a query (`search`, `filters`, `sort`, `offset`, `limit`). |
64
+ | `ln-websocket-connector:request-create` | `{ data, tempId?, idempotencyKey?, meta? }` | Create a record. |
65
+ | `ln-websocket-connector:request-update` | `{ id, data, expected_version?, idempotencyKey?, meta? }` | Update a record. |
66
+ | `ln-websocket-connector:request-delete` | `{ id, idempotencyKey?, meta? }` | Delete a record. |
67
+ | `ln-websocket-connector:request-bulk-delete` | `{ ids, idempotencyKey?, meta? }` | Delete several records. |
68
+
69
+ ### Notifications (emitted BY the connector)
70
+
71
+ | Event | `detail` | Description |
72
+ |-------|----------|-------------|
73
+ | `ln-websocket-connector:connecting` | `{ url, attempt }` | A connection attempt starts. `attempt` counts from 1 and resets on open. |
74
+ | `ln-websocket-connector:connected` | `{ url }` | The socket is open. |
75
+ | `ln-websocket-connector:disconnected` | `{ url, code, reason, willReconnect }` | The socket closed, or a pending reconnect was cancelled. |
76
+ | `ln-websocket-connector:fetched` | `{ data, since, meta }` / `{ data, total, filtered, offset, queryGen, meta }` | A sync reply, a query page, or a server push (`meta: null`). |
77
+ | `ln-websocket-connector:created` | `{ record, tempId, message, meta }` | Create confirmed. |
78
+ | `ln-websocket-connector:updated` | `{ record, id, message, meta }` | Update confirmed. |
79
+ | `ln-websocket-connector:deleted` | `{ response, id, message, meta }` | Delete confirmed. |
80
+ | `ln-websocket-connector:bulk-deleted` | `{ response, ids, message, meta }` | Bulk delete confirmed. |
81
+ | `ln-websocket-connector:error` | `{ action, error, status, data, conflictData, meta, … }` | A rejected request, or one that could not be delivered (`status: 0`). |
82
+
83
+ `meta` is echoed unchanged and never leaves the page.
84
+
85
+ ---
86
+
87
+ ## Wire Protocol
88
+
89
+ JSON text frames.
90
+
91
+ **Request** — `{ "ref": "<string>", "type": "<type>", ...payload }`
92
+
93
+ | `type` | payload |
94
+ |--------|---------|
95
+ | `sync` | `since` |
96
+ | `query` | `query` |
97
+ | `create` | `data`, `idempotencyKey` |
98
+ | `update` | `id`, `data`, `expected_version`, `idempotencyKey` |
99
+ | `delete` | `id`, `idempotencyKey` |
100
+ | `bulk-delete` | `ids`, `idempotencyKey` |
101
+
102
+ **Reply** — the same `ref`, then either `{ "ok": true, "content": …, "message": … }` or
103
+ `{ "ok": false, "status": 409, "error": "…", "data": … }`. `content` is `{ data, deleted, synced_at }`
104
+ for `sync`, `{ data, total, filtered }` for `query`, the record for `create` / `update`, and the
105
+ server response for `delete` / `bulk-delete`. `message` is the same opaque toast payload as the REST
106
+ `{ message, content }` envelope.
107
+
108
+ **Push** — no `ref`: `{ "type": "changes", "data": [...], "deleted": [...], "synced_at": … }`.
109
+
110
+ `synced_at` is the same token the REST delta takes as `since`. Beside REST, a push advances the
111
+ store's `lastSyncedAt`, and the next REST sync sends it — the server must keep both in one currency.
112
+
113
+ ---
114
+
115
+ ## Coordinator Integration
116
+
117
+ `ln-data-coordinator` needs no wiring:
118
+
119
+ - **Request connector:** `[data-ln-api-connector]` or `[data-ln-couchdb-connector]` when present,
120
+ otherwise `[data-ln-websocket-connector]`.
121
+ - **Pushes:** `:fetched` without `meta.kind` goes to `store.applySync` — upsert plus delete, the path
122
+ CouchDB sync already uses. Bound tables and lists refresh on `ln-data-store:synced`.
123
+ - **Catch-up:** on every `:connected` the coordinator runs `store.forceSync()`, a delta since
124
+ `lastSyncedAt`, over whichever connector takes requests.
125
+
126
+ ### Showing the status
127
+
128
+ The coordinator does not render connection status. Page wiring listens and draws it — here with
129
+ an app-owned `data-status`, since app state stays out of `data-ln-*`:
130
+
131
+ ```js
132
+ const chip = document.getElementById('tasks-status');
133
+ ['connecting', 'connected', 'disconnected'].forEach(function (status) {
134
+ coordinatorEl.addEventListener('ln-websocket-connector:' + status, function () {
135
+ chip.setAttribute('data-status', status);
136
+ });
137
+ });
138
+ ```
139
+
140
+ ---
141
+
142
+ ## 🔧 Internals
143
+
144
+ Source: `components/ln-websocket-connector/src/ln-websocket-connector.js`.
145
+
146
+ ### Requests while not connected
147
+
148
+ - **Command `connect`, socket not open** (first connect, or between reconnect attempts): the request
149
+ is held and sent in order on open. `:connected` is dispatched before the flush, so the coordinator's
150
+ catch-up sync sees the store already syncing and does not send a second one.
151
+ - **Command `disconnect`:** the request fails at once with `:error { status: 0 }`.
152
+ - **Socket closes with replies outstanding:** each one fails with `:error { status: 0 }`. The
153
+ coordinator treats status 0 as transient and never deletes local data on it.
154
+
155
+ ### Reconnect
156
+
157
+ An unexpected close while the command is `connect` retries after 1 s, doubling up to 30 s, and the
158
+ counter resets on a successful open. `new WebSocket()` throwing synchronously (an invalid URL, or
159
+ `ws://` blocked on an `https://` page) is not retried: retrying cannot fix it. The component reports
160
+ `:disconnected { willReconnect: false }`; a new URL or a new `connect` command opens again.
161
+
162
+ ### Teardown
163
+
164
+ `destroy()` closes the socket, clears the reconnect timer, removes the request listeners and
165
+ deletes the instance. It dispatches nothing, so held and outstanding requests are discarded, not
166
+ failed.