clumsy-graphics
Version:
a tool for rapidly developing animations where frames are described using svg elements à la react 🙃
246 lines (245 loc) • 14.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.clientServerEventHandlerSaga = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const IO = __importStar(require("io-ts"));
const path_1 = __importDefault(require("path"));
const server_1 = __importDefault(require("react-dom/server"));
const decodeData_1 = require("../../helpers/decodeData");
const storeEffects_1 = require("../helpers/storeEffects");
const GraphicsRendererProcessStateRequestQueryParams_1 = require("../models/GraphicsRendererProcessStateRequestQueryParams");
function* clientServerEventHandlerSaga(api) {
const { clientServerEventChannel, generatedAssetsDirectoryPath, animationModulePath, numberOfFrameRendererWorkers, clientPageBundle, localStorageSessionCacheId, } = api;
while (true) {
const someClientServerEvent = yield* (0, storeEffects_1.takeEvent)(clientServerEventChannel);
switch (someClientServerEvent.eventType) {
case 'clientServerListening':
break;
case 'clientRequestsGraphicsRendererProcessState':
yield* (0, storeEffects_1.spawn)(clientRequestsGraphicsRendererProcessStateHandler, {
generatedAssetsDirectoryPath,
animationModulePath,
numberOfFrameRendererWorkers,
clientRequest: someClientServerEvent.eventPayload.clientRequest,
serverResponse: someClientServerEvent.eventPayload.serverResponse,
});
break;
case 'clientRequestsGraphicAsset':
yield* (0, storeEffects_1.spawn)(clientRequestsGraphicAssetHandler, {
clientRequest: someClientServerEvent.eventPayload.clientRequest,
serverResponse: someClientServerEvent.eventPayload.serverResponse,
});
break;
case 'clientRequestsPage':
yield* (0, storeEffects_1.spawn)(clientRequestsPageHandler, {
clientPageBundle,
localStorageSessionCacheId,
serverResponse: someClientServerEvent.eventPayload.serverResponse,
});
break;
}
}
}
exports.clientServerEventHandlerSaga = clientServerEventHandlerSaga;
function* clientRequestsGraphicsRendererProcessStateHandler(api) {
const { serverResponse, clientRequest, generatedAssetsDirectoryPath, animationModulePath, numberOfFrameRendererWorkers, } = api;
const currentAnimationModuleBundlerState = yield* (0, storeEffects_1.select)((currentAnimationDevelopmentState) => currentAnimationDevelopmentState.animationModuleBundlerState);
try {
switch (currentAnimationModuleBundlerState.bundlerStatus) {
case 'bundlerInitializing':
serverResponse.sendStatus(204);
break;
case 'bundlerActive':
const { graphicsRendererProcessStateRequestQueryParams } = yield* (0, storeEffects_1.call)(getGraphicsRendererProcessStateRequestQueryParams, {
clientRequestQueryData: clientRequest.query,
});
const { specifiedClientGraphicsRendererProcessState } = getSpecifiedClientGraphicsRendererProcessState({
currentAnimationModuleBundlerState,
graphicsRendererProcessStateRequestQueryParams,
});
serverResponse.statusCode = 200;
serverResponse.setHeader('Content-Type', 'application/json');
serverResponse.send(JSON.stringify(specifiedClientGraphicsRendererProcessState));
if (specifiedClientGraphicsRendererProcessState.buildStatus ===
'validBuild' &&
specifiedClientGraphicsRendererProcessState.graphicsRendererProcessStatus ===
'processInitializing') {
const { graphicsRendererProcessCommandString, graphicAssetPathKey, graphicAssetPath, graphicAssetUrlResult, } = getPartialSpawnGraphicsRendererProcessActionPayload({
generatedAssetsDirectoryPath,
animationModulePath,
numberOfFrameRendererWorkers,
graphicsRendererProcessStateRequestQueryParams,
currentBundleSessionVersion: currentAnimationModuleBundlerState.buildVersion,
});
yield* (0, storeEffects_1.put)({
type: 'spawnGraphicsRendererProcess',
actionPayload: {
graphicsRendererProcessCommandString,
graphicAssetPathKey,
graphicAssetPath,
graphicAssetUrlResult,
buildVersion: currentAnimationModuleBundlerState.buildVersion,
graphicsRendererProcessKey: graphicsRendererProcessStateRequestQueryParams.graphicsRendererProcessKey,
},
});
}
break;
}
}
catch (queryParamsError) {
if (queryParamsError instanceof Error) {
serverResponse.statusCode = 400;
serverResponse.send(`${queryParamsError}`);
}
else {
serverResponse.sendStatus(500);
}
}
}
async function getGraphicsRendererProcessStateRequestQueryParams(api) {
const { clientRequestQueryData } = api;
const graphicsRendererProcessStateRequestQueryParams = await (0, decodeData_1.decodeData)({
targetCodec: GraphicsRendererProcessStateRequestQueryParams_1.GraphicsRendererProcessStateRequestQueryParamsCodec,
inputData: clientRequestQueryData,
});
return { graphicsRendererProcessStateRequestQueryParams };
}
function getPartialSpawnGraphicsRendererProcessActionPayload(api) {
const { generatedAssetsDirectoryPath, animationModulePath, graphicsRendererProcessStateRequestQueryParams, currentBundleSessionVersion, numberOfFrameRendererWorkers, } = api;
const generatedAssetsDirectoryAbsolutePath = path_1.default.resolve(generatedAssetsDirectoryPath);
const animationModuleAbsolutePath = path_1.default.resolve(animationModulePath);
if (graphicsRendererProcessStateRequestQueryParams.graphicsRendererProcessKey.startsWith('animation')) {
const animationAssetFilename = `${currentBundleSessionVersion}.mp4`;
const animationMp4OutputPath = path_1.default.join(generatedAssetsDirectoryAbsolutePath, animationAssetFilename);
return {
graphicAssetPathKey: animationAssetFilename,
graphicAssetPath: animationMp4OutputPath,
graphicAssetUrlResult: `/asset/${animationAssetFilename}`,
graphicsRendererProcessCommandString: `_clumsy-graphics renderAnimation --animationModulePath=${animationModuleAbsolutePath} --animationMp4OutputPath=${animationMp4OutputPath} --numberOfFrameRendererWorkers=${numberOfFrameRendererWorkers}`,
};
}
else if (graphicsRendererProcessStateRequestQueryParams.graphicsRendererProcessKey.startsWith('frame')) {
const frameIndex = Number(graphicsRendererProcessStateRequestQueryParams.graphicsRendererProcessKey.replace('frame/', ''));
const frameAssetFilename = `${currentBundleSessionVersion}_${frameIndex}.png`;
const frameFileOutputPath = path_1.default.join(generatedAssetsDirectoryAbsolutePath, frameAssetFilename);
return {
graphicAssetPathKey: frameAssetFilename,
graphicAssetPath: frameFileOutputPath,
graphicsRendererProcessCommandString: `_clumsy-graphics renderAnimationFrame --animationModulePath=${animationModuleAbsolutePath} --frameIndex=${frameIndex} --frameFileOutputPath=${frameFileOutputPath}`,
graphicAssetUrlResult: `/asset/${frameAssetFilename}`,
};
}
else {
throw new Error('wtf? getPartialSpawnGraphicsRendererProcessActionPayload');
}
}
function getSpecifiedClientGraphicsRendererProcessState(api) {
const { graphicsRendererProcessStateRequestQueryParams, currentAnimationModuleBundlerState, } = api;
const specifiedGraphicsRendererProcessState = currentAnimationModuleBundlerState.graphicsRendererProcessStates[graphicsRendererProcessStateRequestQueryParams.graphicsRendererProcessKey];
if (currentAnimationModuleBundlerState.buildStatus === 'validBuild' &&
!specifiedGraphicsRendererProcessState) {
const { getFrameDescription, ...clientAnimationModule } = currentAnimationModuleBundlerState.animationModule;
return {
specifiedClientGraphicsRendererProcessState: {
buildVersion: currentAnimationModuleBundlerState.buildVersion,
buildStatus: currentAnimationModuleBundlerState.buildStatus,
graphicsRendererProcessKey: graphicsRendererProcessStateRequestQueryParams.graphicsRendererProcessKey,
animationModule: clientAnimationModule,
graphicsRendererProcessStatus: 'processInitializing',
graphicsRendererProcessStdoutLog: '',
},
};
}
else if (currentAnimationModuleBundlerState.buildStatus === 'validBuild' &&
specifiedGraphicsRendererProcessState) {
const { spawnedGraphicsRendererProcess, ...someSpecifiedClientGraphicsRendererProcessState } = specifiedGraphicsRendererProcessState;
const { getFrameDescription, ...clientAnimationModule } = currentAnimationModuleBundlerState.animationModule;
return {
specifiedClientGraphicsRendererProcessState: {
...someSpecifiedClientGraphicsRendererProcessState,
buildVersion: currentAnimationModuleBundlerState.buildVersion,
buildStatus: currentAnimationModuleBundlerState.buildStatus,
animationModule: clientAnimationModule,
},
};
}
else if (currentAnimationModuleBundlerState.buildStatus === 'invalidBuild') {
return {
specifiedClientGraphicsRendererProcessState: {
buildVersion: currentAnimationModuleBundlerState.buildVersion,
buildStatus: currentAnimationModuleBundlerState.buildStatus,
buildErrorMessage: currentAnimationModuleBundlerState.buildErrorMessage,
},
};
}
else {
throw new Error('wtf? getSpecifiedClientGraphicsRendererProcessState');
}
}
function* clientRequestsGraphicAssetHandler(api) {
const { clientRequest, serverResponse } = api;
const clientRequestRouteParams = yield* (0, storeEffects_1.call)(() => (0, decodeData_1.decodeData)({
targetCodec: IO.exact(IO.type({
assetFilename: IO.string,
})),
inputData: clientRequest.params,
}));
const currentAvailableAssetsFilePathMap = yield* (0, storeEffects_1.select)((currentAnimationDevelopmentState) => currentAnimationDevelopmentState.availableAssetsFilePathMap);
const targetAssetFilepath = currentAvailableAssetsFilePathMap[clientRequestRouteParams.assetFilename];
if (targetAssetFilepath) {
const targetAssetMimeType = getTargetAssetMimeType({
targetAssetFilepath,
});
serverResponse.setHeader('Content-Type', targetAssetMimeType);
serverResponse.sendFile(targetAssetFilepath);
}
else {
serverResponse.sendStatus(404);
}
}
function getTargetAssetMimeType(api) {
const { targetAssetFilepath } = api;
const assetType = targetAssetFilepath.split(/\.(svg|png|mp4|gif)$/, 2)[1];
switch (assetType) {
case 'svg':
return 'image/svg+xml';
case 'png':
return 'image/png';
case 'mp4':
return 'video/mp4';
case 'gif':
return 'image/gif';
default:
throw new Error('wtf? getTargetAssetMimeType');
}
}
function* clientRequestsPageHandler(api) {
const { serverResponse, localStorageSessionCacheId, clientPageBundle } = api;
serverResponse.statusCode = 200;
serverResponse.setHeader('Content-Type', 'text/html');
serverResponse.send(server_1.default.renderToStaticMarkup((0, jsx_runtime_1.jsxs)("html", Object.assign({ lang: 'en' }, { children: [(0, jsx_runtime_1.jsxs)("head", { children: [(0, jsx_runtime_1.jsx)("meta", { charSet: 'utf-8' }, void 0), (0, jsx_runtime_1.jsx)("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }, void 0), (0, jsx_runtime_1.jsx)("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }, void 0), (0, jsx_runtime_1.jsx)("link", { rel: "preconnect", href: "https://fonts.gstatic.com" }, void 0), (0, jsx_runtime_1.jsx)("link", { href: "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap", rel: "stylesheet" }, void 0)] }, void 0), (0, jsx_runtime_1.jsx)("body", { children: (0, jsx_runtime_1.jsx)("script", { id: 'client-page-bundle-script', "data-local-storage-session-cache-id": localStorageSessionCacheId, dangerouslySetInnerHTML: {
__html: clientPageBundle,
} }, void 0) }, void 0)] }), void 0)));
}