@swoff/cli 0.3.9 → 0.3.11

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 (216) hide show
  1. package/dist/__tests__/assemble-sw.test.js +8 -17
  2. package/dist/__tests__/assemble-sw.test.js.map +1 -1
  3. package/dist/__tests__/cli-integration.test.js +2 -2
  4. package/dist/__tests__/cli-integration.test.js.map +1 -1
  5. package/dist/__tests__/config-types.test.js +18 -8
  6. package/dist/__tests__/config-types.test.js.map +1 -1
  7. package/dist/__tests__/file-generators.test.js +3 -3
  8. package/dist/__tests__/file-generators.test.js.map +1 -1
  9. package/dist/__tests__/loader.test.js +5 -1
  10. package/dist/__tests__/loader.test.js.map +1 -1
  11. package/dist/__tests__/validator.test.js +6 -13
  12. package/dist/__tests__/validator.test.js.map +1 -1
  13. package/dist/index.js +41 -35
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/cli/help.js +13 -8
  16. package/dist/lib/cli/help.js.map +1 -1
  17. package/dist/lib/commands/add.js +38 -8
  18. package/dist/lib/commands/add.js.map +1 -1
  19. package/dist/lib/commands/assets.js +38 -0
  20. package/dist/lib/commands/assets.js.map +1 -0
  21. package/dist/lib/commands/clean.js +33 -17
  22. package/dist/lib/commands/clean.js.map +1 -1
  23. package/dist/lib/commands/generate-assets.js +44 -0
  24. package/dist/lib/commands/generate-assets.js.map +1 -0
  25. package/dist/lib/commands/generate-guide.js +22 -29
  26. package/dist/lib/commands/generate-guide.js.map +1 -1
  27. package/dist/lib/commands/generate.js +16 -9
  28. package/dist/lib/commands/generate.js.map +1 -1
  29. package/dist/lib/commands/info.js +36 -45
  30. package/dist/lib/commands/info.js.map +1 -1
  31. package/dist/lib/commands/init.js +25 -3
  32. package/dist/lib/commands/init.js.map +1 -1
  33. package/dist/lib/commands/validate.js +1 -1
  34. package/dist/lib/commands/validate.js.map +1 -1
  35. package/dist/lib/config/loader.js +25 -20
  36. package/dist/lib/config/loader.js.map +1 -1
  37. package/dist/lib/config/validator.js +20 -11
  38. package/dist/lib/config/validator.js.map +1 -1
  39. package/dist/lib/generators/asset-generator/dependency-resolver.js +76 -0
  40. package/dist/lib/generators/asset-generator/dependency-resolver.js.map +1 -0
  41. package/dist/lib/generators/asset-generator/generate.js +111 -0
  42. package/dist/lib/generators/asset-generator/generate.js.map +1 -0
  43. package/dist/lib/generators/asset-generator/guide.js +43 -0
  44. package/dist/lib/generators/asset-generator/guide.js.map +1 -0
  45. package/dist/lib/generators/asset-generator/html-patcher.js +65 -0
  46. package/dist/lib/generators/asset-generator/html-patcher.js.map +1 -0
  47. package/dist/lib/generators/asset-generator/ico-encoder.js +29 -0
  48. package/dist/lib/generators/asset-generator/ico-encoder.js.map +1 -0
  49. package/dist/lib/generators/asset-generator/manifest-patcher.js +23 -0
  50. package/dist/lib/generators/asset-generator/manifest-patcher.js.map +1 -0
  51. package/dist/lib/generators/asset-generator/maskable.js +15 -0
  52. package/dist/lib/generators/asset-generator/maskable.js.map +1 -0
  53. package/dist/lib/generators/asset-generator/rasterize.js +32 -0
  54. package/dist/lib/generators/asset-generator/rasterize.js.map +1 -0
  55. package/dist/lib/generators/asset-generator/sizes.js +25 -0
  56. package/dist/lib/generators/asset-generator/sizes.js.map +1 -0
  57. package/dist/lib/generators/file-generators/api-config.js +6 -0
  58. package/dist/lib/generators/file-generators/api-config.js.map +1 -0
  59. package/dist/lib/generators/file-generators/auth-state.js +2 -34
  60. package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
  61. package/dist/lib/generators/file-generators/auth-store.js +2 -250
  62. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  63. package/dist/lib/generators/file-generators/auth-user.js +3 -81
  64. package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
  65. package/dist/lib/generators/file-generators/background-sync.js +2 -57
  66. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  67. package/dist/lib/generators/file-generators/cache.js +2 -30
  68. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  69. package/dist/lib/generators/file-generators/client-injector.js +2 -130
  70. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  71. package/dist/lib/generators/file-generators/fetch-state.js +6 -0
  72. package/dist/lib/generators/file-generators/fetch-state.js.map +1 -0
  73. package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -295
  74. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  75. package/dist/lib/generators/file-generators/generate-framework-adapters.js +56 -0
  76. package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -0
  77. package/dist/lib/generators/file-generators/generate-hooks.js +4 -3
  78. package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
  79. package/dist/lib/generators/file-generators/gql-wrapper.js +3 -132
  80. package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
  81. package/dist/lib/generators/file-generators/guide-generator.js +29 -1
  82. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
  83. package/dist/lib/generators/file-generators/invalidation-tags.js +3 -258
  84. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  85. package/dist/lib/generators/file-generators/manifest.js +13 -3
  86. package/dist/lib/generators/file-generators/manifest.js.map +1 -1
  87. package/dist/lib/generators/file-generators/mutation-queue.js +3 -305
  88. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  89. package/dist/lib/generators/file-generators/mutation-state.js +2 -117
  90. package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
  91. package/dist/lib/generators/file-generators/push.js +6 -138
  92. package/dist/lib/generators/file-generators/push.js.map +1 -1
  93. package/dist/lib/generators/file-generators/pwa-install.js +11 -84
  94. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  95. package/dist/lib/generators/file-generators/reset.js +6 -0
  96. package/dist/lib/generators/file-generators/reset.js.map +1 -0
  97. package/dist/lib/generators/file-generators/server-push.js +3 -134
  98. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  99. package/dist/lib/generators/file-generators/sw-generator-build.js +4 -4
  100. package/dist/lib/generators/file-generators/sw-injector.js +9 -230
  101. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  102. package/dist/lib/generators/file-generators/type-definitions.js +2 -111
  103. package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
  104. package/dist/lib/generators/sw-generator.js +5 -5
  105. package/dist/lib/generators/sw-generator.js.map +1 -1
  106. package/dist/lib/generators/sw-sections/activate-handler.js +25 -3
  107. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  108. package/dist/lib/generators/sw-sections/assemble-sw.js +13 -12
  109. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  110. package/dist/lib/generators/sw-sections/background-sync-handler.js +14 -7
  111. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  112. package/dist/lib/generators/sw-sections/config-header.js +1 -1
  113. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  114. package/dist/lib/generators/sw-sections/fetch-handler.js +165 -104
  115. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  116. package/dist/lib/generators/sw-sections/glob-matcher-code.js.map +1 -0
  117. package/dist/lib/generators/sw-sections/server-push-handler.js +12 -0
  118. package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
  119. package/dist/lib/generators/sw-sections/tag-management.js +6 -4
  120. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  121. package/dist/lib/generators/swoff-files-generator.js +66 -35
  122. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  123. package/dist/lib/shared/config-types.js +89 -28
  124. package/dist/lib/shared/config-types.js.map +1 -1
  125. package/dist/runtime/api-config.js +18 -0
  126. package/dist/runtime/api-config.js.map +1 -0
  127. package/dist/runtime/auth-state.js +35 -0
  128. package/dist/runtime/auth-state.js.map +1 -0
  129. package/dist/runtime/auth-store.js +273 -0
  130. package/dist/runtime/auth-store.js.map +1 -0
  131. package/dist/runtime/auth-user.js +168 -0
  132. package/dist/runtime/auth-user.js.map +1 -0
  133. package/dist/runtime/background-sync.js +59 -0
  134. package/dist/runtime/background-sync.js.map +1 -0
  135. package/dist/runtime/cache.js +30 -0
  136. package/dist/runtime/cache.js.map +1 -0
  137. package/dist/runtime/client-injector.js +136 -0
  138. package/dist/runtime/client-injector.js.map +1 -0
  139. package/dist/runtime/fetch-state.js +42 -0
  140. package/dist/runtime/fetch-state.js.map +1 -0
  141. package/dist/runtime/fetch-wrapper.js +355 -0
  142. package/dist/runtime/fetch-wrapper.js.map +1 -0
  143. package/dist/runtime/gql-wrapper.js +135 -0
  144. package/dist/runtime/gql-wrapper.js.map +1 -0
  145. package/dist/runtime/invalidation-tags.js +260 -0
  146. package/dist/runtime/invalidation-tags.js.map +1 -0
  147. package/dist/runtime/mutation-queue.js +304 -0
  148. package/dist/runtime/mutation-queue.js.map +1 -0
  149. package/dist/runtime/mutation-state.js +117 -0
  150. package/dist/runtime/mutation-state.js.map +1 -0
  151. package/dist/runtime/push.js +138 -0
  152. package/dist/runtime/push.js.map +1 -0
  153. package/dist/runtime/pwa-index.js +11 -0
  154. package/dist/runtime/pwa-index.js.map +1 -0
  155. package/dist/runtime/pwa-injector.js +44 -0
  156. package/dist/runtime/pwa-injector.js.map +1 -0
  157. package/dist/runtime/pwa-install.js +83 -0
  158. package/dist/runtime/pwa-install.js.map +1 -0
  159. package/dist/runtime/pwa-prompt.js +54 -0
  160. package/dist/runtime/pwa-prompt.js.map +1 -0
  161. package/dist/runtime/reset.js +148 -0
  162. package/dist/runtime/reset.js.map +1 -0
  163. package/dist/runtime/server-push.js +148 -0
  164. package/dist/runtime/server-push.js.map +1 -0
  165. package/dist/runtime/sw-injector.js +236 -0
  166. package/dist/runtime/sw-injector.js.map +1 -0
  167. package/dist/runtime/type-definitions.js +138 -0
  168. package/dist/runtime/type-definitions.js.map +1 -0
  169. package/dist/runtime/utils.js +27 -0
  170. package/dist/runtime/utils.js.map +1 -0
  171. package/package.json +3 -2
  172. package/templates/react/useAuth.jsx +106 -0
  173. package/templates/react/useAuth.tsx +118 -0
  174. package/templates/{hooks → react}/useBackgroundSync.jsx +6 -4
  175. package/templates/{hooks → react}/useBackgroundSync.tsx +6 -4
  176. package/templates/{hooks → react}/useCacheInvalidation.jsx +2 -2
  177. package/templates/react/useCacheInvalidation.tsx +52 -0
  178. package/templates/react/useCachedFetch.jsx +140 -0
  179. package/templates/react/useCachedFetch.tsx +160 -0
  180. package/templates/react/useIsFetching.jsx +32 -0
  181. package/templates/react/useIsFetching.tsx +32 -0
  182. package/templates/react/useMutation.jsx +141 -0
  183. package/templates/react/useMutation.tsx +180 -0
  184. package/templates/react/useMutationQueue.jsx +69 -0
  185. package/templates/react/useMutationQueue.tsx +83 -0
  186. package/templates/react/useNetworkStatus.jsx +61 -0
  187. package/templates/react/useNetworkStatus.tsx +76 -0
  188. package/templates/react/usePrefetch.jsx +32 -0
  189. package/templates/react/usePrefetch.tsx +42 -0
  190. package/templates/{hooks → react}/usePushSubscription.jsx +4 -4
  191. package/templates/{hooks → react}/usePushSubscription.tsx +4 -4
  192. package/templates/{hooks → react}/useSWUpdate.jsx +3 -3
  193. package/templates/{hooks → react}/useSWUpdate.tsx +4 -4
  194. package/templates/react/useSwoffReset.jsx +32 -0
  195. package/templates/react/useSwoffReset.tsx +39 -0
  196. package/docs/API.md +0 -565
  197. package/docs/ARCHITECTURE.md +0 -374
  198. package/docs/CLI.md +0 -148
  199. package/docs/COMPARISON.md +0 -192
  200. package/docs/CONFIG.md +0 -277
  201. package/docs/ECOSYSTEM.md +0 -33
  202. package/templates/hooks/useAuth.jsx +0 -30
  203. package/templates/hooks/useAuth.tsx +0 -30
  204. package/templates/hooks/useCacheInvalidation.tsx +0 -19
  205. package/templates/hooks/useCachedFetch.jsx +0 -69
  206. package/templates/hooks/useCachedFetch.tsx +0 -81
  207. package/templates/hooks/useMutation.jsx +0 -76
  208. package/templates/hooks/useMutation.tsx +0 -109
  209. package/templates/hooks/useMutationQueue.jsx +0 -43
  210. package/templates/hooks/useMutationQueue.tsx +0 -50
  211. package/templates/hooks/useNetworkStatus.jsx +0 -19
  212. package/templates/hooks/useNetworkStatus.tsx +0 -19
  213. package/templates/hooks/usePrefetch.jsx +0 -10
  214. package/templates/hooks/usePrefetch.tsx +0 -25
  215. /package/templates/{hooks → react}/useMutationState.jsx +0 -0
  216. /package/templates/{hooks → react}/useMutationState.tsx +0 -0
