Milestone 0: import zappier billing, Verae middleware, and Zapier research

Compose-ready workspace: packages/zappier (rate card, portal, Stripe),
packages/verae-zapier-middleware (timestamp + NATS), packages/verae-zapier
(CLI app), vendor/zapier-platform, and research/zapier vendor corpus.

Gate 0 structure checks pass. Product code and research are not yet wired.
This commit is contained in:
George Lambert 2026-09-09 02:37:36 -04:00
commit b4150c8250
1364 changed files with 6814366 additions and 0 deletions

View file

@ -0,0 +1,62 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# environment variables file
.env
.environment
# next.js build output
.next

View file

@ -0,0 +1,30 @@
# search-or-create
This Zapier integration project is generated by the `zapier-platform init` CLI command.
These are what you normally do next:
```bash
# Install dependencies
npm install # or you can use yarn
# Run tests
zapier-platform test
# Register the integration on Zapier if you haven't
zapier-platform register "App Title"
# Or you can link to an existing integration on Zapier
zapier-platform link
# Push it to Zapier
zapier-platform push
```
Find out more on the latest docs: https://github.com/zapier/zapier-platform/blob/main/packages/cli/README.md.
# The "search-or-create" Template
An example showcasing a Search-or-Create.
![](https://cdn.zappy.app/5fc31d104c6bd0050c44510557b3b98f.png)

View file

@ -0,0 +1,34 @@
const perform = async (z, bundle) => {
const response = await z.request({
method: 'POST',
url: 'https://auth-json-server.zapier-staging.com/recipes',
body: {
name: bundle.inputData.name,
},
});
return response.data;
};
module.exports = {
key: 'recipe',
noun: 'Recipe',
display: {
label: 'Create Recipe',
description: 'Creates a recipe.',
},
operation: {
inputFields: [
{ key: 'name', required: true },
{ key: 'directions', required: false },
{ key: 'style', required: false },
],
perform,
sample: {
id: 1,
name: 'Test',
},
},
};

View file

@ -0,0 +1,35 @@
const RecipeCreate = require('./creates/recipe');
const RecipeSearch = require('./searches/recipe');
const addAuthHeader = (request, z, bundle) => {
// Hard-coded auth header just for demo. DON'T do auth like this for your
// production app!
request.headers['X-Api-Key'] = 'secret';
return request;
};
module.exports = {
version: require('./package.json').version,
platformVersion: require('zapier-platform-core').version,
beforeRequest: [addAuthHeader],
searches: { [RecipeSearch.key]: RecipeSearch },
creates: { [RecipeCreate.key]: RecipeCreate },
searchOrCreates: {
[RecipeSearch.key]: {
// The key must match the search
key: RecipeSearch.key, // same as above
display: {
// The label shows up when the search-or-create checkbox is checked.
// See https://cdn.zappy.app/5fc31d104c6bd0050c44510557b3b98f.png
label: 'Find or Create a Recipe',
description: 'x', // this is ignored
},
search: RecipeSearch.key,
create: RecipeCreate.key,
},
},
};

View file

@ -0,0 +1,16 @@
{
"name": "search-or-create",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest --testTimeout 10000"
},
"dependencies": {
"zapier-platform-core": "19.1.0"
},
"devDependencies": {
"jest": "^25.5.3"
},
"private": true
}

View file

@ -0,0 +1,35 @@
const perform = async (z, bundle) => {
const response = await z.request({
url: 'https://auth-json-server.zapier-staging.com/recipes',
params: {
name: bundle.inputData.name,
},
});
return response.data;
};
module.exports = {
key: 'recipe',
noun: 'Recipe',
display: {
label: 'Find Recipe',
description: 'Finds a recipe.',
},
operation: {
inputFields: [
{
key: 'name',
required: true,
helpText: 'Find the Recipe with this name.',
},
],
perform,
sample: {
id: 1,
name: 'Test',
},
},
};

View file

@ -0,0 +1,19 @@
/* globals describe, expect, test */
const zapier = require('zapier-platform-core');
const App = require('../index');
const appTester = zapier.createAppTester(App);
zapier.tools.env.inject();
describe('recipe', () => {
test('create with a name', async () => {
const bundle = { inputData: { name: 'Pancake' } };
const result = await appTester(
App.creates.recipe.operation.perform,
bundle,
);
expect(result.id).toBeTruthy();
expect(result.name).toBe('Pancake');
});
});

View file

@ -0,0 +1,24 @@
/* globals describe, expect, test */
const zapier = require('zapier-platform-core');
const App = require('../index');
const appTester = zapier.createAppTester(App);
zapier.tools.env.inject();
describe('recipe', () => {
test('search by name', async () => {
const bundle = { inputData: { name: 'name 1' } };
const results = await appTester(
App.searches.recipe.operation.perform,
bundle,
);
expect(results.length).toBeGreaterThan(0);
const firstRecipe = results[0];
expect(firstRecipe).toMatchObject({
id: '1',
name: 'name 1',
});
});
});