├── ui-src ├── public │ ├── favicon.ico │ ├── favicon2.ico │ ├── holo-logo.png │ ├── manifest.json │ └── index.html └── src │ ├── index.css │ ├── store.tsx │ ├── helpers.ts │ ├── index.tsx │ ├── components │ ├── App.css │ └── App.tsx │ ├── reducer.ts │ └── registerServiceWorker.ts ├── tsconfig.test.json ├── bridge_specs.json ├── .gitattributes ├── dna-src ├── tsconfig.json ├── properties_schema.json ├── hchc │ ├── app_code.json │ ├── ui_skin.json │ ├── app.json │ └── hchc.ts ├── profile │ ├── profile.json │ └── index.ts ├── bridge_replies │ └── bridge_replies.ts ├── comments │ ├── comments.json │ └── comments.ts ├── whoami │ └── whoami.ts ├── categories │ └── index.ts ├── bridge_request │ └── bridge_request.ts └── dna.json ├── config ├── jest │ ├── typescriptTransform.js │ ├── fileTransform.js │ └── cssTransform.js ├── polyfills.js ├── paths.js ├── env.js ├── webpackDevServer.config.js ├── webpack.config.dev.js └── webpack.config.prod.js ├── test ├── unit_tests │ ├── whoami_test.json │ ├── UISkin_test.json │ ├── DNACode_test.json │ ├── profile_test.json │ └── comments_test.json ├── bridge_test │ ├── myApps_test.json │ ├── bridge_test_.json │ ├── hchc_test.json │ ├── hchc_app_ui_test.json │ ├── UploadingUISkins_test.json │ └── UploadingDNACode_test.json └── categories.json ├── types.ts ├── .gitignore ├── tslint.json ├── @types ├── hchc │ └── index.d.ts └── holochain │ ├── index.d.ts │ └── holochain.d.ts ├── scripts ├── test.js ├── start.js └── build.js ├── tsconfig.json ├── LICENSE ├── .circleci └── config.yml ├── README.md └── package.json /ui-src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holochain/HCHC/HEAD/ui-src/public/favicon.ico -------------------------------------------------------------------------------- /ui-src/public/favicon2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holochain/HCHC/HEAD/ui-src/public/favicon2.ico -------------------------------------------------------------------------------- /ui-src/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /ui-src/public/holo-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holochain/HCHC/HEAD/ui-src/public/holo-logo.png -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /bridge_specs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "/home/zo-el/Documents/GitRepo/Holochain/HApps-Store/build-HApps", 4 | "Side": 1, 5 | "BridgeZome": "bridge_request" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /ui-src/src/store.tsx: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | import * as redux from 'redux'; 3 | 4 | import reducer from './reducer'; 5 | 6 | export default redux.createStore(reducer); 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Declare files that will always have CRLF line endings on checkout. 5 | dna-src/**/*.json text eol=lf 6 | -------------------------------------------------------------------------------- /dna-src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "outDir": "../build-HCHC/dna", 5 | "rootDir": ".", 6 | "typeRoots": ["../@types"], 7 | "newLine" : "lf" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /config/jest/typescriptTransform.js: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | 'use strict'; 4 | 5 | const tsJestPreprocessor = require('ts-jest/preprocessor'); 6 | 7 | module.exports = tsJestPreprocessor; 8 | -------------------------------------------------------------------------------- /dna-src/properties_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Properties Schema", 3 | "type": "object", 4 | "properties": { 5 | "description": { 6 | "type": "string" 7 | }, 8 | "language": { 9 | "type": "string" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/unit_tests/whoami_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [{ 3 | "Convey": "test for gettog", 4 | "Zome": "whoami", 5 | "FnName": "getAgent", 6 | "Input": "", 7 | "Output": {"Hash":"%agent%","Name":"%agentstr%"} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /types.ts: -------------------------------------------------------------------------------- 1 | 2 | export type Hello = string; 3 | 4 | export type AppState = { 5 | numClicks: number, 6 | texts: Array, 7 | }; 8 | 9 | export type ReduxAction 10 | = {type: 'INCREMENT'} 11 | | {type: 'DECREMENT'} 12 | | {type: 'FETCH_TEXTS', entries: Array} 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | agent.txt 2 | priv.key 3 | 4 | build/ 5 | build-HCHC/ 6 | ui/ 7 | dna/ 8 | node_modules/ 9 | .dist/ 10 | .cache/ 11 | npm-debug.log 12 | 13 | # OS generated files # 14 | ###################### 15 | .tags* 16 | .DS_Store 17 | ._* 18 | .Spotlight-V100 19 | .tags* 20 | .Trashes 21 | ehthumbs.db 22 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /ui-src/src/helpers.ts: -------------------------------------------------------------------------------- 1 | 2 | export const fetchPOST = (url: string, data?: any, extraPayload?: any): Promise => { 3 | extraPayload = extraPayload || {} 4 | return fetch(url, { 5 | ...extraPayload, 6 | body: JSON.stringify(data), 7 | headers: { 8 | 'Content-Type': 'application/json' 9 | }, 10 | method: 'post', 11 | }).then(r => r.json()) 12 | } 13 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /ui-src/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /ui-src/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | import './index.css'; 5 | 6 | import {Provider} from 'react-redux'; 7 | 8 | import registerServiceWorker from './registerServiceWorker'; 9 | import store from './store'; 10 | 11 | const root = 12 | 13 | 14 | 15 | ReactDOM.render( 16 | root, 17 | document.getElementById('root') as HTMLElement 18 | ); 19 | registerServiceWorker(); 20 | -------------------------------------------------------------------------------- /ui-src/src/components/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | margin: auto; 3 | text-align: center; 4 | } 5 | 6 | .App-logo { 7 | animation: App-logo-spin infinite 20s linear; 8 | height: 80px; 9 | } 10 | 11 | .App-header { 12 | background-color: #222; 13 | height: 150px; 14 | padding: 20px; 15 | color: white; 16 | } 17 | 18 | .App-title { 19 | font-size: 1.5em; 20 | } 21 | 22 | .App-intro { 23 | font-size: large; 24 | } 25 | 26 | @keyframes App-logo-spin { 27 | from { transform: rotate(0deg); } 28 | to { transform: rotate(360deg); } 29 | } 30 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"], 3 | "linterOptions": { 4 | "exclude": [ 5 | "config/**/*.js", 6 | "node_modules/**/*.ts" 7 | ] 8 | }, 9 | "rules": { 10 | "array-type": false, 11 | "interface-name": [true, "never-prefix"], 12 | "max-classes-per-file": false, 13 | "no-bitwise": false, 14 | "no-console": false, 15 | "no-reference": false, 16 | "interface-over-type-literal": false, 17 | "object-literal-sort-keys": false, 18 | "ordered-imports": false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/bridge_test/myApps_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [ 3 | { 4 | "Convey": "Test for adding a new app through the bridge", 5 | "Zome": "hchc", 6 | "FnName": "createApp", 7 | "Input": { 8 | "title": "Sakura", 9 | "description": "The is the details for the first app", 10 | "thumbnail": "IMG.jpg" 11 | }, 12 | "Output": "%h1%" 13 | }, 14 | { 15 | "Convey": "Test for get All the apps the user has created", 16 | "Zome": "hchc", 17 | "FnName": "getMyApps", 18 | "Input": "", 19 | "Regexp": "[.*]" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/bridge_test/bridge_test_.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [ 3 | { 4 | "Convey": "Test for adding a new app through the bridge", 5 | "Zome": "hchc", 6 | "FnName": "createApp", 7 | "Input": { 8 | "title": "Sakura", 9 | "description": "The is the details for the first app", 10 | "thumbnail": "IMG.jpg" 11 | }, 12 | "Output": "%h1%" 13 | }, 14 | { 15 | "Convey": "Test for get the hash of the app just pushed into the HApps Store", 16 | "Zome": "hchc", 17 | "FnName": "getAppDetails", 18 | "Input": { 19 | "app_hash": "%r1%" 20 | }, 21 | "Regexp": "[.*]" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /test/unit_tests/UISkin_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [ 3 | { 4 | "Convey": "Test for Adding the UI Skin link base to a Agent not the app (Storing it locally)", 5 | "Zome": "hchc", 6 | "FnName": "addUISkin", 7 | "Input": { 8 | "title":"Ichiraku Ramen App", 9 | "link": "/downloadLink.holo.ui.link", 10 | "thumbnail":"/images.json", 11 | "app_hash":"%agent%" 12 | }, 13 | "Output": "%h1%" 14 | }, 15 | { 16 | "Convey": "Test to get the UI Skin ", 17 | "Zome": "hchc", 18 | "FnName": "getUISkin", 19 | "Input": { 20 | "app_hash": "%agent%" 21 | }, 22 | "Regexp": ".*" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /test/unit_tests/DNACode_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [ 3 | { 4 | "Convey": "Test for Adding the DNA Code link base to a Agent not the app (Storing it locally)", 5 | "Zome": "hchc", 6 | "FnName": "addAppCode", 7 | "Input": { 8 | "dna": "/LONG____________________________BASE64_________DNA", 9 | "test":"/LONG____________________________BASE64_________TEST", 10 | "app_hash":"%agent%" 11 | }, 12 | "Output": "%h1%" 13 | }, 14 | { 15 | "Convey": "Test to get the DNA Code ", 16 | "Zome": "hchc", 17 | "FnName": "getAppCode", 18 | "Input": { 19 | "app_hash": "%agent%" 20 | }, 21 | "Regexp": ".*" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /ui-src/src/reducer.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as redux from 'redux'; 3 | 4 | import {AppState, ReduxAction} from '../../types'; 5 | 6 | const defaultState: AppState = { 7 | numClicks: 0, 8 | texts: [], 9 | }; 10 | 11 | export default (oldState: AppState = defaultState, action: ReduxAction): AppState => { 12 | const state = { 13 | ...oldState 14 | }; 15 | 16 | switch (action.type) { 17 | 18 | case 'INCREMENT': { 19 | state.numClicks += 1; 20 | break; 21 | } 22 | 23 | case 'DECREMENT': { 24 | state.numClicks -= 1; 25 | break; 26 | } 27 | 28 | case 'FETCH_TEXTS': { 29 | state.texts = action.entries.map(e => e.text); 30 | break; 31 | } 32 | 33 | } 34 | return state; 35 | } 36 | -------------------------------------------------------------------------------- /dna-src/hchc/app_code.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": {}, 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$id": "http://example.com/root.json", 5 | "type": "object", 6 | "title": "The Root Schema", 7 | "required": [ 8 | "dna", 9 | "test" 10 | ], 11 | "properties": { 12 | "dna": { 13 | "$id": "#/properties/dna", 14 | "type": "string", 15 | "title": "The Dna Schema", 16 | "default": "", 17 | "examples": [ 18 | "" 19 | ], 20 | "pattern": "^(.*)$" 21 | }, 22 | "test": { 23 | "$id": "#/properties/test", 24 | "type": "string", 25 | "title": "The Test Schema", 26 | "default": "", 27 | "examples": [ 28 | "" 29 | ], 30 | "pattern": "^(.*)$" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/bridge_test/hchc_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [{ 3 | "Convey": "test for Adding a new app", 4 | "Zome": "hchc", 5 | "FnName": "createApp", 6 | "Input": { 7 | "title": "Sakura", 8 | "description": "The is the details for the first app", 9 | "thumbnail": "IMG.jpg" 10 | }, 11 | "Output": "%h1%" 12 | }, 13 | { 14 | "Convey": "test for Adding a code for the app", 15 | "Zome": "hchc", 16 | "FnName": "addAppCode", 17 | "Input":{"dna":"file://dna/","test":"file://test/","app_hash":"%r1%"}, 18 | "Output": "%h1%" 19 | }, 20 | { 21 | "Convey": "test for Adding a code for the app", 22 | "Zome": "hchc", 23 | "FnName": "getAppCode", 24 | "Input":{"app_hash":"%r2%"}, 25 | "Regexp": "[{\"dna\":\"\",\"test\":,\"\"}]" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /@types/hchc/index.d.ts: -------------------------------------------------------------------------------- 1 | /*Comments Zome*/ 2 | interface CreateCommentsParams { 3 | comment: "string"; 4 | commentedOnHash: "string"; 5 | } 6 | 7 | interface Comments { 8 | author: "string"; 9 | comment: "string"; 10 | timestamp: "string" 11 | } 12 | 13 | type Reply = Comment_Chain[] 14 | 15 | interface Comment_Chain { 16 | comment: Comment; 17 | reply: Reply; 18 | } 19 | 20 | /*HCHC Zome Params*/ 21 | interface CreateAppParams { 22 | title: string; 23 | description?: string; 24 | thumbnail?: string; 25 | } 26 | 27 | interface CreateAppParams { 28 | title: string; 29 | description?: string; 30 | thumbnail?: string; 31 | } 32 | 33 | interface CodeParams { 34 | dna: string; 35 | test: string; 36 | } 37 | 38 | interface UiSkinParams { 39 | title: string; 40 | link: string; 41 | author: string; 42 | thumbnail?: string; 43 | } 44 | -------------------------------------------------------------------------------- /test/categories.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [{ 3 | "Convey": "test for Adding a new app", 4 | "Zome": "hchc", 5 | "FnName": "createApp", 6 | "Input": { 7 | "title": "Sakura", 8 | "description": "The is the details for the first app", 9 | "thumbnail": "IMG.jpg" 10 | }, 11 | "Output": "%h1%" 12 | }, 13 | { 14 | "Convey": "test to add Categories", 15 | "Zome": "categories", 16 | "FnName": "addCategory", 17 | "Input": {"category":"Games","tags":"ichiraku","hash":"%r1%"}, 18 | "Regexp": ".*" 19 | }, 20 | { 21 | "Convey": "test to get Categories for the app just created", 22 | "Zome": "categories", 23 | "FnName": "getAppCategories", 24 | "Input": {"hash":"%r2%"}, 25 | "Output": {"categories":["Games"],"tags":["ichiraku"]} 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /test/bridge_test/hchc_app_ui_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [{ 3 | "Convey": "test for Adding a new app", 4 | "Zome": "hchc", 5 | "FnName": "createApp", 6 | "Input": { 7 | "title": "Sakura", 8 | "description": "The is the details for the first app", 9 | "thumbnail": "IMG.jpg" 10 | }, 11 | "Output": "%h1%" 12 | }, 13 | { 14 | "Convey": "test for Adding a UISkin code for the app", 15 | "Zome": "hchc", 16 | "FnName": "addUISkin", 17 | "Input":{"title":"UI_TITLE","link":"file://ui/","thumbnail":"image.jpg","app_hash":"%r1%"}, 18 | "Output": "%h1%" 19 | }, 20 | { 21 | "Convey": "test for Getting a UISkin code for the app", 22 | "Zome": "hchc", 23 | "FnName": "getUISkin", 24 | "Input":{"app_hash":"%r2%"}, 25 | "Regexp": "[{\"title\":\"\",\"link\":,\"\"thumbnail\":\"\",\"author\":\"\"}]" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | let argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI, in coverage mode, or explicitly running all tests 22 | if ( 23 | !process.env.CI && 24 | argv.indexOf('--coverage') === -1 && 25 | argv.indexOf('--watchAll') === -1 26 | ) { 27 | argv.push('--watch'); 28 | } 29 | 30 | 31 | jest.run(argv); 32 | -------------------------------------------------------------------------------- /config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | 18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet. 19 | // We don't polyfill it in the browser--this is user's responsibility. 20 | if (process.env.NODE_ENV === 'test') { 21 | require('raf').polyfill(global); 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "esnext", 5 | "target": "es5", 6 | "downlevelIteration": true, 7 | "lib": ["es6", "dom"], 8 | "sourceMap": true, 9 | "allowJs": true, 10 | "jsx": "react", 11 | "moduleResolution": "node", 12 | "rootDir": "src", 13 | "experimentalDecorators": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noImplicitReturns": false, 16 | "noImplicitThis": true, 17 | "noImplicitAny": false, 18 | "strictNullChecks": true, 19 | "suppressImplicitAnyIndexErrors": true, 20 | "noUnusedLocals": false 21 | }, 22 | "files": [ 23 | "./holochain.d.ts" 24 | ], 25 | "include": [ 26 | "ui-src/src/**/*" 27 | ], 28 | "exclude": [ 29 | "node_modules", 30 | "build-HCHC", 31 | "scripts", 32 | "acceptance-tests", 33 | "webpack", 34 | "jest", 35 | "src/setupTests.ts", 36 | "ui-src/src/__tests__" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /test/bridge_test/UploadingUISkins_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [ 3 | { 4 | "Convey": "Test for adding a new app through the bridge", 5 | "Zome": "hchc", 6 | "FnName": "createApp", 7 | "Input": { 8 | "title": "Sakura", 9 | "description": "The is the details for the first app", 10 | "thumbnail": "IMG.jpg" 11 | }, 12 | "Output": "%h1%" 13 | }, 14 | { 15 | "Convey": "Test for Adding the UI Skin (Storing it locally)", 16 | "Zome": "hchc", 17 | "FnName": "addUISkin", 18 | "Input": { 19 | "title":"Ichiraku Ramen App", 20 | "link": "/downloadLink.holo.ui.link", 21 | "thumbnail":"/images.json", 22 | "app_hash":"%r1%" 23 | }, 24 | "Output": "%h1%" 25 | }, 26 | { 27 | "Convey": "Test to get the UI Skin ", 28 | "Zome": "hchc", 29 | "FnName": "getUISkin", 30 | "Input": { 31 | "app_hash": "%r2%" 32 | }, 33 | "Regexp": ".*" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /dna-src/profile/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": {}, 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$id": "http://example.com/root.json", 5 | "type": "object", 6 | "title": "The Root Schema", 7 | "required": [ 8 | "email" 9 | ], 10 | "properties": { 11 | "handle": { 12 | "$id": "#/properties/handle", 13 | "type": "string", 14 | "title": "The Handle Schema", 15 | "default": "", 16 | "examples": [ 17 | "zo-el" 18 | ], 19 | "pattern": "^(.*)$" 20 | }, 21 | "email": { 22 | "$id": "#/properties/email", 23 | "type": "string", 24 | "title": "The Email Schema", 25 | "default": "", 26 | "examples": [ 27 | "zo@el.com" 28 | ], 29 | "pattern": "^(.*)$" 30 | }, 31 | "avatar": { 32 | "$id": "#/properties/avatar", 33 | "type": "string", 34 | "title": "The Avatar Schema", 35 | "default": "", 36 | "examples": [ 37 | "/image.jpg" 38 | ], 39 | "pattern": "^(.*)$" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /dna-src/bridge_replies/bridge_replies.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | export = 0; 3 | let module = {}; 4 | // ----------------------------------------------------------------- 5 | // Public Functions 6 | // Author : Zo-El 7 | // ----------------------------------------------------------------- 8 | // Description : 9 | // ----------------------------------------------------------------- 10 | 11 | function getAppDNA({ app_hash }) { 12 | const hash = JSON.parse(call("hchc", "getAppCode", { app_hash })); 13 | return hash; 14 | } 15 | 16 | function getAppUISkin({ app_hash }) { 17 | const hash = JSON.parse(call("hchc", "getUISkin", { app_hash })); 18 | return hash; 19 | } 20 | 21 | // ----------------------------------------------------------------- 22 | // The Genesis Function https://developer.holochain.org/genesis 23 | // ----------------------------------------------------------------- 24 | 25 | function genesis() { 26 | return true; 27 | } 28 | 29 | function bridgeGenesis(side, dna, appData) { 30 | debug("HCHC Replies Bridge: " + side + " " + dna + " " + appData); 31 | return true; 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Holo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:7.10 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | #- run: yarn test 38 | -------------------------------------------------------------------------------- /dna-src/comments/comments.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": {}, 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$id": "http://example.com/root.json", 5 | "type": "object", 6 | "title": "The Root Schema", 7 | "required": [ 8 | "comment", 9 | "author", 10 | "timestamp" 11 | ], 12 | "properties": { 13 | "comment": { 14 | "$id": "#/properties/comment", 15 | "type": "string", 16 | "title": "The Comment Schema", 17 | "default": "", 18 | "examples": [ 19 | "This is an example comment" 20 | ], 21 | "pattern": "^(.*)$" 22 | }, 23 | "author": { 24 | "$id": "#/properties/author", 25 | "type": "string", 26 | "title": "The Author Schema", 27 | "default": "", 28 | "examples": [ 29 | "QuahfjvrioasdfcAfvrsaFAE" 30 | ], 31 | "pattern": "^(.*)$" 32 | }, 33 | "timestamp": { 34 | "$id": "#/properties/timestamp", 35 | "type": "string", 36 | "title": "The Timestamp Schema", 37 | "default": "", 38 | "examples": [ 39 | "" 40 | ], 41 | "pattern": "^(.*)$" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/bridge_test/UploadingDNACode_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [ 3 | { 4 | "Convey": "Test for adding a new app through the bridge", 5 | "Zome": "hchc", 6 | "FnName": "createApp", 7 | "Input": { 8 | "title": "Sakura", 9 | "description": "The is the details for the first app", 10 | "thumbnail": "IMG.jpg" 11 | }, 12 | "Output": "%h1%" 13 | }, 14 | { 15 | "Convey": "Test for get the hash of the app just pushed into the HApps Store", 16 | "Zome": "hchc", 17 | "FnName": "getAppDetails", 18 | "Input": { 19 | "app_hash": "%r1%" 20 | }, 21 | "Regexp": "[.*]" 22 | }, 23 | { 24 | "Convey": "Test for Adding the DNA Code (Storing it locally)", 25 | "Zome": "hchc", 26 | "FnName": "addAppCode", 27 | "Input": { 28 | "dna": "/LONG____________________________BASE64_________DNA", 29 | "test":"/LONG____________________________BASE64_________TEST", 30 | "app_hash":"%r2%" 31 | }, 32 | "Output": "%h1%" 33 | }, 34 | { 35 | "Convey": "Test to get the DNA Code ", 36 | "Zome": "hchc", 37 | "FnName": "getAppCode", 38 | "Input": { 39 | "app_hash": "%r3%" 40 | }, 41 | "Regexp": ".*" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /dna-src/hchc/ui_skin.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": {}, 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$id": "http://example.com/root.json", 5 | "type": "object", 6 | "title": "The UI_SKIN Schema", 7 | "required": [ 8 | "title", 9 | "link", 10 | "author" 11 | ], 12 | "properties": { 13 | "title": { 14 | "$id": "#/properties/title", 15 | "type": "string", 16 | "title": "The Title Schema", 17 | "default": "", 18 | "examples": [ 19 | "" 20 | ], 21 | "pattern": "^(.*)$" 22 | }, 23 | "link": { 24 | "$id": "#/properties/link", 25 | "type": "string", 26 | "title": "The Link Schema", 27 | "default": "", 28 | "examples": [ 29 | "" 30 | ], 31 | "pattern": "^(.*)$" 32 | }, 33 | "author": { 34 | "$id": "#/properties/author", 35 | "type": "string", 36 | "title": "The Author Schema", 37 | "default": "", 38 | "examples": [ 39 | "" 40 | ], 41 | "pattern": "^(.*)$" 42 | }, 43 | "thumbnail": { 44 | "$id": "#/properties/thumbnail", 45 | "type": "string", 46 | "title": "The Thumbnail Schema", 47 | "default": "", 48 | "examples": [ 49 | "" 50 | ], 51 | "pattern": "^(.*)$" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/unit_tests/profile_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [{ 3 | "Convey": "Test for get default profile", 4 | "Zome": "profile", 5 | "FnName": "getProfile", 6 | "Input": "", 7 | "Regexp": "{\"handle\":\"zo-el@zoel-Inspiron-7559\"}" 8 | }, 9 | { 10 | "Convey": "Test for adding a new profile", 11 | "Zome": "profile", 12 | "FnName": "createProfile", 13 | "Input": { 14 | "handle": "Sakura", 15 | "email": "sakura@haruno.com", 16 | "avatar": "/IMG.jpg" 17 | }, 18 | "Output": "%h1%" 19 | }, 20 | { 21 | "Convey": "Test for get profile created", 22 | "Zome": "profile", 23 | "FnName": "getProfile", 24 | "Input": "", 25 | "Regexp": "{\"avatar\":\"/IMG.jpg\",\"email\":\"sakura@haruno.com\",\"handle\":\"Sakura\"}" 26 | }, 27 | { 28 | "Convey": "Test for updating a profile", 29 | "Zome": "profile", 30 | "FnName": "updateProfile", 31 | "Input": { 32 | "handle": "Saske", 33 | "email": "saske@uchiha.com", 34 | "avatar": "/IMG.jpg" 35 | }, 36 | "Output": "%h%" 37 | }, 38 | { 39 | "Convey": "Test for get profile created", 40 | "Zome": "profile", 41 | "FnName": "getProfile", 42 | "Input": "", 43 | "Regexp": "{\"avatar\":\"/IMG.jpg\",\"email\":\"saske@uchiha.com\",\"handle\":\"Saske\"}" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /@types/holochain/index.d.ts: -------------------------------------------------------------------------------- 1 | // holochain ambient type defs for API 2 | 3 | /// 4 | 5 | declare function property(name: string): string; 6 | declare function makeHash (entryType: string, entryData: any): Hash; 7 | declare function debug(value: any): void; 8 | declare function call(zomeName: string, functionName: string, arguments: string | object): any; 9 | declare function bridge(appDNAHash: Hash, zomeName: string, functionName: string, arguments: string | object): any; 10 | declare function getBridges(): BridgeStatus[]; 11 | declare function sign(doc: string): string; 12 | declare function verifySignature(signature: string, data: string, pubKey: string): boolean; 13 | declare function commit(entryType: string, entryData: string | object): Hash; 14 | declare function get(hash: Hash, options?: object): GetResponse | any; 15 | declare function getLinks(base: Hash, tag: string, options?: object): GetLinksResponse[]; 16 | declare function update(entryType: string, entryData: string | object, replaces: Hash) : Hash; 17 | declare function updateAgent(options: object): Hash; 18 | declare function remove(entryHash: Hash, message: string): Hash; 19 | declare function query(options?: object): QueryResponse[] | any[]; 20 | declare function send(to: Hash, message: object, options?: object): any; 21 | declare function bundleStart(timeout: number, userParam: any): void; 22 | declare function bundleClose(commit: boolean): void; 23 | 24 | declare var HC: HolochainSystemGlobals; 25 | declare var App: HolochainAppGlobals; -------------------------------------------------------------------------------- /test/unit_tests/comments_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tests": [{ 3 | "Convey": "createComments link to base", 4 | "Zome": "comments", 5 | "FnName": "createComments", 6 | "Input": { 7 | "comment": "The First Main Layer Comment", 8 | "commentedOnHash": "%agent%" 9 | }, 10 | "Output": "%h1%" 11 | }, 12 | { 13 | "Convey": "createComments reply to the previous comment", 14 | "Zome": "comments", 15 | "FnName": "createComments", 16 | "Input": { 17 | "comment": "The Second Layer Comment", 18 | "commentedOnHash": "%r1%" 19 | }, 20 | "Output": "%h1%" 21 | }, 22 | { 23 | "Convey": "createComments reply to the previous comment", 24 | "Zome": "comments", 25 | "FnName": "createComments", 26 | "Input": { 27 | "comment": "The Third Layer Comment", 28 | "commentedOnHash": "%r1%" 29 | }, 30 | "Output": "%h1%" 31 | }, 32 | { 33 | "Convey": "createComments reply to the previous comment", 34 | "Zome": "comments", 35 | "FnName": "createComments", 36 | "Input": { 37 | "comment": "The Second Main Layer Comment", 38 | "commentedOnHash": "%agent%" 39 | }, 40 | "Output": "%h1%" 41 | }, 42 | { 43 | "Convey": "test for getComments", 44 | "Zome": "comments", 45 | "FnName": "getComments", 46 | "Input": {"commentedOnHash":"%agent%"}, 47 | "Regexp": "[{\"author\":\"\",\"comment\":,\"}]" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /@types/holochain/holochain.d.ts: -------------------------------------------------------------------------------- 1 | // holochain type definitions 2 | 3 | 4 | type Hash = string; 5 | type Signature = string; 6 | type HolochainError = object; 7 | type PackageRequest = object; 8 | 9 | /*============================================ 10 | = Holochain Data Types = 11 | ============================================*/ 12 | 13 | interface Header { 14 | Type: string; 15 | Time: string; 16 | HeaderLink: Hash; 17 | EntryLink: Hash; 18 | TypeLink: Hash; 19 | Sig: Signature; 20 | Change: Hash; 21 | } 22 | 23 | interface GetResponse { 24 | Entry?: any; 25 | EntryType?: string; 26 | Sources?: Hash[]; 27 | } 28 | 29 | interface GetLinksResponse { 30 | Hash: Hash; 31 | Entry?: any; 32 | EntryType?: string; 33 | Tag?: string; 34 | Source?: Hash; 35 | } 36 | 37 | interface QueryResponse { 38 | Hash?: string 39 | Entry?: any 40 | Header?: Header 41 | } 42 | 43 | interface BridgeStatus { 44 | Side: number; 45 | CalleeName?: string; 46 | CalleeApp?: Hash; 47 | Token?: string; 48 | } 49 | 50 | 51 | /*===== End of Holochain Data Types ======*/ 52 | 53 | 54 | interface HolochainSystemGlobals { 55 | Version: string; 56 | HashNotFound: any; 57 | Status: any; 58 | GetMask: any; 59 | LinkAction: any; 60 | PkgReq: any; 61 | Bridge: any; 62 | SysEntryType: any; 63 | BundleCancel: any; 64 | } 65 | 66 | interface HolochainAppGlobals { 67 | Name: string; 68 | DNA: { 69 | Hash: Hash; 70 | }; 71 | Key: { 72 | Hash: Hash; 73 | } 74 | Agent: { 75 | Hash: Hash; 76 | TopHash: Hash; 77 | String: string; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ui-src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | 23 | HC+TS template 24 | 25 | 26 | 29 |
30 | 31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /dna-src/hchc/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": {}, 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "$id": "http://example.com/root.json", 5 | "type": "object", 6 | "title": "The Root Schema", 7 | "required": [ 8 | "uuid", 9 | "title", 10 | "author" 11 | ], 12 | "properties": { 13 | "uuid": { 14 | "$id": "#/properties/uuid", 15 | "type": "string", 16 | "title": "The Uuid Schema", 17 | "default": "", 18 | "examples": [ 19 | "" 20 | ], 21 | "pattern": "^(.*)$" 22 | }, 23 | "title": { 24 | "$id": "#/properties/title", 25 | "type": "string", 26 | "title": "The Title Schema", 27 | "default": "", 28 | "examples": [ 29 | "" 30 | ], 31 | "pattern": "^(.*)$" 32 | }, 33 | "description": { 34 | "$id": "#/properties/description", 35 | "type": "string", 36 | "title": "The Description Schema", 37 | "default": "", 38 | "examples": [ 39 | "" 40 | ], 41 | "pattern": "^(.*)$" 42 | }, 43 | "author": { 44 | "$id": "#/properties/author", 45 | "type": "object", 46 | "title": "The Author Schema", 47 | "required": [ 48 | "Hash", 49 | "Name" 50 | ], 51 | "properties": { 52 | "Hash": { 53 | "$id": "#/properties/author/properties/Hash", 54 | "type": "string", 55 | "title": "The Hash Schema", 56 | "default": "", 57 | "examples": [ 58 | "" 59 | ], 60 | "pattern": "^(.*)$" 61 | }, 62 | "Name": { 63 | "$id": "#/properties/author/properties/Name", 64 | "type": "string", 65 | "title": "The Name Schema", 66 | "default": "", 67 | "examples": [ 68 | "" 69 | ], 70 | "pattern": "^(.*)$" 71 | } 72 | } 73 | }, 74 | "thumbnail": { 75 | "$id": "#/properties/thumbnail", 76 | "type": "string", 77 | "title": "The Thumbnail Schema", 78 | "default": "", 79 | "examples": [ 80 | "" 81 | ], 82 | "pattern": "^(.*)$" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /dna-src/whoami/whoami.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | export = 0; 3 | let module = {}; 4 | // ----------------------------------------------------------------- 5 | // Public Functions 6 | // Author : Zo-El 7 | // ----------------------------------------------------------------- 8 | // Description : 9 | // This zome can be used to get the users hash or profile details 10 | // ----------------------------------------------------------------- 11 | 12 | function getAgent(): any { 13 | return {Hash : App.Agent.Hash, 14 | Name: App.Agent.String 15 | }; 16 | } 17 | // ----------------------------------------------------------------- 18 | // The Genesis Function https://developer.holochain.org/genesis 19 | // ----------------------------------------------------------------- 20 | 21 | function genesis() { 22 | return true; 23 | } 24 | 25 | // ----------------------------------------------------------------- 26 | // Validation functions for every change to the local chain or DHT 27 | // ----------------------------------------------------------------- 28 | 29 | function validateCommit(entryName, entry, header, pkg, sources) { 30 | // debug("entryName: " + entryName + " entry: " + entry + " header: " + header + " pkg: " + pkg + " sources: " + sources) 31 | switch (entryName) { 32 | default: 33 | return false; 34 | } 35 | } 36 | 37 | function validatePut(entryName, entry, header, pkg, sources) { 38 | // debug("entryName: " + entryName + " entry: " + entry + " header: " + header + " pkg: " + pkg + " sources: " + sources) 39 | switch (entryName) { 40 | default: 41 | return false; 42 | } 43 | } 44 | 45 | function validateMod(entryName, entry, header, replaces, pkg, sources) { 46 | switch (entryName) { 47 | default: 48 | return false; 49 | } 50 | } 51 | 52 | function validateDel(entryName, hash, pkg, sources) { 53 | switch (entryName) { 54 | default: 55 | return false; 56 | } 57 | } 58 | function validateLink(entryName, baseHash, links, pkg, sources) { 59 | // debug("entryName: " + entryName + " baseHash: " + baseHash + " links: " + links + " pkg: " + pkg + " sources: " + sources) 60 | switch (entryName) { 61 | default: 62 | return false; 63 | } 64 | } 65 | function validatePutPkg(entryName) { 66 | return null; 67 | } 68 | function validateModPkg(entryName) { 69 | return null; 70 | } 71 | function validateDelPkg(entryName) { 72 | return null; 73 | } 74 | function validateLinkPkg(entryName) { 75 | return null; 76 | } 77 | -------------------------------------------------------------------------------- /ui-src/src/components/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as redux from 'redux'; 3 | import {BrowserRouter, Route} from 'react-router-dom' 4 | import {connect} from 'react-redux'; 5 | 6 | import './App.css'; 7 | 8 | import store from '../store' 9 | import {fetchPOST} from '../helpers' 10 | import {Hello, ReduxAction} from '../../../types'; 11 | 12 | 13 | type AppProps = { 14 | numClicks: number, 15 | texts: Array, 16 | decrement: () => void, 17 | increment: () => void, 18 | fetchTexts: () => void, 19 | } 20 | 21 | class App extends React.Component { 22 | 23 | private text = React.createRef(); 24 | 25 | public render() { 26 | const greeting: Hello = "Welcome to your brand new Typescript-enabled React+Redux app"; 27 | return ( 28 |
29 | 30 |

{ greeting }

31 |

{ this.props.numClicks }

32 |
33 | 34 | 35 |
36 |
37 |
38 | 39 | 40 |
41 |
    42 | { this.props.texts.map((text, i) =>
  • {text}
  • ) } 43 |
44 |
45 |
46 | ); 47 | } 48 | 49 | public componentDidMount() { 50 | setInterval(this.props.fetchTexts, 1000) 51 | } 52 | 53 | private handleSubmit = e => { 54 | e.preventDefault() 55 | if (this.text.current) { 56 | fetchPOST('/fn/sampleZome/sampleEntryCreate', { 57 | text: this.text.current.value, 58 | }).then(() => this.text.current!.value = '') 59 | } 60 | } 61 | } 62 | 63 | const mapStateToProps = ({numClicks, texts}) => ({numClicks, texts}) 64 | const mapDispatchToProps = dispatch => ({ 65 | increment: () => dispatch({type: 'INCREMENT'}), 66 | decrement: () => dispatch({type: 'DECREMENT'}), 67 | fetchTexts: () => { 68 | fetchPOST('/fn/sampleZome/sampleEntryList').then(entries => { 69 | dispatch({type: 'FETCH_TEXTS', entries}) 70 | }) 71 | }, 72 | }) 73 | 74 | export default connect(mapStateToProps, mapDispatchToProps)(App); 75 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Use base dir for node_modules and stuff, but 8 | // point to the app subdir for actual code 9 | const appSubdirectory = 'ui-src' 10 | 11 | // Make sure any symlinks in the project folder are resolved: 12 | // https://github.com/facebookincubator/create-react-app/issues/637 13 | const baseDirectory = fs.realpathSync(process.cwd()); 14 | const appDirectory = path.join(baseDirectory, appSubdirectory); 15 | const resolveBase = relativePath => path.resolve(baseDirectory, relativePath); 16 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 17 | 18 | const envPublicUrl = process.env.PUBLIC_URL; 19 | 20 | function ensureSlash(path, needsSlash) { 21 | const hasSlash = path.endsWith('/'); 22 | if (hasSlash && !needsSlash) { 23 | return path.substr(path, path.length - 1); 24 | } else if (!hasSlash && needsSlash) { 25 | return `${path}/`; 26 | } else { 27 | return path; 28 | } 29 | } 30 | 31 | const getPublicUrl = appPackageJson => 32 | envPublicUrl || require(appPackageJson).homepage; 33 | 34 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 35 | // "public path" at which the app is served. 36 | // Webpack needs to know it to put the right