Enforce Typecheck #51
					 5 changed files with 80 additions and 54 deletions
				
			
		| 
						 | 
				
			
			@ -4,23 +4,27 @@ import { dispatchCall, isJSONRPCCall, type Call } from "./workerrpc";
 | 
			
		|||
 | 
			
		||||
function isServiceWorker(
 | 
			
		||||
  self: WorkerGlobalScope,
 | 
			
		||||
  // @ts-ignore: workaround for workbox logger.d.ts decl
 | 
			
		||||
): self is ServiceWorkerGlobalScope {
 | 
			
		||||
  return !!(self as unknown as ServiceWorkerGlobalScope).registration;
 | 
			
		||||
  return (
 | 
			
		||||
    (self as unknown as Record<string, unknown>)["serviceWorker"] instanceof
 | 
			
		||||
    ServiceWorker
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (isServiceWorker(self)) {
 | 
			
		||||
  cleanupOutdatedCaches();
 | 
			
		||||
  precacheAndRoute(self.__WB_MANIFEST, {
 | 
			
		||||
    cleanURLs: false,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // auto update
 | 
			
		||||
  self.skipWaiting();
 | 
			
		||||
  clientsClaim();
 | 
			
		||||
} else {
 | 
			
		||||
if (!isServiceWorker(self)) {
 | 
			
		||||
  throw new TypeError("This entry point must be run in a service worker");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cleanupOutdatedCaches();
 | 
			
		||||
precacheAndRoute(self.__WB_MANIFEST, {
 | 
			
		||||
  cleanURLs: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// auto update
 | 
			
		||||
self.skipWaiting();
 | 
			
		||||
clientsClaim();
 | 
			
		||||
 | 
			
		||||
export const Service = {
 | 
			
		||||
  ping() {},
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +33,9 @@ self.addEventListener("message", (event: MessageEvent<unknown>) => {
 | 
			
		|||
  const payload = event.data;
 | 
			
		||||
  if (typeof payload !== "object") return;
 | 
			
		||||
  if (isJSONRPCCall(payload as Record<string, unknown>)) {
 | 
			
		||||
    dispatchCall(Service, event as MessageEvent<Call<unknown>>);
 | 
			
		||||
    dispatchCall(
 | 
			
		||||
      Service,
 | 
			
		||||
      event as MessageEvent<Call<unknown>> & { source: MessageEventSource },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "lib": ["ESNext", "WebWorker"],
 | 
			
		||||
    "lib": ["WebWorker", "ESNext"],
 | 
			
		||||
  },
 | 
			
		||||
  "extends": ["../../tsconfig.super.json"],
 | 
			
		||||
  "include": ["./**/*.ts"],
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ export type Result<T, E> = JSONRPC & { id: string | number } & (
 | 
			
		|||
export function isJSONRPCResult(
 | 
			
		||||
  object: Record<string, unknown>,
 | 
			
		||||
): object is Result<unknown, unknown> {
 | 
			
		||||
  return object["jsonrpc"] === "2.0" && object["id"] && !object["method"];
 | 
			
		||||
  return !!(object["jsonrpc"] === "2.0" && object["id"] && !object["method"]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function isJSONRPCCall(
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +114,9 @@ export class ResultDispatcher {
 | 
			
		|||
    {
 | 
			
		||||
      const callback = this.map.get(id);
 | 
			
		||||
      if (!callback) return;
 | 
			
		||||
      // Now the callback is not undefined
 | 
			
		||||
 | 
			
		||||
      // Fast path
 | 
			
		||||
      if (typeof callback !== "boolean") {
 | 
			
		||||
        callback(message);
 | 
			
		||||
        this.map.delete(id);
 | 
			
		||||
| 
						 | 
				
			
			@ -121,15 +124,16 @@ export class ResultDispatcher {
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return new Promise<void>((resolve) => {
 | 
			
		||||
    const { promise, resolve } = Promise.withResolvers<undefined>();
 | 
			
		||||
 | 
			
		||||
    let retried = 0;
 | 
			
		||||
 | 
			
		||||
    const checkAndDispatch = () => {
 | 
			
		||||
      const callback = this.map.get(id);
 | 
			
		||||
      if (typeof callback !== "boolean") {
 | 
			
		||||
          callback(message);
 | 
			
		||||
        callback!(message); // the nullability is already checked before
 | 
			
		||||
        this.map.delete(id);
 | 
			
		||||
          resolve();
 | 
			
		||||
        resolve(undefined);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      setTimeout(checkAndDispatch, 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +146,8 @@ export class ResultDispatcher {
 | 
			
		|||
 | 
			
		||||
    // start the loop
 | 
			
		||||
    checkAndDispatch();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return promise;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createTypedCall<
 | 
			
		||||
| 
						 | 
				
			
			@ -159,14 +164,16 @@ export class ResultDispatcher {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AnyService = Record<string, ((...args: unknown[]) => unknown) | undefined>
 | 
			
		||||
type AnyService = Record<string, (...args: unknown[]) => unknown>;
 | 
			
		||||
 | 
			
		||||
export async function dispatchCall<
 | 
			
		||||
  S extends AnyService,
 | 
			
		||||
>(service: S, event: MessageEvent<Call<unknown>>) {
 | 
			
		||||
export async function dispatchCall<S extends Partial<AnyService>>(
 | 
			
		||||
  service: S,
 | 
			
		||||
  event: MessageEvent<Call<unknown>> & { source: MessageEventSource },
 | 
			
		||||
) {
 | 
			
		||||
  try {
 | 
			
		||||
    const fn = service[event.data.method];
 | 
			
		||||
    if (!fn) {
 | 
			
		||||
      console.warn("requested unknown method", event.data.method, event.data);
 | 
			
		||||
      if (event.data.id)
 | 
			
		||||
        return event.source.postMessage({
 | 
			
		||||
          jsonrpc: "2.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -176,10 +183,12 @@ export async function dispatchCall<
 | 
			
		|||
            message: "Method not found",
 | 
			
		||||
          },
 | 
			
		||||
        } as Result<void, void>);
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const result = await fn(...event.data.params as unknown[]);
 | 
			
		||||
      const result = await fn(...(event.data.params as unknown[]));
 | 
			
		||||
 | 
			
		||||
      if (!event.data.id) return;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,11 @@
 | 
			
		|||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "strict": true,
 | 
			
		||||
    "target": "ESNext",
 | 
			
		||||
    "module": "esnext",
 | 
			
		||||
    "moduleResolution": "node",
 | 
			
		||||
    "allowSyntheticDefaultImports": true,
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "jsx": "preserve",
 | 
			
		||||
    "jsxImportSource": "solid-js",
 | 
			
		||||
    "types": ["vite/client", "vite-plugin-pwa/solid"],
 | 
			
		||||
    "noEmit": true,
 | 
			
		||||
    "isolatedModules": true,
 | 
			
		||||
    "resolveJsonModule": true,
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "~platform/*": ["./src/platform/*"],
 | 
			
		||||
      "~material/*": ["./src/material/*"]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
    "lib": ["ESNext", "DOM", "DOM.Iterable"]
 | 
			
		||||
  },
 | 
			
		||||
  "include": ["./src/**/*.ts", "./src/**/*.tsx", "./*.ts", "./types/**.ts"],
 | 
			
		||||
  "exclude": ["./src/serviceworker/**"],
 | 
			
		||||
  "extends": ["./tsconfig.super.json"]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								tsconfig.super.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tsconfig.super.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "strict": true,
 | 
			
		||||
    "target": "ESNext",
 | 
			
		||||
    "module": "esnext",
 | 
			
		||||
    "moduleResolution": "node",
 | 
			
		||||
    "allowSyntheticDefaultImports": true,
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "noEmit": true,
 | 
			
		||||
    "isolatedModules": true,
 | 
			
		||||
    "resolveJsonModule": true,
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "~platform/*": ["./src/platform/*"],
 | 
			
		||||
      "~material/*": ["./src/material/*"]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue