In terms of type inference
None. I think the advantages are realized in areas beyond type inference. If you enjoy zod
+ zodern:relay
, then I think that’s great. At this time, I can’t say that end-to-end type safety is an explicit goal of mine. Though if others see the benefits of the jam
packages and they aren’t meeting their type safety desires, I’d welcome help in improving them to get them to the point they’d like. 
cross validation
I’m not entirely sure what you mean by this but hopefully the examples below will help.
jam:easy-schema
mostly originated from needing a schema for a particular project. I didn’t love the existing solutions – overly complicated, too heavy on the client, hard-to-grok syntax etc. I wanted something much more lightweight on the client that also adhered to Meteor’s founding principles, primarily:
- Latency Compensation (aka Optimistic UI)
- Simplicity Equals Productivity
- Embrace the Ecosystem
It replaces the simpl-schema
+ collection2
combo and adds more features – e.g. automatic Mongo JSON Schema and automatic inferred types – while being simpler (in concepts you need to understand) and all contained in a dramatically smaller footprint.
jam:method
seeks to be a worthy successor to ValidatedMethod
and adds more features that have been long requested of Meteor.methods
while maintaining its core principles.
To be clear, the two packages can be used independently of each other, but combined things get a lot more powerful. Simply call functions. No excessive boilerplating needed.
For example, here’s how I can set things up:
// in a shared file imported into main.js on client and server
// this is optional, but helps to write less code :)
import { EasySchema, has } from 'meteor/jam:easy-schema';
const base = {
createdAt: Date[has].default(Date.now), // static
updatedAt: Date[has].default(() => Date.now()) // dynamic
}
EasySchema.configure({ base }); // all schemas will inherit this base
// /imports/api/todos/schema.js
export const schema = { // Create one schema to use across methods. If needed, create one-off schemas for particular methods.
_id: ID,
text: String[has].min(1),
owner: ID[has].default(Meteor.userId), // if you prefer, you can avoid using .default and set things explicitly within a method of course
username: String[has].default(Meteor.user),
done: Boolean[has].default(false),
isPrivate: Boolean[has].default(false)
}
// /imports/api/todos/methods.js
import { server } from 'meteor/jam:method';
export const create = async ({ text }) => {
return Todos.insertAsync({ text });
};
export const edit = async ({ _id, text }) => {
await checkPrivacy(_id);
return Todos.updateAsync({ _id }, { $set: { text } });
};
export const erase = async ({ _id }) => {
await checkPrivacy(_id);
return Todos.removeAsync({ _id });
};
// this one is callable from the client but executes only on the server
export const restore = server(async ({ text }) => {
return Todos.restoreAsync({ text });
});
// ... other Todos methods
// /imports/api/todos/collection.js
import { schema } from './schema';
import * as methods from './methods';
export const Todos = new Mongo.Collection('todos');
Todos.attachSchema(schema);
Todos.attachMethods(methods); // these can also be attached dynamically to reduce client bundle size
svelte frontend
<script>
import { Todos } from '/imports/api/todos/collection';
import { setupForm } from './form.js';
const { form, method } = setupForm(); // this is kind of like Superforms but much simpler and lightweight
</script>
<form use:method={Todos.create}>
<input name='text' type='text' placeholder='add new todo' required />
{#if $form.errors?.text}<small class='error'>{$form.errors.text}</small>{/if}
</form>
Now let’s say you’re interested in creating an offline-first app. Well you can add jam:offline
and either jam:archive
or jam:soft-delete
and you’re good to go because of the jam:method
integration.
I think the integration between them all makes things really powerful with minimal effort.