Skip to content

Commit

Permalink
Improved piral-update
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianRappl committed Jul 5, 2024
1 parent 666dd88 commit fd5ff47
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Fixed declaration merging for `moduleResolution` set to `bundler` (#701)
- Improved setup of event listeners in `piral-blazor` (#696)
- Improved fallback signature in `piral-translate`
- Improved streaming update in `piral-update`
- Updated dependencies
- Added `--strict-port` option to the `piral-cli` debug commands (#699)
- Added update capabilities to `piral-blazor` extension boundaries
Expand Down
79 changes: 58 additions & 21 deletions src/plugins/piral-update/src/actions.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,104 @@
import { withKey, GlobalStateContext, PiletEntries, PiletMetadata } from 'piral-core';
import { PiletUpdateMode } from './types';

function getPiletHash(pilet: PiletMetadata) {
interface HashEntry {
name: string;
version: string;
}

function getPiletHash(pilet: PiletMetadata): HashEntry {
return {
name: pilet.name,
version: pilet.version,
};
}

function sortPilets(a: { name: string }, b: { name: string }) {
function sortPilets(a: HashEntry, b: HashEntry) {
return a.name.localeCompare(b.name);
}

function computePiletHash(pilets: PiletEntries) {
return JSON.stringify(pilets.map(getPiletHash).sort(sortPilets));
}

export function rejectUpdate(ctx: GlobalStateContext) {
function reset(ctx: GlobalStateContext) {
ctx.dispatch((state) => ({
...state,
updatability: {
...state.updatability,
target: [],
added: [],
updated: [],
removed: [],
active: false,
},
}));
}

export function approveUpdate(ctx: GlobalStateContext) {
const pilets = ctx.readState((s) => s.updatability.target);
async function apply(ctx: GlobalStateContext) {
const { added, removed, updated } = ctx.readState((s) => s.updatability);

for (const pilet of pilets) {
ctx.addPilet(pilet);
for (const pilet of removed) {
await ctx.removePilet(pilet);
}

ctx.rejectUpdate();
for (const pilet of updated) {
await ctx.removePilet(pilet.name);
await ctx.addPilet(pilet);
}

for (const pilet of added) {
await ctx.addPilet(pilet);
}
}

export function rejectUpdate(ctx: GlobalStateContext) {
reset(ctx);
}

export function approveUpdate(ctx: GlobalStateContext) {
apply(ctx);
reset(ctx);
}

export function checkForUpdates(ctx: GlobalStateContext, pilets: PiletEntries) {
const checkHash = computePiletHash(pilets);
const lastHash = ctx.readState((s) => s.updatability.lastHash || computePiletHash(s.modules));
const currentHash = computePiletHash(pilets);
const currentPilets = ctx.readState((s) => s.modules);
const previousHash = ctx.readState((s) => s.updatability.lastHash || computePiletHash(currentPilets));

if (checkHash !== lastHash) {
if (currentHash !== previousHash) {
const currentModes = ctx.readState((s) => s.registry.updatability);
const piletNames = Object.keys(currentModes);
const blocked = piletNames.filter((m) => currentModes[m].mode === 'block');
const ask = piletNames.filter((m) => currentModes[m].mode === 'ask');
const target = pilets.filter((pilet) => !blocked.includes(pilet.name));
const active = ask.length > 0;
const currentPiletNames = currentPilets.map((m) => m.name);
const isPending = (name: string) => currentModes[name]?.mode === 'ask';
const isNotBlocked = (name: string) => currentModes[name]?.mode !== 'block';

const added = pilets.filter((m) => !currentPiletNames.includes(m.name));
const removed = currentPiletNames.filter((m) => !pilets.some((p) => p.name === m) && isNotBlocked(m));
const updated = pilets.filter((pilet, i) => {
if ('version' in pilet && isNotBlocked(pilet.name)) {
const version = currentPilets.find((m) => m.name)?.version;
return !!version && version !== pilet.version;
}

return false;
});

ctx.dispatch((state) => {
const anyPendingDecision = removed.some((m) => isPending(m)) || updated.some((m) => isPending(m.name));

// no need to ask for approval
if (!active) {
if (!anyPendingDecision) {
// automatically start the update in the next cycle
setTimeout(ctx.approveUpdate, 0);
}

return {
...state,
updatability: {
active,
lastHash: checkHash,
target,
active: anyPendingDecision,
lastHash: currentHash,
added,
removed,
updated,
},
};
});
Expand Down
6 changes: 5 additions & 1 deletion src/plugins/piral-update/src/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export function createUpdateApi(config: UpdateConfig = {}): PiralPlugin<PiletUpd
updatability: {
active: false,
lastHash: undefined,
target: [],
added: [],
removed: [],
updated: [],
},
}));

Expand All @@ -43,6 +45,8 @@ export function createUpdateApi(config: UpdateConfig = {}): PiralPlugin<PiletUpd
return (_, target) => {
const pilet = target.name;

target.config

return {
canUpdate(mode) {
context.setUpdateMode(pilet, mode);
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/piral-update/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export interface UpdateModeRegistration {
export interface UpdatabilityState {
active: boolean;
lastHash: string;
target: PiletEntries;
added: PiletEntries;
removed: Array<string>;
updated: PiletEntries;
}

export interface ListenCallback {
Expand Down

0 comments on commit fd5ff47

Please sign in to comment.