@@ -3,91 +3,10 @@
3
3
  * configurable glob patterns, prefix skipping, singularization, and cascading.
4
4
  */
5
5
  import { writeFile } from "./context.js";
6
- function escapeRegex(str) {
7
- return str.replace(/[.+^${}()|[\]\\]/g, "\\$&");
8
- }
9
- function compilePatternEntry(rawPattern, tagTemplates) {
10
- const rawParts = rawPattern.split("/");
11
- const parts = rawParts.filter((p) => p !== "");
12
- const paramNames = [];
13
- const regexParts = [];
14
- for (let i = 0; i < parts.length; i++) {
15
- const part = parts[i];
16
- if (part === "**") {
17
- regexParts.push("(?:\\/[^/]+)*");
18
- continue;
19
- }
20
- if (regexParts.length > 0) {
21
- regexParts.push("\\/");
22
- }
23
- if (part === "*") {
24
- regexParts.push("[^/]+");
25
- }
26
- else if (part.startsWith(":")) {
27
- paramNames.push(part.slice(1));
28
- regexParts.push("([^/]+)");
29
- }
30
- else if (part.includes("{")) {
31
- const close = part.indexOf("}");
32
- if (close !== -1) {
33
- const inner = part.slice(part.indexOf("{") + 1, close);
34
- const alternatives = inner.split(",").map((s) => escapeRegex(s.trim()));
35
- regexParts.push("(?:" + alternatives.join("|") + ")");
36
- }
37
- else {
38
- regexParts.push(escapeRegex(part));
39
- }
40
- }
41
- else {
42
- regexParts.push(escapeRegex(part));
43
- }
44
- }
45
- if (regexParts.length === 0)
46
- return null;
47
- const hasLeadingSlash = rawPattern.startsWith("/");
48
- const regex = (hasLeadingSlash ? "^\\/" : "^") + regexParts.join("") + "$";
49
- // Validate template params match captured params
50
- const templatePlaceholders = new Set();
51
- for (const tmpl of tagTemplates) {
52
- const matches = tmpl.match(/\{(\w+)\}/g);
53
- if (matches) {
54
- for (const m of matches) {
55
- templatePlaceholders.add(m.slice(1, -1));
56
- }
57
- }
58
- }
59
- for (const ph of templatePlaceholders) {
60
- if (!paramNames.includes(ph)) {
61
- return null;
62
- }
63
- }
64
- return { regex, params: paramNames, templates: tagTemplates };
65
- }
66
- function generatePatternCode(patterns) {
67
- const entries = [];
68
- for (const [pattern, templates] of Object.entries(patterns)) {
69
- const compiled = compilePatternEntry(pattern, templates);
70
- if (compiled) {
71
- entries.push(` { re: new RegExp("${compiled.regex.replace(/\\/g, "\\\\")}"), params: ${JSON.stringify(compiled.params)}, templates: ${JSON.stringify(compiled.templates)} }`);
72
- }
73
- }
74
- return "[\n" + entries.join(",\n") + "\n]";
75
- }
76
- function generateSingularizationCode(singularization) {
77
- if (!singularization || Object.keys(singularization).length === 0)
78
- return "null";
79
- return JSON.stringify(singularization);
80
- }
81
- function generateCascadingCode(cascading) {
82
- if (!cascading || Object.keys(cascading).length === 0)
83
- return "null";
84
- return JSON.stringify(cascading);
85
- }
6
+ import { generateInvalidationTagsCode } from "../../../runtime/invalidation-tags.js";
86
7
  export function generateInvalidationTags(ctx) {
87
8
  const ext = ctx.ext;
88
9
  const ts = ext === "ts";
89
- const T = (type) => (ts ? `: ${type}` : "");
90
- const R = (type) => (ts ? `: ${type} ` : " ");
91
10
  const tiConfig = ctx.config.features.tagInvalidation;
92
11
  const prefixes = tiConfig.prefixes ?? [
93
12
  "api",
@@ -101,181 +20,7 @@ export function generateInvalidationTags(ctx) {
101
20
  const patterns = tiConfig.patterns ?? {};
102
21
  const singularization = tiConfig.singularization ?? {};
103
22
  const cascading = tiConfig.cascading ?? {};
104
- const patternCode = generatePatternCode(patterns);
105
- const singularizationCode = generateSingularizationCode(singularization);
106
- const cascadingCode = generateCascadingCode(cascading);
107
- const prefixesCode = JSON.stringify(prefixes);
108
- const code = `/**
109
- * Swoff Invalidation Tags Helper
110
- * Pattern-based tag generation from URLs with configurable glob patterns,
111
- * prefix skipping, singularization, and cascading invalidation.
112
- *
113
- * Usage:
114
- * import { generateTags, invalidateUrl } from './swoff/invalidation-tags.${ext}';
115
- *
116
- * // Generate tags from URL
117
- * generateTags("/api/todos"); // ["todos"]
118
- * generateTags("/api/todos/42"); // ["todos", "todo:42"]
119
- *
120
- * // Use with fetch wrapper
121
- * const data = await fetchWithCache("/api/todos", {
122
- * tags: generateTags("/api/todos"),
123
- * });
124
- *
125
- * // Invalidate after mutation
126
- * await invalidateUrl("/api/todos/42");
127
- */
128
-
129
- import { invalidateByTags } from "./cache.${ext}";
130
- ${ts ? `
131
- interface TagPattern {
132
- re: RegExp;
133
- params: string[];
134
- templates: string[];
135
- }
136
- ` : ""}
137
- // Pattern entries compiled from swoff.config.json
138
- const TAG_PATTERNS${T("TagPattern[]")} = ${patternCode};
139
-
140
- // URL path prefixes to skip during tag generation
141
- const SKIP_PREFIXES = ${prefixesCode};
142
-
143
- // Custom singularization map (plural → singular)
144
- const SINGULARIZATION${T("Record<string, string> | null")} = ${singularizationCode};
145
-
146
- // Cascading invalidation map (tag → dependent tags)
147
- const CASCADING${T("Record<string, string[]> | null")} = ${cascadingCode};
148
-
149
- /** Generate cache invalidation tags from a URL path. Tries configured patterns first, falls back to segment-based generation. */
150
- export function generateTags(url${T("string | URL")})${R("string[]")}{
151
- const parsed = typeof url === "string" ? new URL(url, window.location.origin) : url;
152
- const path = parsed.pathname;
153
-
154
- // Try configured patterns first
155
- for (const entry of TAG_PATTERNS) {
156
- const match = path.match(entry.re);
157
- if (match) {
158
- const params${T("Record<string, string>")} = {};
159
- for (let i = 0; i < entry.params.length; i++) {
160
- params[entry.params[i]] = match[i + 1];
161
- }
162
- return entry.templates.map((tmpl) => {
163
- return tmpl.replace(/\\{(\\w+)\\}/g, (_, key) => params[key] ?? "");
164
- });
165
- }
166
- }
167
-
168
- // Fallback: segment-based generation
169
- const segments = path.split("/").filter(Boolean);
170
- if (segments.length === 0) return ["root"];
171
-
172
- let startIdx = 0;
173
- while (startIdx < segments.length && SKIP_PREFIXES.includes(segments[startIdx])) {
174
- startIdx++;
175
- }
176
-
177
- const resourceSegments = segments.slice(startIdx);
178
- if (resourceSegments.length === 0) return ["root"];
179
-
180
- const tags = [];
181
- tags.push(resourceSegments[0]);
182
-
183
- if (resourceSegments.length >= 2 && !isNaN(Number(resourceSegments[1]))) {
184
- const collection = resourceSegments[0];
185
- const id = resourceSegments[1];
186
- const singular = SINGULARIZATION && SINGULARIZATION[collection] !== undefined
187
- ? SINGULARIZATION[collection]
188
- : collection.replace(/s$/, "");
189
- tags.push(\`\${singular}:\${id}\`);
190
- }
191
-
192
- for (let i = 2; i < resourceSegments.length; i++) {
193
- if (isNaN(Number(resourceSegments[i]))) {
194
- tags.push(resourceSegments[i]);
195
- }
196
- }
197
-
198
- return tags;
199
- }
200
-
201
- /** Generate tags prefixed by HTTP method. e.g. POST /api/todos → ["post-todos"]. */
202
- export function generateTagsFromMethod(method${T("string")}, url${T("string | URL")})${R("string[]")}{
203
- const tags = generateTags(url);
204
- if (method === "GET" || method === "HEAD") return tags;
205
- return tags.map((tag) => \`\${method.toLowerCase()}-\${tag}\`);
206
- }
207
-
208
- /** Invalidate cached responses related to a URL, including cascading dependencies. */
209
- export async function invalidateUrl(url${T("string | URL")})${R("Promise<void>")}{
210
- const tags = generateTags(url);
211
- const allTags = CASCADING ? expandCascading(tags) : tags;
212
- await invalidateByTags(allTags);
213
- }
214
-
215
- /** Invalidate cached responses tagged with method-prefixed tags, including cascading dependencies. */
216
- export async function invalidateByMethod(method${T("string")}, url${T("string | URL")})${R("Promise<void>")}{
217
- const tags = generateTagsFromMethod(method, url);
218
- const allTags = CASCADING ? expandCascading(tags) : tags;
219
- await invalidateByTags(allTags);
220
- }
221
-
222
- /** Expand tags with their cascading dependencies, deduplicated. */
223
- export function expandCascading(tags${T("string[]")})${R("string[]")}{
224
- if (!CASCADING) return [...tags];
225
- const result = new Set(tags);
226
- for (const tag of tags) {
227
- const deps = CASCADING[tag];
228
- if (deps) {
229
- for (const dep of deps) {
230
- result.add(dep);
231
- }
232
- }
233
- }
234
- return [...result];
235
- }
236
-
237
- /** Introspect: get all URLs cached under a given tag. */
238
- export async function getUrlsForTag(tag${T("string")})${R("Promise<{ url: string; actualUrl: string }[]>")}{
239
- const controller = navigator.serviceWorker?.controller;
240
- if (!controller) return [];
241
- return new Promise((resolve) => {
242
- const channel = new MessageChannel();
243
- channel.port1.onmessage = (event) => {
244
- resolve(event.data.urls || []);
245
- };
246
- controller.postMessage(
247
- { type: "GET_URLS_FOR_TAG", tag },
248
- [channel.port2],
249
- );
250
- });
251
- }
252
-
253
- /** Introspect: get all tags associated with a given URL. */
254
- export async function getTagsForUrl(url${T("string")})${R("Promise<string[]>")}{
255
- const controller = navigator.serviceWorker?.controller;
256
- if (!controller) return [];
257
- return new Promise((resolve) => {
258
- const channel = new MessageChannel();
259
- channel.port1.onmessage = (event) => {
260
- resolve(event.data.tags || []);
261
- };
262
- controller.postMessage(
263
- { type: "GET_TAGS_FOR_URL", url },
264
- [channel.port2],
265
- );
266
- });
267
- }
268
-
269
- /** Invalidate all cached responses whose URL matches the given glob pattern. */
270
- export async function invalidateMatching(glob${T("string")})${R("Promise<void>")}{
271
- const controller = navigator.serviceWorker?.controller;
272
- if (!controller) return;
273
- controller.postMessage({
274
- type: "INVALIDATE_MATCHING",
275
- glob,
276
- });
277
- }
278
- `;
279
- writeFile(ctx, `invalidation-tags.${ext}`, code);
23
+ const code = generateInvalidationTagsCode({ ts, ext }, prefixes, patterns, singularization, cascading);
24
+ writeFile(ctx, `cache/tags.${ext}`, code);
280
25
  }
281
26
  //# sourceMappingURL=invalidation-tags.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/invalidation-tags.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/invalidation-tags.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAErF,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;IAErD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI;QACpC,KAAK;QACL,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,MAAM;QACN,SAAS;QACT,KAAK;KACN,CAAC;IACF,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC;IACzC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,IAAI,EAAE,CAAC;IACvD,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;IAE3C,MAAM,IAAI,GAAG,4BAA4B,CACvC,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,SAAS,CACV,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC"}
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * Generates manifest.json - PWA web app manifest.
3
+ * Reads icon paths from config if assets were generated, otherwise uses defaults.
3
4
  */
4
5
  import { ensureDir } from "./context.js";
5
6
  import { existsSync, writeFileSync } from "fs";
@@ -10,14 +11,16 @@ export function generateManifest(ctx) {
10
11
  const manifestPath = join(outputDir, "manifest.json");
11
12
  if (existsSync(manifestPath))
12
13
  return;
14
+ const pwaAssets = ctx.config.features.pwa.assets;
15
+ const hasGeneratedSplash = pwaAssets.source && pwaAssets.generated;
13
16
  const manifest = {
14
17
  name: "Swoff App",
15
18
  short_name: "Swoff",
16
19
  description: "Offline-first web application",
17
20
  start_url: "/",
18
21
  display: "standalone",
19
- background_color: "#ffffff",
20
- theme_color: "#000000",
22
+ background_color: pwaAssets.bgColor || "#ffffff",
23
+ theme_color: pwaAssets.themeColor || "#000000",
21
24
  orientation: "portrait-primary",
22
25
  scope: "/",
23
26
  lang: "en-US",
@@ -25,11 +28,18 @@ export function generateManifest(ctx) {
25
28
  prefer_related_applications: false,
26
29
  display_override: ["window-controls-overlay", "standalone", "browser"],
27
30
  icons: [
31
+ { src: "/icon-64.png", sizes: "64x64", type: "image/png", purpose: "any" },
28
32
  { src: "/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any" },
29
- { src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any maskable" },
33
+ { src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any" },
34
+ { src: "/maskable-icon-512.png", sizes: "512x512", type: "image/png", purpose: "maskable" },
30
35
  ],
31
36
  screenshots: [],
32
37
  };
38
+ if (hasGeneratedSplash) {
39
+ manifest.screenshots = [
40
+ { src: "/og-image.png", sizes: "1200x630", type: "image/png", form_factor: "narrow", label: "App screenshot" },
41
+ ];
42
+ }
33
43
  writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
34
44
  ctx.generatedFiles.push("public/manifest.json");
35
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/manifest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAClD,SAAS,CAAC,SAAS,CAAC,CAAC;IAErB,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACtD,IAAI,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO;IAErC,MAAM,QAAQ,GAAG;QACf,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,OAAO;QACnB,WAAW,EAAE,+BAA+B;QAC5C,SAAS,EAAE,GAAG;QACd,OAAO,EAAE,YAAY;QACrB,gBAAgB,EAAE,SAAS;QAC3B,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EAAE,GAAG;QACV,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC;QAChC,2BAA2B,EAAE,KAAK;QAClC,gBAAgB,EAAE,CAAC,yBAAyB,EAAE,YAAY,EAAE,SAAS,CAAC;QACtE,KAAK,EAAE;YACL,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE;YAC7E,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE;SACvF;QACD,WAAW,EAAE,EAAE;KAChB,CAAC;IAEF,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/D,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/manifest.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAClD,SAAS,CAAC,SAAS,CAAC,CAAC;IAErB,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACtD,IAAI,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO;IAErC,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;IACjD,MAAM,kBAAkB,GAAG,SAAS,CAAC,MAAM,IAAI,SAAS,CAAC,SAAS,CAAC;IAEnE,MAAM,QAAQ,GAA4B;QACxC,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,OAAO;QACnB,WAAW,EAAE,+BAA+B;QAC5C,SAAS,EAAE,GAAG;QACd,OAAO,EAAE,YAAY;QACrB,gBAAgB,EAAE,SAAS,CAAC,OAAO,IAAI,SAAS;QAChD,WAAW,EAAE,SAAS,CAAC,UAAU,IAAI,SAAS;QAC9C,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EAAE,GAAG;QACV,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC;QAChC,2BAA2B,EAAE,KAAK;QAClC,gBAAgB,EAAE,CAAC,yBAAyB,EAAE,YAAY,EAAE,SAAS,CAAC;QACtE,KAAK,EAAE;YACL,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE;YAC1E,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE;YAC7E,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE;YAC7E,EAAE,GAAG,EAAE,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE;SAC5F;QACD,WAAW,EAAE,EAAE;KAChB,CAAC;IAEF,IAAI,kBAAkB,EAAE,CAAC;QACvB,QAAQ,CAAC,WAAW,GAAG;YACrB,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE;SAC/G,CAAC;IACJ,CAAC;IAED,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/D,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAClD,CAAC"}
@@ -1,313 +1,11 @@
1
1
  import { writeFile } from "./context.js";
2
+ import { generateMutationQueueCode } from "../../../runtime/mutation-queue.js";
2
3
  export function generateMutationQueue(ctx) {
3
4
  const ext = ctx.ext;
4
5
  const ts = ext === "ts";
5
- const T = (type) => (ts ? `: ${type}` : "");
6
- const R = (type) => (ts ? `: ${type} ` : " ");
7
- const PT = (type) => (ts ? `<${type}>` : "");
8
- const AS = (type) => (ts ? ` as ${type}` : "");
9
6
  const authEnabled = ctx.config.features.auth.enabled;
10
- const tagInvalidation = ctx.config.features.tagInvalidation.enabled;
11
7
  const mqConfig = ctx.config.features.mutationQueue;
12
- const batchSize = mqConfig.batchSize;
13
- const batchDelayMs = mqConfig.batchDelayMs;
14
- const maxRetries = mqConfig.maxRetries;
15
- const retryBackoffMs = mqConfig.retryBackoffMs;
16
- const importLines = authEnabled
17
- ? `import { getAuth } from "./auth/store.${ext}";
18
- `
19
- : "";
20
- const invalidateImport = tagInvalidation
21
- ? `import { invalidateByTags } from "./cache.${ext}";
22
- `
23
- : "";
24
- const additionalImports = `${invalidateImport}${ts
25
- ? `import type { MutationQueueItem } from "./swoff.d.ts";
26
- `
27
- : ""}`;
28
- const authReplayHeaders = authEnabled
29
- ? ` const auth = await getAuth();
30
- const authHeader${T("Record<string, string>")} = auth?.token ? { Authorization: \`Bearer \${auth.token}\` } : {};
31
- `
32
- : "";
33
- const authHeaderSpread = authEnabled
34
- ? ` ...authHeader,\n`
35
- : " ";
36
- const code = `/**
37
- * Swoff Mutation Queue
38
- * Queue offline writes and sync when connection returns.
39
- * Supports configurable batch size, rate limiting, and exponential backoff.
40
- *
41
- * Usage:
42
- * import { queueMutation, processMutationQueue, flushMutations, getPendingCount } from './swoff/mutation-queue.${ext}';
43
- *
44
- * // Queue a mutation
45
- * await queueMutation({
46
- * method: "POST",
47
- * url: "/api/todos",
48
- * body: { title: "Grocery" },
49
- * tags: ["todos"],
50
- * });
51
- *
52
- * // Flush queued mutations (e.g., after login)
53
- * await flushMutations();
54
- *
55
- * // Auto-processes on online event
56
- *
57
- * Config:
58
- * batchSize: ${batchSize} — mutations per progress event
59
- * batchDelayMs: ${batchDelayMs} — delay between mutations (rate limiting)
60
- * maxRetries: ${maxRetries} — max retries before dropping
61
- * retryBackoffMs: ${retryBackoffMs} — exponential backoff base
62
- */
63
-
64
- ${importLines}${additionalImports}const DB_NAME = "swoff-queue";
65
- const STORE_NAME = "mutations";
66
- const BATCH_SIZE = ${batchSize};
67
- const BATCH_DELAY_MS = ${batchDelayMs};
68
- const MAX_RETRIES = ${maxRetries};
69
- const RETRY_BACKOFF_MS = ${retryBackoffMs};
70
-
71
- function sleep(ms${T("number")})${R("Promise<void>")}{
72
- return new Promise((r) => setTimeout(r, ms));
73
- }
74
-
75
- function openQueueDB()${R("Promise<IDBDatabase>")}{
76
- return new Promise${PT("IDBDatabase")}((resolve, reject) => {
77
- const request = indexedDB.open(DB_NAME, 1);
78
- request.onupgradeneeded = (e) => {
79
- const db = (e.target${AS("IDBOpenDBRequest")}).result;
80
- if (!db.objectStoreNames.contains(STORE_NAME)) {
81
- const store = db.createObjectStore(STORE_NAME, { keyPath: "id" });
82
- store.createIndex("by-timestamp", "timestamp");
83
- }
84
- };
85
- request.onsuccess = (e) => resolve((e.target${AS("IDBOpenDBRequest")}).result);
86
- request.onerror = (e) => reject((e.target${AS("IDBRequest")}).error);
87
- });
88
- }
89
-
90
- let isSyncing = false;
91
-
92
- /** Store a write operation in IndexedDB for later sync. Works offline — use it for POST/PUT/PATCH/DELETE when the user might be offline. */
93
- export async function queueMutation(mutation${T("Partial<MutationQueueItem>")})${R("Promise<void>")}{
94
- const db = await openQueueDB();
95
- const tx = db.transaction(STORE_NAME, "readwrite");
96
- const store = tx.objectStore(STORE_NAME);
97
-
98
- let body = mutation.body;
99
- let bodyType = "json";
100
- if (body instanceof FormData) {
101
- bodyType = "formdata";
102
- body = [...body.entries()];
103
- } else if (body instanceof Blob) {
104
- bodyType = "blob";
105
- } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
106
- bodyType = "buffer";
107
- }
108
-
109
- // Strip auth headers before storing — tokens must never persist in IDB
110
- const safeHeaders${T("Record<string, string>")} = { ...(mutation.headers || {}) };
111
- delete safeHeaders["authorization"];
112
- delete safeHeaders["Authorization"];
113
-
114
- store.add({
115
- id: crypto.randomUUID(),
116
- method: mutation.method,
117
- url: mutation.url,
118
- body,
119
- bodyType,
120
- headers: safeHeaders,
121
- timestamp: Date.now(),
122
- retryCount: 0,
123
- nextRetryAt: 0,
124
- tags: mutation.tags || [],
125
- });
126
-
127
- await new Promise<void>((resolve, reject) => {
128
- tx.oncomplete = () => resolve();
129
- tx.onerror = () => reject(tx.error);
130
- });
131
-
132
- window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
133
- }
134
-
135
- /** Replay a single queued mutation. Returns true on success, false on failure. */
136
- async function replayMutation(item${T("MutationQueueItem")})${R("Promise<boolean>")}{
137
- try {
138
- ${authReplayHeaders} let replayBody${T("BodyInit | null")}${ts ? " = null" : ""};
139
- let contentType${T("string | undefined")};
140
- const bt = item.bodyType || "json";
141
- if (bt === "formdata") {
142
- replayBody = new FormData();
143
- for (const [key, value] of (item.body || [])${AS("[string, FormDataEntryValue][]")}) {
144
- replayBody.append(key, value);
145
- }
146
- } else if (bt === "blob") {
147
- replayBody = item.body${AS("BodyInit | null")};
148
- } else if (bt === "buffer") {
149
- replayBody = item.body instanceof ArrayBuffer ? new Uint8Array(item.body)${AS("BodyInit")} : item.body${AS("BodyInit")};
150
- } else {
151
- replayBody = JSON.stringify(item.body);
152
- contentType = "application/json";
153
- }
154
- const response = await fetch(item.url, {
155
- method: item.method,
156
- headers: {
157
- ...(contentType ? { "Content-Type": contentType } : {}),
158
- ...item.headers,
159
- ${authHeaderSpread} },
160
- body: replayBody,
161
- });
162
-
163
- if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
164
-
165
- if (item.tags && item.tags.length > 0) {
166
- ${tagInvalidation ? "await invalidateByTags(item.tags);" : "// tagInvalidation disabled — skipping cache invalidation"}
167
- }
168
-
169
- await removeFromQueue(item.id);
170
- return true;
171
- } catch {
172
- item.retryCount++;
173
- item.nextRetryAt = Date.now() + RETRY_BACKOFF_MS * Math.pow(2, item.retryCount - 1);
174
- await updateInQueue(item);
175
- return false;
176
- }
177
- }
178
-
179
- /** Process all queued mutations in order. Sends them to the server. Runs automatically when mutations are queued or on reconnection. Respects batchSize for progress reporting and batchDelayMs for rate limiting. */
180
- export async function processMutationQueue()${R("Promise<void>")}{
181
- if (isSyncing) return;
182
- isSyncing = true;
183
-
184
- try {
185
- const db = await openQueueDB();
186
- const tx = db.transaction(STORE_NAME, "readonly");
187
- const store = tx.objectStore(STORE_NAME);
188
- const index = store.index("by-timestamp");
189
- const queue${T("MutationQueueItem[]")} = await new Promise${PT("MutationQueueItem[]")}((resolve, reject) => {
190
- const request = index.getAll();
191
- request.onsuccess = () => resolve(request.result);
192
- request.onerror = () => reject(request.error);
193
- });
194
-
195
- if (queue.length === 0) return;
196
-
197
- let succeeded = 0;
198
- let failed = 0;
199
- const total = queue.length;
200
- let earliestRetry = Infinity;
201
-
202
- for (const item of queue) {
203
- if (item.retryCount >= MAX_RETRIES) {
204
- await removeFromQueue(item.id);
205
- failed++;
206
- continue;
207
- }
208
-
209
- // Skip items whose backoff delay hasn't elapsed yet
210
- if (item.nextRetryAt && Date.now() < item.nextRetryAt) {
211
- if (item.nextRetryAt < earliestRetry) earliestRetry = item.nextRetryAt;
212
- continue;
213
- }
214
-
215
- const ok = await replayMutation(item);
216
- if (ok) {
217
- succeeded++;
218
- } else {
219
- failed++;
220
- }
221
-
222
- // Rate limiting delay between mutations
223
- if (BATCH_DELAY_MS > 0 && succeeded + failed < total) {
224
- await sleep(BATCH_DELAY_MS);
225
- }
226
-
227
- // Emit progress after every BATCH_SIZE mutations
228
- if ((succeeded + failed) % BATCH_SIZE === 0 || succeeded + failed === total) {
229
- window.dispatchEvent(
230
- new CustomEvent("mutation-sync-complete", {
231
- detail: { succeeded, failed, total, current: succeeded + failed },
232
- })
233
- );
234
- }
235
- }
236
-
237
- window.dispatchEvent(
238
- new CustomEvent("mutation-sync-complete", {
239
- detail: { succeeded, failed, total },
240
- })
241
- );
242
-
243
- // Schedule retry when the earliest backoff timer expires
244
- if (earliestRetry < Infinity && earliestRetry > Date.now()) {
245
- setTimeout(() => {
246
- if (!isSyncing) processMutationQueue();
247
- }, earliestRetry - Date.now());
248
- }
249
- } finally {
250
- isSyncing = false;
251
- window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
252
- }
253
- }
254
-
255
- /** Immediately process all queued mutations. Call this after re-login to flush mutations that failed due to auth expiry. */
256
- export async function flushMutations()${R("Promise<void>")}{
257
- await processMutationQueue();
258
- }
259
- `;
260
- const helpers = `
261
- async function removeFromQueue(id${T("string")})${R("Promise<void>")}{
262
- const db = await openQueueDB();
263
- const tx = db.transaction(STORE_NAME, "readwrite");
264
- tx.objectStore(STORE_NAME).delete(id);
265
- await new Promise${PT("void")}((resolve, reject) => {
266
- tx.oncomplete = () => resolve();
267
- tx.onerror = () => reject(tx.error);
268
- });
269
- }
270
-
271
- async function updateInQueue(item${T("MutationQueueItem")})${R("Promise<void>")}{
272
- const db = await openQueueDB();
273
- const tx = db.transaction(STORE_NAME, "readwrite");
274
- tx.objectStore(STORE_NAME).put(item);
275
- await new Promise${PT("void")}((resolve, reject) => {
276
- tx.oncomplete = () => resolve();
277
- tx.onerror = () => reject(tx.error);
278
- });
279
- }
280
-
281
- /** Get the number of queued mutations waiting to be synced. Useful for showing a sync badge. */
282
- export async function getPendingCount()${R("Promise<number>")}{
283
- const db = await openQueueDB();
284
- return new Promise${PT("number")}((resolve, reject) => {
285
- const tx = db.transaction(STORE_NAME, "readonly");
286
- const request = tx.objectStore(STORE_NAME).count();
287
- request.onsuccess = () => resolve((request${AS("IDBRequest<number>")}).result);
288
- request.onerror = () => reject((request${AS("IDBRequest")}).error);
289
- });
290
- }
291
-
292
- /** Get the position of a mutation in the queue (0-based). Returns -1 if not found. */
293
- export async function getQueuePosition(id${T("string")})${R("Promise<number>")}{
294
- const items = await getQueueItems();
295
- return items.findIndex((item) => item.id === id);
296
- }
297
-
298
- /** Get all pending queue items with their details. */
299
- export async function getQueueItems()${R("Promise<MutationQueueItem[]>")}{
300
- const db = await openQueueDB();
301
- const tx = db.transaction(STORE_NAME, "readonly");
302
- const store = tx.objectStore(STORE_NAME);
303
- const index = store.index("by-timestamp");
304
- return new Promise${PT("MutationQueueItem[]")}((resolve, reject) => {
305
- const request = index.getAll();
306
- request.onsuccess = () => resolve(request.result);
307
- request.onerror = () => reject(request.error);
308
- });
309
- }
310
- `;
311
- writeFile(ctx, `mutation-queue.${ext}`, code + helpers);
8
+ const code = generateMutationQueueCode({ ts, ext }, authEnabled, mqConfig.batchSize, mqConfig.batchDelayMs, mqConfig.maxRetries, mqConfig.retryBackoffMs);
9
+ writeFile(ctx, `offline/queue.${ext}`, code);
312
10
  }
313
11
  //# sourceMappingURL=mutation-queue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/mutation-queue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/mutation-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAE/E,MAAM,UAAU,qBAAqB,CAAC,GAAqB;IACzD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;IACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;IAEnD,MAAM,IAAI,GAAG,yBAAyB,CACpC,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,WAAW,EACX,QAAQ,CAAC,SAAS,EAClB,QAAQ,CAAC,YAAY,EACrB,QAAQ,CAAC,UAAU,EACnB,QAAQ,CAAC,cAAc,CACxB,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC"}