Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 37 additions & 22 deletions build/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3094,6 +3094,7 @@ import path6 from "node:path";
import process9 from "node:process";
import { join as join72 } from "node:path";
import process10 from "node:process";
import { pathToFileURL } from "node:url";
import process11 from "node:process";
import process13 from "node:process";

Expand Down Expand Up @@ -87653,7 +87654,7 @@ var require_thread_stream = __commonJS({
var { EventEmitter } = __require2("events");
var { Worker: Worker2 } = __require2("worker_threads");
var { join: join92 } = __require2("path");
var { pathToFileURL } = __require2("url");
var { pathToFileURL: pathToFileURL2 } = __require2("url");
var { wait } = require_wait2();
var {
WRITE_INDEX,
Expand Down Expand Up @@ -87693,7 +87694,7 @@ var require_thread_stream = __commonJS({
...opts.workerOpts,
trackUnmanagedFds: false,
workerData: {
filename: filename.indexOf("file://") === 0 ? filename : pathToFileURL(filename).href,
filename: filename.indexOf("file://") === 0 ? filename : pathToFileURL2(filename).href,
dataBuf: stream[kImpl].dataBuf,
stateBuf: stream[kImpl].stateBuf,
workerData: {
Expand Down Expand Up @@ -108555,15 +108556,14 @@ var Hapi2;
var import_inert2;
var import_npm_chalk3;
var import_npm_nconf6;
var cheloniaAppManifest;
var ARCHIVE_MODE2;
var ownerSizeTotalWorker;
var creditsWorker;
var CONTRACTS_VERSION;
var GI_VERSION;
var hapi;
var appendToOrphanedNamesIndex;
var init_server = __esm({
"src/serve/server.ts"() {
async "src/serve/server.ts"() {
"use strict";
init_SPMessage();
init_chelonia();
Expand All @@ -108585,14 +108585,22 @@ var init_server = __esm({
init_pubsub2();
init_push();
import_npm_nconf6 = __toESM(require_nconf());
cheloniaAppManifest = await (async () => {
try {
return (await import(pathToFileURL(join72(process10.cwd(), "chelonia.json")).toString(), {
with: { type: "json" }
})).default;
} catch {
console.warn("`chelonia.json` not found. Version information will be unavailable.");
}
})();
ARCHIVE_MODE2 = import_npm_nconf6.default.get("server:archiveMode");
if (CREDITS_WORKER_TASK_TIME_INTERVAL && OWNER_SIZE_TOTAL_WORKER_TASK_TIME_INTERVAL > CREDITS_WORKER_TASK_TIME_INTERVAL) {
process10.stderr.write("The size calculation worker must run more frequently than the credits worker for accurate billing");
process10.exit(1);
}
ownerSizeTotalWorker = ARCHIVE_MODE2 || !OWNER_SIZE_TOTAL_WORKER_TASK_TIME_INTERVAL ? void 0 : createWorker_default(join72(import.meta.dirname || ".", "serve", "ownerSizeTotalWorker.js"));
creditsWorker = ARCHIVE_MODE2 || !CREDITS_WORKER_TASK_TIME_INTERVAL ? void 0 : createWorker_default(join72(import.meta.dirname || ".", "serve", "creditsWorker.js"));
({ CONTRACTS_VERSION, GI_VERSION } = process10.env);
hapi = new Hapi2.Server({
// debug: false, // <- Hapi v16 was outputing too many unnecessary debug statements
// // v17 doesn't seem to do this anymore so I've re-enabled the logging
Expand Down Expand Up @@ -108874,8 +108882,10 @@ var init_server = __esm({
serverHandlers: {
connection(socket) {
const versionInfo = {
GI_VERSION: GI_VERSION || null,
CONTRACTS_VERSION: CONTRACTS_VERSION || null
appVersion: cheloniaAppManifest?.appVersion || null,
contractsVersion: cheloniaAppManifest?.contracts ? Object.fromEntries(
Object.entries(cheloniaAppManifest?.contracts).map(([k, v2]) => [k, v2.version])
) : null
};
socket.send(createNotification(NOTIFICATION_TYPE.VERSION_INFO, versionInfo));
}
Expand Down Expand Up @@ -109074,7 +109084,7 @@ var init_serve = __esm({
console.info(import_npm_chalk4.default.bold("backend startup sequence complete."));
resolve82();
});
Promise.resolve().then(() => (init_server(), server_exports)).catch(reject);
init_server().then(() => server_exports).catch(reject);
});
esm_default("okTurtles.events/once", SERVER_EXITING, () => {
esm_default("okTurtles.data/apply", PUBSUB_INSTANCE, function(pubsub) {
Expand Down Expand Up @@ -110607,10 +110617,11 @@ var module9 = {
return migrate(argv);
}
};
var RESERVED_FILE_CHARS = /[/\\:*?"<>|]/g;
var projectRoot;
var cheloniaConfig;
function sanitizeContractName(contractName) {
return contractName.replace(/[/\\:*?"<>|]/g, "_").replace(/\.\./g, "__");
return contractName.replace(RESERVED_FILE_CHARS, "_").replace(/\.\./g, "__");
}
async function pin(args) {
const version3 = args["manifest-version"];
Expand All @@ -110628,8 +110639,11 @@ async function pin(args) {
if (!existsSync(fullManifestPath)) {
exit(`Manifest file not found: ${manifestPath}`);
}
const { contractName, contractFiles, manifestVersion } = await parseManifest(fullManifestPath);
console.log(blue(`Contract name: ${contractName}`));
const { contractName, fullContractName, contractFiles, manifestVersion } = await parseManifest(fullManifestPath);
if (RESERVED_FILE_CHARS.test(manifestVersion)) {
exit(`Invalid manifest version: ${manifestVersion}`);
}
console.log(blue(`Contract name: ${fullContractName}`));
console.log(blue(`Manifest version: ${manifestVersion}`));
if (version3) {
if (version3 !== manifestVersion) {
Expand All @@ -110639,28 +110653,28 @@ async function pin(args) {
}
console.log(green(`\u2705 Version validation passed: ${version3}`));
}
const currentPinnedVersion = cheloniaConfig.contracts[contractName]?.version;
const currentPinnedVersion = cheloniaConfig.contracts[fullContractName]?.version;
if (currentPinnedVersion === manifestVersion) {
console.log(yellow(`\u2728 Contract ${contractName} is already pinned to version ${manifestVersion} - no action needed`));
console.log(yellow(`\u2728 Contract ${fullContractName} is already pinned to version ${manifestVersion} - no action needed`));
return;
}
if (currentPinnedVersion) {
console.log(cyan(`\u{1F4CC} Updating ${contractName} from version ${currentPinnedVersion} to ${manifestVersion}`));
console.log(cyan(`\u{1F4CC} Updating ${fullContractName} from version ${currentPinnedVersion} to ${manifestVersion}`));
} else {
console.log(cyan(`\u{1F4CC} Pinning ${contractName} to version ${manifestVersion} (first time)`));
console.log(cyan(`\u{1F4CC} Pinning ${fullContractName} to version ${manifestVersion} (first time)`));
}
const contractVersionDir = join62(projectRoot, "contracts", contractName, manifestVersion);
if (existsSync(contractVersionDir)) {
if (!args.overwrite) {
exit(`Version ${manifestVersion} already exists for contract ${contractName}. Use --overwrite to replace it.`);
exit(`Version ${manifestVersion} already exists for contract ${fullContractName}. Use --overwrite to replace it.`);
}
console.log(yellow(`Version ${manifestVersion} already exists for ${contractName} - checking files...`));
console.log(yellow(`Version ${manifestVersion} already exists for ${fullContractName} - checking files...`));
} else {
await createVersionDirectory(contractName, manifestVersion);
}
await copyContractFiles(contractFiles, manifestPath, contractName, manifestVersion, args);
await updateCheloniaConfig(contractName, manifestVersion, manifestPath);
console.log(green(`\u2705 Successfully pinned ${contractName} to version ${version3}`));
await updateCheloniaConfig(fullContractName, contractName, manifestVersion, manifestPath);
console.log(green(`\u2705 Successfully pinned ${contractName} to version ${manifestVersion}`));
console.log(gray(`Location: contracts/${contractName}/${manifestVersion}/`));
} catch (error2) {
exit(error2);
Expand All @@ -110682,6 +110696,7 @@ async function parseManifest(manifestPath) {
return {
contractName,
manifestVersion,
fullContractName,
contractFiles: {
main: mainFile,
slim: slimFile
Expand Down Expand Up @@ -110746,10 +110761,10 @@ async function loadCheloniaConfig() {
cheloniaConfig.contracts = {};
}
}
async function updateCheloniaConfig(contractName, version3, manifestPath) {
async function updateCheloniaConfig(fullContractName, contractName, version3, manifestPath) {
const manifestFileName = basename42(manifestPath);
const pinnedManifestPath = `contracts/${contractName}/${version3}/${manifestFileName}`;
cheloniaConfig.contracts[contractName] = {
cheloniaConfig.contracts[fullContractName] = {
version: version3,
path: pinnedManifestPath
};
Expand Down
34 changes: 21 additions & 13 deletions src/pin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import process from 'node:process'
import type { ArgumentsCamelCase, CommandModule } from './commands.ts'
import { exit } from './utils.ts'

const RESERVED_FILE_CHARS = /[/\\:*?"<>|]/g

type Params = { overwrite: boolean, 'dir'?: string, 'manifest-version'?: string, manifest: string }

let projectRoot: string
let cheloniaConfig: { contracts: Record<string, { version: string, path: string }> }

function sanitizeContractName (contractName: string): string {
return contractName.replace(/[/\\:*?"<>|]/g, '_').replace(/\.\./g, '__')
return contractName.replace(RESERVED_FILE_CHARS, '_').replace(/\.\./g, '__')
}

export async function pin (args: ArgumentsCamelCase<Params>): Promise<void> {
Expand All @@ -36,8 +38,13 @@ export async function pin (args: ArgumentsCamelCase<Params>): Promise<void> {
exit(`Manifest file not found: ${manifestPath}`)
}

const { contractName, contractFiles, manifestVersion } = await parseManifest(fullManifestPath)
console.log(colors.blue(`Contract name: ${contractName}`))
const { contractName, fullContractName, contractFiles, manifestVersion } = await parseManifest(fullManifestPath)

if (RESERVED_FILE_CHARS.test(manifestVersion)) {
exit(`Invalid manifest version: ${manifestVersion}`)
}
Comment thread
corrideat marked this conversation as resolved.
Outdated

console.log(colors.blue(`Contract name: ${fullContractName}`))
console.log(colors.blue(`Manifest version: ${manifestVersion}`))

if (version) {
Expand All @@ -50,33 +57,33 @@ export async function pin (args: ArgumentsCamelCase<Params>): Promise<void> {
console.log(colors.green(`✅ Version validation passed: ${version}`))
}

const currentPinnedVersion = cheloniaConfig.contracts[contractName]?.version
const currentPinnedVersion = cheloniaConfig.contracts[fullContractName]?.version
if (currentPinnedVersion === manifestVersion) {
console.log(colors.yellow(`✨ Contract ${contractName} is already pinned to version ${manifestVersion} - no action needed`))
console.log(colors.yellow(`✨ Contract ${fullContractName} is already pinned to version ${manifestVersion} - no action needed`))
return
}

if (currentPinnedVersion) {
console.log(colors.cyan(`📌 Updating ${contractName} from version ${currentPinnedVersion} to ${manifestVersion}`))
console.log(colors.cyan(`📌 Updating ${fullContractName} from version ${currentPinnedVersion} to ${manifestVersion}`))
} else {
console.log(colors.cyan(`📌 Pinning ${contractName} to version ${manifestVersion} (first time)`))
console.log(colors.cyan(`📌 Pinning ${fullContractName} to version ${manifestVersion} (first time)`))
}

const contractVersionDir = join(projectRoot, 'contracts', contractName, manifestVersion)

if (existsSync(contractVersionDir)) {
if (!args.overwrite) {
exit(`Version ${manifestVersion} already exists for contract ${contractName}. Use --overwrite to replace it.`)
exit(`Version ${manifestVersion} already exists for contract ${fullContractName}. Use --overwrite to replace it.`)
}
console.log(colors.yellow(`Version ${manifestVersion} already exists for ${contractName} - checking files...`))
console.log(colors.yellow(`Version ${manifestVersion} already exists for ${fullContractName} - checking files...`))
} else {
await createVersionDirectory(contractName, manifestVersion)
}

await copyContractFiles(contractFiles, manifestPath, contractName, manifestVersion, args)
await updateCheloniaConfig(contractName, manifestVersion, manifestPath)
await updateCheloniaConfig(fullContractName, contractName, manifestVersion, manifestPath)

console.log(colors.green(`✅ Successfully pinned ${contractName} to version ${version}`))
console.log(colors.green(`✅ Successfully pinned ${contractName} to version ${manifestVersion}`))
console.log(colors.gray(`Location: contracts/${contractName}/${manifestVersion}/`))
} catch (error) {
exit(error)
Expand All @@ -103,6 +110,7 @@ async function parseManifest (manifestPath: string) {
return {
contractName,
manifestVersion,
fullContractName,
contractFiles: {
main: mainFile,
slim: slimFile
Expand Down Expand Up @@ -195,11 +203,11 @@ async function loadCheloniaConfig () {
}
}

async function updateCheloniaConfig (contractName: string, version: string, manifestPath: string) {
async function updateCheloniaConfig (fullContractName: string, contractName: string, version: string, manifestPath: string) {
const manifestFileName = basename(manifestPath)
const pinnedManifestPath = `contracts/${contractName}/${version}/${manifestFileName}`

cheloniaConfig.contracts[contractName] = {
cheloniaConfig.contracts[fullContractName] = {
version,
path: pinnedManifestPath
}
Expand Down
20 changes: 16 additions & 4 deletions src/serve/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,20 @@ import {
type WSS
} from './pubsub.ts'
import { addChannelToSubscription, deleteChannelFromSubscription, postEvent, pushServerActionhandlers, subscriptionInfoWrapper } from './push.ts'
import { pathToFileURL } from 'node:url'
// @deno-types="npm:@types/nconf"
import nconf from 'npm:nconf'

const cheloniaAppManifest = await (async () => {
try {
return (await import(pathToFileURL(join(process.cwd(), 'chelonia.json')).toString(), {
Comment thread
corrideat marked this conversation as resolved.
Outdated
with: { type: 'json' }
})).default
} catch {
console.warn('`chelonia.json` not found. Version information will be unavailable.')
Comment thread
corrideat marked this conversation as resolved.
Outdated
}
})()

const ARCHIVE_MODE = nconf.get('server:archiveMode')

if (CREDITS_WORKER_TASK_TIME_INTERVAL && OWNER_SIZE_TOTAL_WORKER_TASK_TIME_INTERVAL > CREDITS_WORKER_TASK_TIME_INTERVAL) {
Expand All @@ -49,8 +60,6 @@ const creditsWorker = ARCHIVE_MODE || !CREDITS_WORKER_TASK_TIME_INTERVAL
? undefined
: createWorker(join(import.meta.dirname || '.', import.meta.workerDir || '.', 'creditsWorker.js'))

const { CONTRACTS_VERSION, GI_VERSION } = process.env

// Dynamic runtime import to bypass bundling issues with npm: specifier
const hapi = new Hapi.Server({
// debug: false, // <- Hapi v16 was outputing too many unnecessary debug statements
Expand Down Expand Up @@ -426,8 +435,11 @@ sbp('okTurtles.data/set', PUBSUB_INSTANCE, createServer(hapi.listener, {
serverHandlers: {
connection (socket) {
const versionInfo = {
GI_VERSION: GI_VERSION || null,
CONTRACTS_VERSION: CONTRACTS_VERSION || null
appVersion: cheloniaAppManifest?.appVersion || null,
contractsVersion: cheloniaAppManifest?.contracts ? Object.fromEntries(
Object.entries(cheloniaAppManifest?.contracts)
.map(([k, v]) => [k, (v as Record<string, number | string>).version])
) : null
Comment on lines +449 to +453
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: does this design assume there will only be one app per server?

If so, that's clearly not a safe assumption (per the other open issues).

So I think we should address this somehow in this PR in a way that either fully addresses it, or sets up future versions to address it properly.

I.e.:

  • It could fully address this by returning all apps
  • It could partially address this by passing in some server API version perhaps that clients could distinguish for figuring out what format to expect?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment there can only possibly be one app per server. Since this is being sent when a WS connection is established, I think this is fine (since presumably, a WS client will be for a single app).

I think it's low risk to make this assumption since, whichever changes are needed, they are limited to the server API and will not require changing contracts.

In addition, presumably, we'll want https://groupincome.app (as an example) to be a single app, regardless of how many apps the backend is running. I.e., the origin will identify the app.

With this in mind, these changes look safe and perhaps even future proof:

  1. We might need to refactor chel to serve multiple apps, but those changes don't seem related to the ones here (it'd mean, in this case, that we'd have to pick which cheloniaAppManifest we want --- now, there's a single choice)
  2. In the future, since we need to pick a way of choosing the cheloniaAppManifest (see previous point), we need a way to do it. If we do it based on origin, this already is future proof. If we do it some other way, we can change the WS protocol to add an app id selector (which would not require recreating contracts)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: does this design assume there will only be one app per server?

Currently, yes. But, I think little if anything will change from the client's perspective.

}
socket.send(createNotification(NOTIFICATION_TYPE.VERSION_INFO, versionInfo))
}
Expand Down
Loading