@livenetworks/ashlar 2.0.7 → 2.0.8

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 (207) 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 +96 -96
  12. package/components/ln-api-connector/src/ln-api-connector.js +1 -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/crypto.js +286 -265
  33. package/components/ln-core/dom.js +115 -115
  34. package/components/ln-core/form.js +136 -136
  35. package/components/ln-core/helpers.js +33 -33
  36. package/components/ln-core/http.js +48 -48
  37. package/components/ln-core/index.js +1 -1
  38. package/components/ln-core/ln-core.schema.json +109 -109
  39. package/components/ln-core/locale.js +55 -55
  40. package/components/ln-core/matching.js +78 -78
  41. package/components/ln-core/number.js +125 -125
  42. package/components/ln-core/template.js +206 -206
  43. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  44. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -50
  45. package/components/ln-couchdb-connector/src/ln-couchdb-connector.js +1 -0
  46. package/components/ln-data-coordinator/README.md +9 -38
  47. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -8
  48. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
  49. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +16 -16
  50. package/components/ln-data-coordinator/src/data-read-policy.js +0 -7
  51. package/components/ln-data-coordinator/src/ln-data-coordinator.js +428 -1308
  52. package/components/ln-data-store/ln-data-store-dev.scss +13 -13
  53. package/components/ln-data-store/ln-data-store.js +1 -1
  54. package/components/ln-data-store/ln-data-store.schema.json +84 -84
  55. package/components/ln-data-store/src/data-store-model.js +184 -184
  56. package/components/ln-data-store/src/ln-data-store.js +1 -1
  57. package/components/ln-date/ln-date.js +1 -1
  58. package/components/ln-date/ln-date.schema.json +92 -92
  59. package/components/ln-date/src/date-model.js +186 -186
  60. package/components/ln-debug/ln-debug.js +1 -1
  61. package/components/ln-debug/ln-debug.schema.json +28 -28
  62. package/components/ln-debug/src/console-sink.js +19 -19
  63. package/components/ln-debug/src/gate.js +95 -95
  64. package/components/ln-debug/src/generated-attributes.js +2 -2
  65. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -13
  66. package/components/ln-dropdown/ln-dropdown.js +1 -1
  67. package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
  68. package/components/ln-editor/ln-editor.js +1 -1
  69. package/components/ln-editor/ln-editor.schema.json +58 -58
  70. package/components/ln-external-links/ln-external-links.js +1 -1
  71. package/components/ln-external-links/ln-external-links.schema.json +18 -18
  72. package/components/ln-fill/ln-fill.js +1 -1
  73. package/components/ln-fill/ln-fill.schema.json +23 -23
  74. package/components/ln-filter/ln-filter.js +1 -1
  75. package/components/ln-filter/ln-filter.schema.json +153 -153
  76. package/components/ln-filter/src/filter-model.js +185 -185
  77. package/components/ln-form/ln-form-dev.scss +8 -8
  78. package/components/ln-form/ln-form.js +1 -1
  79. package/components/ln-form/ln-form.schema.json +45 -45
  80. package/components/ln-http/ln-http.js +1 -1
  81. package/components/ln-http/ln-http.schema.json +6 -6
  82. package/components/ln-icon/ln-icon.schema.json +6 -6
  83. package/components/ln-include/ln-include.js +1 -1
  84. package/components/ln-include/ln-include.schema.json +23 -23
  85. package/components/ln-key/ln-key-dev.scss +18 -18
  86. package/components/ln-key/ln-key.js +1 -1
  87. package/components/ln-key/ln-key.schema.json +56 -56
  88. package/components/ln-key/src/ln-key.js +10 -10
  89. package/components/ln-link/ln-link-dev.scss +8 -8
  90. package/components/ln-link/ln-link.js +1 -1
  91. package/components/ln-link/ln-link.schema.json +22 -22
  92. package/components/ln-list/README.md +152 -152
  93. package/components/ln-list/ln-list-dev.scss +8 -8
  94. package/components/ln-list/ln-list.js +1 -1
  95. package/components/ln-list/ln-list.schema.json +166 -166
  96. package/components/ln-modal/ln-modal.js +1 -1
  97. package/components/ln-modal/ln-modal.schema.json +65 -65
  98. package/components/ln-nav/ln-nav.js +1 -1
  99. package/components/ln-nav/ln-nav.schema.json +25 -25
  100. package/components/ln-number/ln-number.js +1 -1
  101. package/components/ln-number/ln-number.schema.json +63 -63
  102. package/components/ln-number/src/number-model.js +31 -31
  103. package/components/ln-obfuscator/README.md +60 -60
  104. package/components/ln-obfuscator/ln-obfuscator.js +1 -1
  105. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -49
  106. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -159
  107. package/components/ln-obfuscator/src/obfuscator-model.js +250 -250
  108. package/components/ln-options/ln-options.js +1 -1
  109. package/components/ln-options/ln-options.schema.json +41 -41
  110. package/components/ln-persist/README.md +151 -151
  111. package/components/ln-persist/ln-persist.js +1 -1
  112. package/components/ln-persist/ln-persist.schema.json +28 -28
  113. package/components/ln-persist/src/ln-persist.js +83 -83
  114. package/components/ln-picklist/README.md +230 -230
  115. package/components/ln-picklist/ln-picklist-dev.scss +22 -22
  116. package/components/ln-picklist/ln-picklist.js +1 -1
  117. package/components/ln-picklist/ln-picklist.schema.json +49 -49
  118. package/components/ln-picklist/src/ln-picklist.js +228 -228
  119. package/components/ln-popover/ln-popover-dev.scss +13 -13
  120. package/components/ln-popover/ln-popover.js +1 -1
  121. package/components/ln-popover/ln-popover.schema.json +82 -82
  122. package/components/ln-progress/ln-progress-dev.scss +8 -8
  123. package/components/ln-progress/ln-progress.js +1 -1
  124. package/components/ln-progress/ln-progress.schema.json +40 -40
  125. package/components/ln-router/ln-router.js +1 -1
  126. package/components/ln-router/ln-router.schema.json +76 -76
  127. package/components/ln-scroll/README.md +45 -45
  128. package/components/ln-scroll/ln-scroll.js +1 -1
  129. package/components/ln-scroll/ln-scroll.schema.json +85 -85
  130. package/components/ln-scroll/src/ln-scroll.js +150 -150
  131. package/components/ln-search/README.md +188 -188
  132. package/components/ln-search/ln-search.js +1 -1
  133. package/components/ln-search/ln-search.schema.json +103 -103
  134. package/components/ln-slug/ln-slug.js +1 -1
  135. package/components/ln-slug/ln-slug.schema.json +23 -23
  136. package/components/ln-sort/ln-sort.js +1 -1
  137. package/components/ln-sort/ln-sort.schema.json +104 -104
  138. package/components/ln-sort/src/ln-sort.js +321 -321
  139. package/components/ln-sort/src/sort-model.js +117 -117
  140. package/components/ln-sortable/ln-sortable-dev.scss +13 -13
  141. package/components/ln-sortable/ln-sortable.js +1 -1
  142. package/components/ln-sortable/ln-sortable.schema.json +36 -36
  143. package/components/ln-stat/ln-stat-dev.scss +8 -8
  144. package/components/ln-stat/ln-stat.js +1 -1
  145. package/components/ln-stat/ln-stat.schema.json +32 -32
  146. package/components/ln-stat/src/stat-model.js +28 -28
  147. package/components/ln-table/ln-table.js +1 -1
  148. package/components/ln-table/ln-table.schema.json +211 -211
  149. package/components/ln-table/src/table-model.js +98 -98
  150. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
  151. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -93
  152. package/components/ln-tabs/ln-tabs.js +1 -1
  153. package/components/ln-tabs/ln-tabs.schema.json +75 -75
  154. package/components/ln-tabs/src/ln-tabs.js +208 -208
  155. package/components/ln-tabs/src/tabs-model.js +82 -82
  156. package/components/ln-time/ln-time-dev.scss +13 -13
  157. package/components/ln-time/ln-time.js +1 -1
  158. package/components/ln-time/ln-time.schema.json +38 -38
  159. package/components/ln-toast/ln-toast.js +1 -1
  160. package/components/ln-toast/ln-toast.schema.json +65 -65
  161. package/components/ln-toggle/ln-toggle.js +1 -1
  162. package/components/ln-toggle/ln-toggle.schema.json +52 -52
  163. package/components/ln-tooltip/ln-tooltip.js +1 -1
  164. package/components/ln-tooltip/ln-tooltip.schema.json +86 -86
  165. package/components/ln-translations/ln-translations.js +1 -1
  166. package/components/ln-translations/ln-translations.schema.json +120 -120
  167. package/components/ln-ui-coordinator/README.md +62 -62
  168. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -1
  169. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +28 -28
  170. package/components/ln-ui-coordinator/src/ln-ui-coordinator.js +140 -140
  171. package/components/ln-upload/ln-upload.js +1 -1
  172. package/components/ln-upload/ln-upload.schema.json +193 -193
  173. package/components/ln-upload/src/upload-model.js +78 -78
  174. package/components/ln-validate/ln-validate.js +1 -1
  175. package/components/ln-validate/ln-validate.schema.json +50 -50
  176. package/components/ln-validate/src/validate-model.js +64 -64
  177. package/components/ln-websocket-connector/README.md +166 -166
  178. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -1
  179. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -29
  180. package/components/ln-websocket-connector/src/ln-websocket-connector.js +283 -279
  181. package/ln-ashlar.html-data.json +7 -8
  182. package/package.json +84 -84
  183. package/theme/brand.css +73 -73
  184. package/theme/components/_confirm.scss +6 -6
  185. package/theme/components/_picklist.scss +3 -3
  186. package/theme/config/_palette.scss +274 -274
  187. package/theme/config/_theme.scss +292 -292
  188. package/theme/config/_tokens.scss +285 -285
  189. package/theme/config/mixins/_index.scss +62 -62
  190. package/theme/config/mixins/_picklist.scss +66 -66
  191. package/theme/ln-ashlar-dev.scss +66 -66
  192. package/theme/ln-ashlar-landing.scss +56 -56
  193. package/theme/ln-ashlar-theme.scss +63 -63
  194. package/theme/presentation/_bento.scss +151 -151
  195. package/theme/presentation/_cta.scss +111 -111
  196. package/theme/presentation/_effects.scss +216 -216
  197. package/theme/presentation/_features.scss +108 -108
  198. package/theme/presentation/_footer.scss +129 -129
  199. package/theme/presentation/_hero.scss +158 -158
  200. package/theme/presentation/_layout.scss +170 -170
  201. package/theme/presentation/_navbar.scss +140 -140
  202. package/theme/presentation/_pricing.scss +262 -262
  203. package/theme/presentation/_stats.scss +87 -87
  204. package/theme/presentation/_testimonials.scss +124 -124
  205. package/theme/presentation/_tokens.scss +39 -39
  206. package/theme/presentation/_typography.scss +163 -163
  207. package/web-types.json +12 -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.