@arcote.tech/arc 0.7.16 → 0.7.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/adapters/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { AuthAdapter } from "./auth-adapter";
|
|
12
12
|
export type { DecodedToken } from "./auth-adapter";
|
|
13
|
+
export { awaitModuleSync, hasModuleSyncProvider, registerModuleSyncProvider, triggerModuleSync, } from "./module-sync-coordinator";
|
|
14
|
+
export type { ModuleSyncProvider } from "./module-sync-coordinator";
|
|
13
15
|
export { CommandWire } from "./command-wire";
|
|
14
16
|
export { EventWire } from "./event-wire";
|
|
15
17
|
export type { QuerySubscriptionCallbacks, ReceivedEvent, SyncableEvent, } from "./event-wire";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-sync coordinator — neutral seam letting token-setters (core/react)
|
|
3
|
+
* await the platform module loader WITHOUT core/react depending on platform.
|
|
4
|
+
*
|
|
5
|
+
* Token-gated code-splitting: setting a scope token must dynamically import
|
|
6
|
+
* that token's module chunks before routes inside them exist. The loader
|
|
7
|
+
* lives in the platform layer; setToken lives in core/react. This singleton
|
|
8
|
+
* is the bridge: platform's useModuleLoader registers a provider (runs
|
|
9
|
+
* syncModules), and setToken triggers a sync + returns a promise that
|
|
10
|
+
* resolves when the LATEST sync finishes.
|
|
11
|
+
*
|
|
12
|
+
* With no provider registered (server, SSR, tests, app-without-platform)
|
|
13
|
+
* every call resolves immediately — preserving the historical synchronous
|
|
14
|
+
* semantics. It NEVER rejects: a hung/failed sync resolves after a bounded
|
|
15
|
+
* timeout so navigation always proceeds.
|
|
16
|
+
*/
|
|
17
|
+
export type ModuleSyncProvider = (scope?: string) => Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Register the function that performs a module sync. Returns an unregister fn.
|
|
20
|
+
* Last registration wins (only one platform loader is ever mounted).
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerModuleSyncProvider(fn: ModuleSyncProvider): () => void;
|
|
23
|
+
export declare function hasModuleSyncProvider(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Trigger a sync through the registered provider; resolve when it finishes
|
|
26
|
+
* (or after `timeoutMs`). Coalesces concurrent calls — the published
|
|
27
|
+
* `latestSync` tracks the newest invocation.
|
|
28
|
+
*
|
|
29
|
+
* - No provider → resolves immediately (sync semantics preserved).
|
|
30
|
+
* - Provider hangs → resolves (never rejects) after `timeoutMs` + a warning.
|
|
31
|
+
* - Provider throws → resolves (never rejects) + a warning; the loader
|
|
32
|
+
* surfaces its own error state. setToken must not throw on module-load fail.
|
|
33
|
+
*/
|
|
34
|
+
export declare function triggerModuleSync(scope?: string, timeoutMs?: number): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Await the most recent sync without triggering a new one. Resolves
|
|
37
|
+
* immediately when nothing is in flight.
|
|
38
|
+
*/
|
|
39
|
+
export declare function awaitModuleSync(): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=module-sync-coordinator.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -119,6 +119,56 @@ class AuthAdapter {
|
|
|
119
119
|
this.scopes.clear();
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
// src/adapters/module-sync-coordinator.ts
|
|
123
|
+
var DEFAULT_TIMEOUT_MS = 8000;
|
|
124
|
+
var provider = null;
|
|
125
|
+
var latestSync = null;
|
|
126
|
+
var syncSeq = 0;
|
|
127
|
+
function registerModuleSyncProvider(fn) {
|
|
128
|
+
provider = fn;
|
|
129
|
+
return () => {
|
|
130
|
+
if (provider === fn)
|
|
131
|
+
provider = null;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function hasModuleSyncProvider() {
|
|
135
|
+
return provider !== null;
|
|
136
|
+
}
|
|
137
|
+
function triggerModuleSync(scope, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
138
|
+
if (!provider) {
|
|
139
|
+
latestSync = Promise.resolve();
|
|
140
|
+
return latestSync;
|
|
141
|
+
}
|
|
142
|
+
const seq = ++syncSeq;
|
|
143
|
+
const run = (async () => {
|
|
144
|
+
try {
|
|
145
|
+
await provider(scope);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.warn("[arc] module sync failed during setToken:", err);
|
|
148
|
+
}
|
|
149
|
+
})();
|
|
150
|
+
const guarded = new Promise((resolve) => {
|
|
151
|
+
let settled = false;
|
|
152
|
+
const done = () => {
|
|
153
|
+
if (settled)
|
|
154
|
+
return;
|
|
155
|
+
settled = true;
|
|
156
|
+
resolve();
|
|
157
|
+
};
|
|
158
|
+
const timer = setTimeout(() => {
|
|
159
|
+
console.warn(`[arc] module sync did not complete within ${timeoutMs}ms; proceeding anyway.`);
|
|
160
|
+
done();
|
|
161
|
+
}, timeoutMs);
|
|
162
|
+
timer?.unref?.();
|
|
163
|
+
run.then(done, done).finally(() => clearTimeout(timer));
|
|
164
|
+
});
|
|
165
|
+
if (seq === syncSeq)
|
|
166
|
+
latestSync = guarded;
|
|
167
|
+
return guarded;
|
|
168
|
+
}
|
|
169
|
+
function awaitModuleSync() {
|
|
170
|
+
return latestSync ?? Promise.resolve();
|
|
171
|
+
}
|
|
122
172
|
// src/adapters/wire.ts
|
|
123
173
|
class Wire {
|
|
124
174
|
baseUrl;
|
|
@@ -4452,6 +4502,7 @@ class ScopedModel {
|
|
|
4452
4502
|
for (const listener4 of this.tokenListeners) {
|
|
4453
4503
|
listener4();
|
|
4454
4504
|
}
|
|
4505
|
+
return triggerModuleSync(this.scopeName);
|
|
4455
4506
|
}
|
|
4456
4507
|
getToken() {
|
|
4457
4508
|
return this.authAdapter.getToken();
|
|
@@ -5250,6 +5301,7 @@ class TokenCache {
|
|
|
5250
5301
|
}
|
|
5251
5302
|
export {
|
|
5252
5303
|
view,
|
|
5304
|
+
triggerModuleSync,
|
|
5253
5305
|
token,
|
|
5254
5306
|
stringEnum,
|
|
5255
5307
|
string,
|
|
@@ -5257,6 +5309,7 @@ export {
|
|
|
5257
5309
|
secureDataStorage,
|
|
5258
5310
|
route,
|
|
5259
5311
|
resolveQueryChange,
|
|
5312
|
+
registerModuleSyncProvider,
|
|
5260
5313
|
record,
|
|
5261
5314
|
or,
|
|
5262
5315
|
observeQueries,
|
|
@@ -5267,6 +5320,7 @@ export {
|
|
|
5267
5320
|
mergeUnsafe,
|
|
5268
5321
|
listener,
|
|
5269
5322
|
id,
|
|
5323
|
+
hasModuleSyncProvider,
|
|
5270
5324
|
file,
|
|
5271
5325
|
extractDatabaseAgnosticSchema,
|
|
5272
5326
|
executeDescriptor,
|
|
@@ -5285,6 +5339,7 @@ export {
|
|
|
5285
5339
|
buildContextAccessor,
|
|
5286
5340
|
boolean,
|
|
5287
5341
|
blob,
|
|
5342
|
+
awaitModuleSync,
|
|
5288
5343
|
array,
|
|
5289
5344
|
arcFunctionWithCtx,
|
|
5290
5345
|
arcFunction,
|
|
@@ -21,7 +21,15 @@ export declare class ScopedModel<Context extends ArcContextAny> implements Model
|
|
|
21
21
|
private readonly scopedAdapters;
|
|
22
22
|
private tokenListeners;
|
|
23
23
|
constructor(parent: ModelLike<Context>, scopeName: string);
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Set this scope's token. Returns a promise that resolves when the platform
|
|
26
|
+
* module loader has synced the chunks for the new token state (so routes in
|
|
27
|
+
* newly-gated modules exist before the caller navigates). With no module-sync
|
|
28
|
+
* provider registered (server, SSR, tests, app-without-platform) it resolves
|
|
29
|
+
* immediately — preserving the historical synchronous semantics for the many
|
|
30
|
+
* server-side `setToken(rawToken)` callers that ignore the return value.
|
|
31
|
+
*/
|
|
32
|
+
setToken(token: string | null): Promise<void>;
|
|
25
33
|
getToken(): string | null;
|
|
26
34
|
getDecoded(): DecodedToken | null;
|
|
27
35
|
getParams(): Record<string, any> | null;
|
package/package.json
CHANGED