@swoff/cli 0.3.12 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -30
- package/dist/index.js +2 -32
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +6 -16
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/cli/logger.js +1 -0
- package/dist/lib/cli/logger.js.map +1 -1
- package/dist/lib/commands/add.js +29 -87
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/assets.js +11 -4
- package/dist/lib/commands/assets.js.map +1 -1
- package/dist/lib/commands/clean.js +2 -10
- package/dist/lib/commands/clean.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +16 -30
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +55 -58
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/init.js +187 -31
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/commands/validate.js +2 -4
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/loader.js +34 -25
- package/dist/lib/config/loader.js.map +1 -1
- package/dist/lib/config/validator.js +210 -89
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/api-config.js +1 -1
- package/dist/lib/generators/file-generators/api-config.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-adapter.js +13 -0
- package/dist/lib/generators/file-generators/auth-adapter.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-check.js +40 -0
- package/dist/lib/generators/file-generators/auth-check.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-store.js +2 -5
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +1 -1
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +2 -1
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js +15 -0
- package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -0
- package/dist/lib/generators/file-generators/client-injector.js +2 -1
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/connectivity-manager.js +12 -0
- package/dist/lib/generators/file-generators/connectivity-manager.js.map +1 -0
- package/dist/lib/generators/file-generators/connectivity.js +12 -0
- package/dist/lib/generators/file-generators/connectivity.js.map +1 -0
- package/dist/lib/generators/file-generators/context.js +7 -0
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +29 -23
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
- package/dist/lib/generators/file-generators/gql-wrapper.js +2 -2
- package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +57 -16
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
- package/dist/lib/generators/file-generators/invalidation-tags.js +2 -3
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +2 -2
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
- package/dist/lib/generators/file-generators/notification-generator.js +6 -0
- package/dist/lib/generators/file-generators/notification-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/open-db.js +6 -0
- package/dist/lib/generators/file-generators/open-db.js.map +1 -0
- package/dist/lib/generators/file-generators/push.js +1 -2
- package/dist/lib/generators/file-generators/push.js.map +1 -1
- package/dist/lib/generators/file-generators/pwa-install.js +2 -6
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/server-push.js +1 -1
- package/dist/lib/generators/file-generators/server-push.js.map +1 -1
- package/dist/lib/generators/file-generators/storage-generator.js +6 -0
- package/dist/lib/generators/file-generators/storage-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/storage-notify.js +6 -0
- package/dist/lib/generators/file-generators/storage-notify.js.map +1 -0
- package/dist/lib/generators/file-generators/storage.js +6 -0
- package/dist/lib/generators/file-generators/storage.js.map +1 -0
- package/dist/lib/generators/file-generators/sw-generator-build.js +96 -45
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +1 -4
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-template.js +8 -9
- package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-version-gen.js +32 -0
- package/dist/lib/generators/file-generators/sw-version-gen.js.map +1 -0
- package/dist/lib/generators/file-generators/swoff-api-bundle.js +51 -0
- package/dist/lib/generators/file-generators/swoff-api-bundle.js.map +1 -0
- package/dist/lib/generators/sw-build-utils.js +69 -0
- package/dist/lib/generators/sw-build-utils.js.map +1 -0
- package/dist/lib/generators/sw-generator.js +26 -45
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +74 -23
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +15 -95
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-sync-handler.js +74 -72
- package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js +102 -0
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -0
- package/dist/lib/generators/sw-sections/config-header.js +2 -3
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/default-template.js +59 -6
- package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +621 -495
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/install-handler.js +40 -23
- package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/message-handler.js +25 -8
- package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/server-push-handler.js +10 -14
- package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/shared.js +68 -0
- package/dist/lib/generators/sw-sections/shared.js.map +1 -0
- package/dist/lib/generators/sw-sections/sw-push.js +2 -6
- package/dist/lib/generators/sw-sections/sw-push.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +55 -86
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +185 -66
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +68 -72
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/shared/feature-registry.js +198 -0
- package/dist/lib/shared/feature-registry.js.map +1 -0
- package/dist/lib/utils/detect-framework.js +52 -9
- package/dist/lib/utils/detect-framework.js.map +1 -1
- package/dist/runtime/api-config.js +2 -2
- package/dist/runtime/api-config.js.map +1 -1
- package/dist/runtime/auth-adapter.js +179 -0
- package/dist/runtime/auth-adapter.js.map +1 -0
- package/dist/runtime/auth-state.js +16 -6
- package/dist/runtime/auth-state.js.map +1 -1
- package/dist/runtime/auth-store.js +219 -213
- package/dist/runtime/auth-store.js.map +1 -1
- package/dist/runtime/auth-user.js +5 -19
- package/dist/runtime/auth-user.js.map +1 -1
- package/dist/runtime/background-sync.js +5 -2
- package/dist/runtime/background-sync.js.map +1 -1
- package/dist/runtime/cache.js +91 -3
- package/dist/runtime/cache.js.map +1 -1
- package/dist/runtime/client-injector-bundle.js +314 -0
- package/dist/runtime/client-injector-bundle.js.map +1 -0
- package/dist/runtime/client-injector.js +173 -30
- package/dist/runtime/client-injector.js.map +1 -1
- package/dist/runtime/connectivity-manager.js +83 -0
- package/dist/runtime/connectivity-manager.js.map +1 -0
- package/dist/runtime/connectivity.js +83 -0
- package/dist/runtime/connectivity.js.map +1 -0
- package/dist/runtime/fetch-wrapper.js +26 -59
- package/dist/runtime/fetch-wrapper.js.map +1 -1
- package/dist/runtime/gql-wrapper.js +15 -9
- package/dist/runtime/gql-wrapper.js.map +1 -1
- package/dist/runtime/invalidation-tags.js +12 -89
- package/dist/runtime/invalidation-tags.js.map +1 -1
- package/dist/runtime/mutation-queue.js +83 -31
- package/dist/runtime/mutation-queue.js.map +1 -1
- package/dist/runtime/mutation-state.js +3 -3
- package/dist/runtime/mutation-state.js.map +1 -1
- package/dist/runtime/notification.js +65 -0
- package/dist/runtime/notification.js.map +1 -0
- package/dist/runtime/open-db.js +41 -0
- package/dist/runtime/open-db.js.map +1 -0
- package/dist/runtime/push.js +15 -27
- package/dist/runtime/push.js.map +1 -1
- package/dist/runtime/pwa-injector.js +3 -32
- package/dist/runtime/pwa-injector.js.map +1 -1
- package/dist/runtime/pwa-prompt.js +35 -3
- package/dist/runtime/pwa-prompt.js.map +1 -1
- package/dist/runtime/reset.js +39 -42
- package/dist/runtime/reset.js.map +1 -1
- package/dist/runtime/server-push.js +23 -13
- package/dist/runtime/server-push.js.map +1 -1
- package/dist/runtime/storage.js +32 -0
- package/dist/runtime/storage.js.map +1 -0
- package/dist/runtime/sw-injector.js +19 -178
- package/dist/runtime/sw-injector.js.map +1 -1
- package/dist/runtime/sw-version.js +13 -0
- package/dist/runtime/sw-version.js.map +1 -0
- package/dist/runtime/swoff-api-bundle.js +1491 -0
- package/dist/runtime/swoff-api-bundle.js.map +1 -0
- package/dist/runtime/type-definitions.js +8 -7
- package/dist/runtime/type-definitions.js.map +1 -1
- package/dist/runtime/utils.js +4 -0
- package/dist/runtime/utils.js.map +1 -1
- package/dist/swoff-nkdhhmw8. +18 -0
- package/dist/swoff.js +5 -0
- package/package.json +1 -3
- package/templates/react/useAuth.jsx +7 -4
- package/templates/react/useAuth.tsx +14 -14
- package/templates/react/useBackgroundSync.jsx +5 -4
- package/templates/react/useBackgroundSync.tsx +5 -4
- package/templates/react/useCachedFetch.jsx +8 -0
- package/templates/react/useCachedFetch.tsx +8 -0
- package/templates/react/useMutation.jsx +60 -56
- package/templates/react/useMutation.tsx +65 -64
- package/templates/react/useMutationQueue.jsx +5 -1
- package/templates/react/useMutationQueue.tsx +8 -2
- package/templates/react/useMutationState.jsx +3 -4
- package/templates/react/useMutationState.tsx +4 -8
- package/templates/react/useNetworkStatus.jsx +57 -17
- package/templates/react/useNetworkStatus.tsx +42 -20
- package/templates/react/useOfflineAnalytics.jsx +42 -0
- package/templates/react/useOfflineAnalytics.tsx +50 -0
- package/templates/react/usePwaInstall.jsx +26 -0
- package/templates/react/usePwaInstall.tsx +26 -0
- package/templates/react/useSWUpdate.jsx +7 -52
- package/templates/react/useSWUpdate.tsx +12 -51
- package/templates/react/useStorageEstimate.jsx +56 -0
- package/templates/react/useStorageEstimate.tsx +67 -0
- package/dist/__tests__/assemble-sw.test.js +0 -145
- package/dist/__tests__/assemble-sw.test.js.map +0 -1
- package/dist/__tests__/cli-integration.test.js +0 -231
- package/dist/__tests__/cli-integration.test.js.map +0 -1
- package/dist/__tests__/config-types.test.js +0 -124
- package/dist/__tests__/config-types.test.js.map +0 -1
- package/dist/__tests__/detect-language.test.js +0 -51
- package/dist/__tests__/detect-language.test.js.map +0 -1
- package/dist/__tests__/file-generators.test.js +0 -228
- package/dist/__tests__/file-generators.test.js.map +0 -1
- package/dist/__tests__/glob-matcher.test.js +0 -86
- package/dist/__tests__/glob-matcher.test.js.map +0 -1
- package/dist/__tests__/invalidation-tags.test.js +0 -283
- package/dist/__tests__/invalidation-tags.test.js.map +0 -1
- package/dist/__tests__/loader.test.js +0 -77
- package/dist/__tests__/loader.test.js.map +0 -1
- package/dist/__tests__/validator.test.js +0 -340
- package/dist/__tests__/validator.test.js.map +0 -1
- package/templates/react/useCacheInvalidation.jsx +0 -19
- package/templates/react/useCacheInvalidation.tsx +0 -52
- package/templates/react/useIsFetching.jsx +0 -32
- package/templates/react/useIsFetching.tsx +0 -32
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { T, R, G, AS } from "./utils.js";
|
|
2
|
-
export function generateGqlWrapperCode(ctx,
|
|
2
|
+
export function generateGqlWrapperCode(ctx, endpoints) {
|
|
3
3
|
const { ext, ts } = ctx;
|
|
4
4
|
const optionsInterface = ts
|
|
5
5
|
? `
|
|
@@ -50,6 +50,8 @@ export interface GqlOptions {
|
|
|
50
50
|
import { fetchWithCache } from "../fetch/core.${ext}";
|
|
51
51
|
import type { GqlResult } from "../swoff";
|
|
52
52
|
${optionsInterface}
|
|
53
|
+
const GQL_ENDPOINTS = ${JSON.stringify(endpoints)};
|
|
54
|
+
|
|
53
55
|
/** Extract operation name from a GraphQL document. Returns null for anonymous queries (e.g. "{ todos { id } }"). */
|
|
54
56
|
function getOperationName(query${T(ts, "string")})${T(ts, "string | null")}{
|
|
55
57
|
const match = query.match(/(query|mutation|subscription)\\s+(\\w+)/);
|
|
@@ -76,23 +78,25 @@ function tagsFromOpName(name${T(ts, "string | null")})${T(ts, "string[]")}{
|
|
|
76
78
|
if (!name) return [];
|
|
77
79
|
const stripped = name.replace(/^(get|fetch|list|all|query)/i, "").replace(/^(create|set|add|new|update|delete|remove)/i, "");
|
|
78
80
|
if (!stripped) return [name.toLowerCase()];
|
|
79
|
-
const tag = stripped.
|
|
81
|
+
const tag = stripped.toLowerCase();
|
|
80
82
|
const plural = tag.replace(/s$/, "") + "s";
|
|
81
83
|
return [plural, tag];
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
/** Fetch a GraphQL endpoint with Swoff's caching, auth, offline queue, and auto-invalidation. Hashes query + variables for deterministic cache keys. */
|
|
86
|
+
/** Fetch a GraphQL endpoint with Swoff's caching, auth, offline queue, and auto-invalidation. Hashes query + variables for deterministic cache keys. Uses GQL_ENDPOINTS[0] by default. Pass endpointIndex to use a different endpoint. */
|
|
85
87
|
export async function fetchWithGql${G(ts, "T")}(
|
|
86
88
|
query${T(ts, "string")},
|
|
87
|
-
options${T(ts, "GqlOptions")} = {}
|
|
89
|
+
options${T(ts, "GqlOptions")} = {},
|
|
90
|
+
endpointIndex${T(ts, "number")} = 0
|
|
88
91
|
)${R(ts, "Promise<GqlResult<T>>")}{
|
|
89
92
|
const isRead = isReadOperation(query);
|
|
90
93
|
const opName = getOperationName(query);
|
|
91
94
|
const variables = options.variables;
|
|
92
95
|
const hash = await bodyHash({ query, variables });
|
|
93
96
|
const tags = options.tags || tagsFromOpName(opName);
|
|
97
|
+
const endpoint = GQL_ENDPOINTS[endpointIndex] || GQL_ENDPOINTS[0];
|
|
94
98
|
|
|
95
|
-
const { response, fromCache } = await fetchWithCache(
|
|
99
|
+
const { response, fromCache } = await fetchWithCache(endpoint, {
|
|
96
100
|
method: "POST",
|
|
97
101
|
body: JSON.stringify({ query, variables }),
|
|
98
102
|
headers: {
|
|
@@ -117,18 +121,20 @@ export async function fetchWithGql${G(ts, "T")}(
|
|
|
117
121
|
export async function queryGql${G(ts, "T")}(
|
|
118
122
|
query${T(ts, "string")},
|
|
119
123
|
variables${T(ts, "Record<string, unknown> | undefined")} = undefined,
|
|
120
|
-
options${T(ts, "GqlOptions")} = {}
|
|
124
|
+
options${T(ts, "GqlOptions")} = {},
|
|
125
|
+
endpointIndex${T(ts, "number")} = 0
|
|
121
126
|
)${R(ts, "Promise<GqlResult<T>>")}{
|
|
122
|
-
return fetchWithGql${G(ts, "T")}(query, { ...options, variables });
|
|
127
|
+
return fetchWithGql${G(ts, "T")}(query, { ...options, variables }, endpointIndex);
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
/** Shorthand for GraphQL mutations (type: "mutation"). Auto-invalidates related cache tags. */
|
|
126
131
|
export async function mutateGql${G(ts, "T")}(
|
|
127
132
|
mutation${T(ts, "string")},
|
|
128
133
|
variables${T(ts, "Record<string, unknown> | undefined")} = undefined,
|
|
129
|
-
options${T(ts, "GqlOptions")} = {}
|
|
134
|
+
options${T(ts, "GqlOptions")} = {},
|
|
135
|
+
endpointIndex${T(ts, "number")} = 0
|
|
130
136
|
)${R(ts, "Promise<GqlResult<T>>")}{
|
|
131
|
-
return fetchWithGql${G(ts, "T")}(mutation, { ...options, variables });
|
|
137
|
+
return fetchWithGql${G(ts, "T")}(mutation, { ...options, variables }, endpointIndex);
|
|
132
138
|
}
|
|
133
139
|
`;
|
|
134
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gql-wrapper.js","sourceRoot":"","sources":["../../src/runtime/gql-wrapper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,UAAU,sBAAsB,CACpC,GAAmB,EACnB,
|
|
1
|
+
{"version":3,"file":"gql-wrapper.js","sourceRoot":"","sources":["../../src/runtime/gql-wrapper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,UAAU,sBAAsB,CACpC,GAAmB,EACnB,SAAmB;IAEnB,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAExB,MAAM,gBAAgB,GAAG,EAAE;QACzB,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;8EAOqE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;gDA4BjC,GAAG;;EAEjD,gBAAgB;wBACM,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;iCAGhB,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;gCAM1C,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;;;;;;;6BAOtC,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,iBAAiB,CAAC;;;;;;;;8BAQ3C,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC;;;;;;;;;;oCAUrC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC;SACrC,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;WACb,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC;iBACb,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;GAC7B,CAAC,CAAC,EAAE,EAAE,uBAAuB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;8BA0BH,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC;;;;gCAIT,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC;SACjC,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;aACX,CAAC,CAAC,EAAE,EAAE,qCAAqC,CAAC;WAC9C,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC;iBACb,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;GAC7B,CAAC,CAAC,EAAE,EAAE,uBAAuB,CAAC;uBACV,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC;;;;iCAIA,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC;YAC/B,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;aACd,CAAC,CAAC,EAAE,EAAE,qCAAqC,CAAC;WAC9C,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC;iBACb,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;GAC7B,CAAC,CAAC,EAAE,EAAE,uBAAuB,CAAC;uBACV,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC;;CAEhC,CAAC;AACF,CAAC"}
|
|
@@ -74,24 +74,17 @@ function generateSingularizationCode(singularization) {
|
|
|
74
74
|
return "null";
|
|
75
75
|
return JSON.stringify(singularization);
|
|
76
76
|
}
|
|
77
|
-
function
|
|
78
|
-
if (!cascading || Object.keys(cascading).length === 0)
|
|
79
|
-
return "null";
|
|
80
|
-
return JSON.stringify(cascading);
|
|
81
|
-
}
|
|
82
|
-
export function generateInvalidationTagsCode(ctx, prefixes, patterns, singularization, cascading) {
|
|
77
|
+
export function generateInvalidationTagsCode(ctx, prefixes, patterns, singularization) {
|
|
83
78
|
const { ext, ts } = ctx;
|
|
84
79
|
const patternCode = generatePatternCode(patterns);
|
|
85
80
|
const singularizationCode = generateSingularizationCode(singularization);
|
|
86
|
-
const cascadingCode = generateCascadingCode(cascading);
|
|
87
81
|
const prefixesCode = JSON.stringify(prefixes);
|
|
88
82
|
return `/**
|
|
89
83
|
* Swoff Invalidation Tags Helper
|
|
90
|
-
*
|
|
91
|
-
* prefix skipping, singularization, and cascading invalidation.
|
|
84
|
+
* Segment- and pattern-based tag generation from URLs.
|
|
92
85
|
*
|
|
93
86
|
* Usage:
|
|
94
|
-
* import { generateTags
|
|
87
|
+
* import { generateTags } from './swoff/cache/tags.${ext}';
|
|
95
88
|
*
|
|
96
89
|
* // Generate tags from URL
|
|
97
90
|
* generateTags("/api/todos"); // ["todos"]
|
|
@@ -102,11 +95,9 @@ export function generateInvalidationTagsCode(ctx, prefixes, patterns, singulariz
|
|
|
102
95
|
* tags: generateTags("/api/todos"),
|
|
103
96
|
* });
|
|
104
97
|
*
|
|
105
|
-
* //
|
|
106
|
-
*
|
|
98
|
+
* // Generate method-prefixed tags for mutations
|
|
99
|
+
* generateTagsFromMethod("POST", "/api/todos"); // ["post-todos"]
|
|
107
100
|
*/
|
|
108
|
-
|
|
109
|
-
import { invalidateByTags } from "./index.${ext}";
|
|
110
101
|
${ts ? `
|
|
111
102
|
interface TagPattern {
|
|
112
103
|
re: RegExp;
|
|
@@ -123,12 +114,15 @@ const SKIP_PREFIXES = ${prefixesCode};
|
|
|
123
114
|
// Custom singularization map (plural → singular)
|
|
124
115
|
const SINGULARIZATION${T(ts, "Record<string, string> | null")} = ${singularizationCode};
|
|
125
116
|
|
|
126
|
-
// Cascading invalidation map (tag → dependent tags)
|
|
127
|
-
const CASCADING${T(ts, "Record<string, string[]> | null")} = ${cascadingCode};
|
|
128
|
-
|
|
129
117
|
/** Generate cache invalidation tags from a URL path. Tries configured patterns first, falls back to segment-based generation. */
|
|
130
118
|
export function generateTags(url${T(ts, "string | URL")})${R(ts, "string[]")}{
|
|
131
|
-
const
|
|
119
|
+
const base = typeof window !== "undefined" ? window.location.origin : "";
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = typeof url === "string" ? new URL(url, base) : url;
|
|
123
|
+
} catch {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
132
126
|
const path = parsed.pathname;
|
|
133
127
|
|
|
134
128
|
// Try configured patterns first
|
|
@@ -184,77 +178,6 @@ export function generateTagsFromMethod(method${T(ts, "string")}, url${T(ts, "str
|
|
|
184
178
|
if (method === "GET" || method === "HEAD") return tags;
|
|
185
179
|
return tags.map((tag) => \`\${method.toLowerCase()}-\${tag}\`);
|
|
186
180
|
}
|
|
187
|
-
|
|
188
|
-
/** Invalidate cached responses related to a URL, including cascading dependencies. */
|
|
189
|
-
export async function invalidateUrl(url${T(ts, "string | URL")})${R(ts, "Promise<void>")}{
|
|
190
|
-
const tags = generateTags(url);
|
|
191
|
-
const allTags = CASCADING ? expandCascading(tags) : tags;
|
|
192
|
-
await invalidateByTags(allTags);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Invalidate cached responses tagged with method-prefixed tags, including cascading dependencies. */
|
|
196
|
-
export async function invalidateByMethod(method${T(ts, "string")}, url${T(ts, "string | URL")})${R(ts, "Promise<void>")}{
|
|
197
|
-
const tags = generateTagsFromMethod(method, url);
|
|
198
|
-
const allTags = CASCADING ? expandCascading(tags) : tags;
|
|
199
|
-
await invalidateByTags(allTags);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** Expand tags with their cascading dependencies, deduplicated. */
|
|
203
|
-
export function expandCascading(tags${T(ts, "string[]")})${R(ts, "string[]")}{
|
|
204
|
-
if (!CASCADING) return [...tags];
|
|
205
|
-
const result = new Set(tags);
|
|
206
|
-
for (const tag of tags) {
|
|
207
|
-
const deps = CASCADING[tag];
|
|
208
|
-
if (deps) {
|
|
209
|
-
for (const dep of deps) {
|
|
210
|
-
result.add(dep);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return [...result];
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** Introspect: get all URLs cached under a given tag. */
|
|
218
|
-
export async function getUrlsForTag(tag${T(ts, "string")})${R(ts, "Promise<{ url: string; actualUrl: string }[]>")}{
|
|
219
|
-
const controller = navigator.serviceWorker?.controller;
|
|
220
|
-
if (!controller) return [];
|
|
221
|
-
return new Promise((resolve) => {
|
|
222
|
-
const channel = new MessageChannel();
|
|
223
|
-
channel.port1.onmessage = (event) => {
|
|
224
|
-
resolve(event.data.urls || []);
|
|
225
|
-
};
|
|
226
|
-
controller.postMessage(
|
|
227
|
-
{ type: "GET_URLS_FOR_TAG", tag },
|
|
228
|
-
[channel.port2],
|
|
229
|
-
);
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/** Introspect: get all tags associated with a given URL. */
|
|
234
|
-
export async function getTagsForUrl(url${T(ts, "string")})${R(ts, "Promise<string[]>")}{
|
|
235
|
-
const controller = navigator.serviceWorker?.controller;
|
|
236
|
-
if (!controller) return [];
|
|
237
|
-
return new Promise((resolve) => {
|
|
238
|
-
const channel = new MessageChannel();
|
|
239
|
-
channel.port1.onmessage = (event) => {
|
|
240
|
-
resolve(event.data.tags || []);
|
|
241
|
-
};
|
|
242
|
-
controller.postMessage(
|
|
243
|
-
{ type: "GET_TAGS_FOR_URL", url },
|
|
244
|
-
[channel.port2],
|
|
245
|
-
);
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/** Invalidate all cached responses whose URL matches the given glob pattern. */
|
|
250
|
-
export async function invalidateMatching(glob${T(ts, "string")})${R(ts, "Promise<void>")}{
|
|
251
|
-
const controller = navigator.serviceWorker?.controller;
|
|
252
|
-
if (!controller) return;
|
|
253
|
-
controller.postMessage({
|
|
254
|
-
type: "INVALIDATE_MATCHING",
|
|
255
|
-
glob,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
181
|
`;
|
|
259
182
|
}
|
|
260
183
|
//# sourceMappingURL=invalidation-tags.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../src/runtime/invalidation-tags.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"invalidation-tags.js","sourceRoot":"","sources":["../../src/runtime/invalidation-tags.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { T, R, PT, AS } from "./utils.js";
|
|
2
|
-
export function generateMutationQueueCode(ctx, authEnabled, batchSize, batchDelayMs, maxRetries, retryBackoffMs) {
|
|
2
|
+
export function generateMutationQueueCode(ctx, authEnabled, batchSize, batchDelayMs, maxRetries, retryBackoffMs, retryMaxBackoffMs, retryJitterMs) {
|
|
3
3
|
const { ext, ts } = ctx;
|
|
4
|
-
const
|
|
5
|
-
? `import { getAuth } from "../auth/store.${ext}";
|
|
4
|
+
const authImports = authEnabled
|
|
5
|
+
? `import { getAuth, ensureValidAuth, clearAuth } from "../auth/store.${ext}";
|
|
6
6
|
`
|
|
7
7
|
: "";
|
|
8
|
-
const additionalImports = `import { invalidateByTags } from "../cache/
|
|
8
|
+
const additionalImports = `import { invalidateByTags } from "../cache/invalidate.${ext}";
|
|
9
9
|
${ts
|
|
10
10
|
? `import type { MutationQueueItem } from "../swoff.d.ts";
|
|
11
11
|
`
|
|
@@ -18,6 +18,41 @@ ${ts
|
|
|
18
18
|
const authHeaderSpread = authEnabled
|
|
19
19
|
? ` ...authHeader,\n`
|
|
20
20
|
: " ";
|
|
21
|
+
const auth401ReplayBlock = authEnabled
|
|
22
|
+
? ` if (response.status === 401) {
|
|
23
|
+
// Auth failure — try silent refresh (/refresh endpoint)
|
|
24
|
+
const refreshed = await ensureValidAuth();
|
|
25
|
+
if (refreshed?.token) {
|
|
26
|
+
// Refresh succeeded — retry the mutation once
|
|
27
|
+
const retryHeader${T(ts, "Record<string, string>")} = refreshed?.token ? { Authorization: \`Bearer \${refreshed.token}\` } : {};
|
|
28
|
+
const retryResponse = await fetch(item.url, {
|
|
29
|
+
method: item.method,
|
|
30
|
+
headers: {
|
|
31
|
+
...(contentType ? { "Content-Type": contentType } : {}),
|
|
32
|
+
...item.headers,
|
|
33
|
+
...retryHeader,
|
|
34
|
+
},
|
|
35
|
+
...(replayBody != null ? { body: replayBody } : {}),
|
|
36
|
+
});
|
|
37
|
+
if (retryResponse.ok) {
|
|
38
|
+
// Success after auth refresh — normal flow
|
|
39
|
+
if (item.tags && item.tags.length > 0) {
|
|
40
|
+
await invalidateByTags(item.tags);
|
|
41
|
+
}
|
|
42
|
+
await removeFromQueue(item.id, db);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
// Retry still failed — authenticated but not authorized for this resource
|
|
46
|
+
// Drop this mutation immediately (don't retry)
|
|
47
|
+
await removeFromQueue(item.id, db);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// Refresh failed — session expired. ClearAuth cascades to clear queue + caches.
|
|
51
|
+
await clearAuth();
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
`
|
|
55
|
+
: "";
|
|
21
56
|
const code = `/**
|
|
22
57
|
* Swoff Mutation Queue
|
|
23
58
|
* Queue offline writes and sync when connection returns.
|
|
@@ -44,33 +79,36 @@ ${ts
|
|
|
44
79
|
* batchDelayMs: ${batchDelayMs} — delay between mutations (rate limiting)
|
|
45
80
|
* maxRetries: ${maxRetries} — max retries before dropping
|
|
46
81
|
* retryBackoffMs: ${retryBackoffMs} — exponential backoff base
|
|
82
|
+
* retryMaxBackoffMs: ${retryMaxBackoffMs} — max backoff cap
|
|
83
|
+
* retryJitterMs: ${retryJitterMs} — jitter for randomization
|
|
47
84
|
*/
|
|
48
85
|
|
|
49
|
-
${
|
|
86
|
+
${authImports}${additionalImports}const DB_NAME = "swoff-queue";
|
|
50
87
|
const STORE_NAME = "mutations";
|
|
51
|
-
// Bump this when adding new indexes/stores for schema migration
|
|
52
|
-
const DB_VERSION = 1;
|
|
53
88
|
const BATCH_SIZE = ${batchSize};
|
|
54
89
|
const BATCH_DELAY_MS = ${batchDelayMs};
|
|
55
90
|
const MAX_RETRIES = ${maxRetries};
|
|
56
91
|
const RETRY_BACKOFF_MS = ${retryBackoffMs};
|
|
92
|
+
const RETRY_MAX_BACKOFF_MS = ${retryMaxBackoffMs};
|
|
93
|
+
const RETRY_JITTER_MS = ${retryJitterMs};
|
|
94
|
+
|
|
95
|
+
function backoffDelay(attempt${T(ts, "number")}) {
|
|
96
|
+
const delay = Math.min(RETRY_BACKOFF_MS * Math.pow(2, attempt), RETRY_MAX_BACKOFF_MS);
|
|
97
|
+
return delay + (RETRY_JITTER_MS > 0 ? Math.random() * RETRY_JITTER_MS : 0);
|
|
98
|
+
}
|
|
57
99
|
|
|
58
100
|
function sleep(ms${T(ts, "number")})${R(ts, "Promise<void>")}{
|
|
59
101
|
return new Promise((r) => setTimeout(r, ms));
|
|
60
102
|
}
|
|
61
103
|
|
|
104
|
+
import { openDB } from "../db.${ext}";
|
|
105
|
+
|
|
62
106
|
function openQueueDB()${R(ts, "Promise<IDBDatabase>")}{
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const store = db.createObjectStore(STORE_NAME, { keyPath: "id" });
|
|
69
|
-
store.createIndex("by-timestamp", "timestamp");
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
request.onsuccess = (e) => resolve((e.target${AS(ts, "IDBOpenDBRequest")}).result);
|
|
73
|
-
request.onerror = (e) => reject((e.target${AS(ts, "IDBRequest")}).error);
|
|
107
|
+
return openDB(DB_NAME, STORE_NAME, "id", (db) => {
|
|
108
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
109
|
+
const store = db.createObjectStore(STORE_NAME, { keyPath: "id" });
|
|
110
|
+
store.createIndex("by-timestamp", "timestamp");
|
|
111
|
+
}
|
|
74
112
|
});
|
|
75
113
|
}
|
|
76
114
|
|
|
@@ -114,8 +152,8 @@ export async function queueMutation(mutation${T(ts, "Partial<MutationQueueItem>"
|
|
|
114
152
|
});
|
|
115
153
|
|
|
116
154
|
await new Promise<void>((resolve, reject) => {
|
|
117
|
-
tx.oncomplete = () => resolve();
|
|
118
|
-
tx.onerror = () => reject(tx.error);
|
|
155
|
+
tx.oncomplete = () => { db.close(); resolve(); };
|
|
156
|
+
tx.onerror = () => { db.close(); reject(tx.error); };
|
|
119
157
|
});
|
|
120
158
|
|
|
121
159
|
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
@@ -151,7 +189,7 @@ ${authHeaderSpread} },
|
|
|
151
189
|
...(replayBody != null ? { body: replayBody } : {}),
|
|
152
190
|
});
|
|
153
191
|
|
|
154
|
-
if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
|
|
192
|
+
${auth401ReplayBlock} if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
|
|
155
193
|
|
|
156
194
|
if (item.tags && item.tags.length > 0) {
|
|
157
195
|
await invalidateByTags(item.tags);
|
|
@@ -161,18 +199,30 @@ ${authHeaderSpread} },
|
|
|
161
199
|
return true;
|
|
162
200
|
} catch {
|
|
163
201
|
item.retryCount++;
|
|
164
|
-
item.nextRetryAt = Date.now() +
|
|
202
|
+
item.nextRetryAt = Date.now() + backoffDelay(item.retryCount - 1);
|
|
165
203
|
await updateInQueue(item, db);
|
|
166
204
|
return false;
|
|
167
205
|
}
|
|
168
206
|
}
|
|
169
207
|
|
|
208
|
+
/** Clear all queued mutations. Called when auth context changes (logout, session expiry) — queued operations belong to the old session and must not leak. */
|
|
209
|
+
export async function clearQueue()${R(ts, "Promise<void>")}{
|
|
210
|
+
const db = await openQueueDB();
|
|
211
|
+
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
212
|
+
tx.objectStore(STORE_NAME).clear();
|
|
213
|
+
await new Promise${PT(ts, "void")}((resolve, reject) => {
|
|
214
|
+
tx.oncomplete = () => { db.close(); resolve(); };
|
|
215
|
+
tx.onerror = () => { db.close(); reject(tx.error); };
|
|
216
|
+
});
|
|
217
|
+
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
218
|
+
}
|
|
219
|
+
|
|
170
220
|
/** 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. */
|
|
171
221
|
export async function processMutationQueue()${R(ts, "Promise<void>")}{
|
|
172
222
|
if (isSyncing) return;
|
|
173
|
-
isSyncing = true;
|
|
174
223
|
|
|
175
224
|
const db = await openQueueDB();
|
|
225
|
+
isSyncing = true;
|
|
176
226
|
try {
|
|
177
227
|
const tx = db.transaction(STORE_NAME, "readonly");
|
|
178
228
|
const store = tx.objectStore(STORE_NAME);
|
|
@@ -218,8 +268,8 @@ export async function processMutationQueue()${R(ts, "Promise<void>")}{
|
|
|
218
268
|
// Emit progress after every BATCH_SIZE mutations
|
|
219
269
|
if ((succeeded + failed) % BATCH_SIZE === 0 || succeeded + failed === total) {
|
|
220
270
|
window.dispatchEvent(
|
|
221
|
-
new CustomEvent("mutation-sync-
|
|
222
|
-
detail: { succeeded, failed, total
|
|
271
|
+
new CustomEvent("mutation-sync-progress", {
|
|
272
|
+
detail: { succeeded, failed, total },
|
|
223
273
|
})
|
|
224
274
|
);
|
|
225
275
|
}
|
|
@@ -250,22 +300,24 @@ export async function flushMutations()${R(ts, "Promise<void>")}{
|
|
|
250
300
|
}
|
|
251
301
|
|
|
252
302
|
async function removeFromQueue(id${T(ts, "string")}, db${T(ts, "IDBDatabase | undefined")})${R(ts, "Promise<void>")}{
|
|
253
|
-
|
|
303
|
+
var ownDb = false;
|
|
304
|
+
if (!db) { db = await openQueueDB(); ownDb = true; }
|
|
254
305
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
255
306
|
tx.objectStore(STORE_NAME).delete(id);
|
|
256
307
|
await new Promise${PT(ts, "void")}((resolve, reject) => {
|
|
257
|
-
tx.oncomplete = () => resolve();
|
|
258
|
-
tx.onerror = () => reject(tx.error);
|
|
308
|
+
tx.oncomplete = () => { if (ownDb) db.close(); resolve(); };
|
|
309
|
+
tx.onerror = () => { if (ownDb) db.close(); reject(tx.error); };
|
|
259
310
|
});
|
|
260
311
|
}
|
|
261
312
|
|
|
262
313
|
async function updateInQueue(item${T(ts, "MutationQueueItem")}, db${T(ts, "IDBDatabase | undefined")})${R(ts, "Promise<void>")}{
|
|
263
|
-
|
|
314
|
+
var ownDb = false;
|
|
315
|
+
if (!db) { db = await openQueueDB(); ownDb = true; }
|
|
264
316
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
265
317
|
tx.objectStore(STORE_NAME).put(item);
|
|
266
318
|
await new Promise${PT(ts, "void")}((resolve, reject) => {
|
|
267
|
-
tx.oncomplete = () => resolve();
|
|
268
|
-
tx.onerror = () => reject(tx.error);
|
|
319
|
+
tx.oncomplete = () => { if (ownDb) db.close(); resolve(); };
|
|
320
|
+
tx.onerror = () => { if (ownDb) db.close(); reject(tx.error); };
|
|
269
321
|
});
|
|
270
322
|
}
|
|
271
323
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../../src/runtime/mutation-queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,UAAU,yBAAyB,CACvC,GAAmB,EACnB,WAAoB,EACpB,SAAiB,EACjB,YAAoB,EACpB,UAAkB,EAClB,cAAsB;
|
|
1
|
+
{"version":3,"file":"mutation-queue.js","sourceRoot":"","sources":["../../src/runtime/mutation-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T, R } from "./utils.js";
|
|
1
|
+
import { T, R, G } from "./utils.js";
|
|
2
2
|
export function generateMutationStateCode(ctx) {
|
|
3
3
|
const { ext, ts } = ctx;
|
|
4
4
|
return `/**
|
|
@@ -31,10 +31,10 @@ export interface MutationState {
|
|
|
31
31
|
timestamp${T(ts, "number")};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const mutations = new Map
|
|
34
|
+
const mutations = new Map${G(ts, "string, MutationState")}();
|
|
35
35
|
|
|
36
36
|
let listenerId = 0;
|
|
37
|
-
const listeners = new Map
|
|
37
|
+
const listeners = new Map${G(ts, "number, (state: MutationState) => void")}();
|
|
38
38
|
|
|
39
39
|
/** Track a mutation with the given ID and initial status. */
|
|
40
40
|
export function trackMutation(id${T(ts, "string")}, status${T(ts, "MutationStatus")} = "idle")${R(ts, "MutationState")}{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation-state.js","sourceRoot":"","sources":["../../src/runtime/mutation-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"mutation-state.js","sourceRoot":"","sources":["../../src/runtime/mutation-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAErC,MAAM,UAAU,yBAAyB,CAAC,GAAmB;IAC3D,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,OAAO;;;;;;mGAM0F,GAAG;;;;;;;;;;;;;;;;;MAiBhG,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;UACX,CAAC,CAAC,EAAE,EAAE,gBAAgB,CAAC;SACxB,CAAC,CAAC,EAAE,EAAE,cAAc,CAAC;QACtB,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC;aACX,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;;;2BAGD,CAAC,CAAC,EAAE,EAAE,uBAAuB,CAAC;;;2BAG9B,CAAC,CAAC,EAAE,EAAE,wCAAwC,CAAC;;;kCAGxC,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC;;;;;;;;;;;;;;;wCAe9E,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,wBAAwB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,sBAAsB,CAAC;;;;;;;;;;;oCAW/F,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;mCAKjE,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;qCAKtD,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,2BAA2B,CAAC;;;;;uCAKnD,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;wCAK/B,CAAC,CAAC,EAAE,EAAE,iBAAiB,CAAC;;;;;yCAKvB,CAAC,CAAC,EAAE,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;;;;;;;;;gDASnC,CAAC,CAAC,EAAE,EAAE,gCAAgC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC;;;;;;gCAM9E,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;;;;;;;;;CAStE,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { T, R } from "./utils.js";
|
|
2
|
+
export function generateNotificationCode(ctx) {
|
|
3
|
+
const { ext, ts } = ctx;
|
|
4
|
+
return `/**
|
|
5
|
+
* Swoff Notification
|
|
6
|
+
* Dispatches \`swoff:notification\` custom events so your app can
|
|
7
|
+
* show toast/alert UI for offline-first events (fetch failures,
|
|
8
|
+
* storage quota warnings, background sync errors, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { checkStorage } from "./swoff/notification.${ext}";
|
|
12
|
+
* const { usage, quota, percentUsed } = await checkStorage();
|
|
13
|
+
*
|
|
14
|
+
* Window events:
|
|
15
|
+
* swoff:notification — dispatched by the service worker or by checkStorage()
|
|
16
|
+
* detail: { level: "error"|"warn"|"info", code: string, message: string }
|
|
17
|
+
*
|
|
18
|
+
* ── Placeholder listener ────────────────────────────────
|
|
19
|
+
* Add a listener in your app entry point and replace the body
|
|
20
|
+
* with your own toast library (e.g. react-hot-toast, vue-toastification):
|
|
21
|
+
*
|
|
22
|
+
* window.addEventListener("swoff:notification", (event) => {
|
|
23
|
+
* const { level, code, message } = event.detail;
|
|
24
|
+
* // console.log(\`[swoff:\${level}] \${code}: \${message}\`);
|
|
25
|
+
* // alert(\`\${code}: \${message}\`);
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** Get raw storage estimate without dispatching events. Reuse in your own UI. */
|
|
30
|
+
export async function getStorageEstimate()${R(ts, "Promise<{ usage: number; quota: number; percentUsed: number }>")}{
|
|
31
|
+
if (!navigator.storage?.estimate) {
|
|
32
|
+
return { usage: 0, quota: 0, percentUsed: 0 };
|
|
33
|
+
}
|
|
34
|
+
const { usage = 0, quota = 0 } = await navigator.storage.estimate();
|
|
35
|
+
const percentUsed = quota > 0 ? Math.round((usage / quota) * 100) : 0;
|
|
36
|
+
return { usage, quota, percentUsed };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Check available storage and dispatch a warning if >80% used. */
|
|
40
|
+
export async function checkStorage()${R(ts, "Promise<{ usage: number; quota: number; percentUsed: number }>")}{
|
|
41
|
+
const result = await getStorageEstimate();
|
|
42
|
+
if (result.percentUsed > 80) {
|
|
43
|
+
window.dispatchEvent(
|
|
44
|
+
new CustomEvent("swoff:notification", {
|
|
45
|
+
detail: {
|
|
46
|
+
level: "warn",
|
|
47
|
+
code: "STORAGE_QUOTA_HIGH",
|
|
48
|
+
message: \`Storage at \${result.percentUsed}% capacity (\${formatBytes(result.usage)} / \${formatBytes(result.quota)})\`,
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Format bytes to a human-readable string (e.g. "1.5 MB"). */
|
|
57
|
+
export function formatBytes(bytes${T(ts, "number")})${R(ts, "string")}{
|
|
58
|
+
if (bytes === 0) return "0 B";
|
|
59
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
60
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
61
|
+
return \`\${(bytes / Math.pow(1024, i)).toFixed(1)} \${units[i]}\`;
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=notification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.js","sourceRoot":"","sources":["../../src/runtime/notification.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,UAAU,wBAAwB,CAAC,GAAmB;IAC1D,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,OAAO;;;;;;;0DAOiD,GAAG;;;;;;;;;;;;;;;;;;;4CAmBjB,CAAC,CAAC,EAAE,EAAE,gEAAgE,CAAC;;;;;;;;;;sCAU7E,CAAC,CAAC,EAAE,EAAE,gEAAgE,CAAC;;;;;;;;;;;;;;;;;mCAiB1E,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;;;;;;CAMpE,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { T, R, PT, AS, O } from "./utils.js";
|
|
2
|
+
export function generateOpenDBCode(ctx) {
|
|
3
|
+
const { ts } = ctx;
|
|
4
|
+
return `/**
|
|
5
|
+
* Swoff IndexedDB Helper
|
|
6
|
+
* Generic promise-based IndexedDB open helper used by auth, queue, and push modules.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { openDB } from "./db.${ctx.ext}";
|
|
10
|
+
*
|
|
11
|
+
* const db = await openDB("my-db", "my-store", "id");
|
|
12
|
+
* // optional upgrade callback for additional indexes:
|
|
13
|
+
* const db = await openDB("my-db", "my-store", "id", (db) => {
|
|
14
|
+
* db.createIndex("by-timestamp", "timestamp");
|
|
15
|
+
* });
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export function openDB(
|
|
19
|
+
name${T(ts, "string")},
|
|
20
|
+
storeName${T(ts, "string")},
|
|
21
|
+
keyPath${T(ts, "string | string[]")},
|
|
22
|
+
upgradeCallback${O(ts, "(db: IDBDatabase) => void")},
|
|
23
|
+
version${O(ts, "number")}
|
|
24
|
+
)${R(ts, "Promise<IDBDatabase>")}{
|
|
25
|
+
return new Promise${PT(ts, "IDBDatabase")}((resolve, reject) => {
|
|
26
|
+
const request = indexedDB.open(name, version ?? 1);
|
|
27
|
+
request.onupgradeneeded = (e) => {
|
|
28
|
+
const db = (e.target${AS(ts, "IDBOpenDBRequest")}).result;
|
|
29
|
+
if (upgradeCallback) {
|
|
30
|
+
upgradeCallback(db);
|
|
31
|
+
} else if (!db.objectStoreNames.contains(storeName)) {
|
|
32
|
+
db.createObjectStore(storeName, { keyPath: keyPath${AS(ts, 'IDBObjectStoreParameters["keyPath"]')} });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
request.onsuccess = (e) => resolve((e.target${AS(ts, "IDBOpenDBRequest")}).result);
|
|
36
|
+
request.onerror = (e) => reject((e.target${AS(ts, "IDBRequest")}).error);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=open-db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-db.js","sourceRoot":"","sources":["../../src/runtime/open-db.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,UAAU,kBAAkB,CAAC,GAAmB;IACpD,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACnB,OAAO;;;;;oCAK2B,GAAG,CAAC,GAAG;;;;;;;;;;QAUnC,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;aACV,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;WACjB,CAAC,CAAC,EAAE,EAAE,mBAAmB,CAAC;mBAClB,CAAC,CAAC,EAAE,EAAE,2BAA2B,CAAC;WAC1C,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC;GACvB,CAAC,CAAC,EAAE,EAAE,sBAAsB,CAAC;sBACV,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC;;;4BAGf,EAAE,CAAC,EAAE,EAAE,kBAAkB,CAAC;;;;4DAIM,EAAE,CAAC,EAAE,EAAE,qCAAqC,CAAC;;;kDAGvD,EAAE,CAAC,EAAE,EAAE,kBAAkB,CAAC;+CAC7B,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC;;;CAGlE,CAAC;AACF,CAAC"}
|